001/*
002 * Copyright The Apache Software Foundation
003 *
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *     http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020package org.apache.hadoop.hbase.filter;
021
022import java.io.IOException;
023import java.util.List;
024import java.util.Objects;
025
026import org.apache.hadoop.hbase.Cell;
027import org.apache.yetus.audience.InterfaceAudience;
028import org.apache.hadoop.hbase.exceptions.DeserializationException;
029import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
030import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos;
031import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
032
033/**
034 * This is a Filter wrapper class which is used in the server side. Some filter
035 * related hooks can be defined in this wrapper. The only way to create a
036 * FilterWrapper instance is passing a client side Filter instance through
037 * {@link org.apache.hadoop.hbase.client.Scan#getFilter()}.
038 *
039 */
040@InterfaceAudience.Private
041final public class FilterWrapper extends Filter {
042  Filter filter = null;
043
044  /**
045   * Constructor.
046   * @param filter filter to wrap
047   * @throws NullPointerException if {@code filter} is {@code null}
048   */
049  public FilterWrapper(Filter filter) {
050    this.filter = Objects.requireNonNull(filter, "Cannot create FilterWrapper with null Filter");
051  }
052
053  /**
054   * @return The filter serialized using pb
055   */
056  @Override
057  public byte[] toByteArray() throws IOException {
058    FilterProtos.FilterWrapper.Builder builder =
059      FilterProtos.FilterWrapper.newBuilder();
060    builder.setFilter(ProtobufUtil.toFilter(this.filter));
061    return builder.build().toByteArray();
062  }
063
064  /**
065   * @param pbBytes A pb serialized {@link FilterWrapper} instance
066   * @return An instance of {@link FilterWrapper} made from <code>bytes</code>
067   * @throws org.apache.hadoop.hbase.exceptions.DeserializationException
068   * @see #toByteArray
069   */
070  public static FilterWrapper parseFrom(final byte [] pbBytes)
071  throws DeserializationException {
072    FilterProtos.FilterWrapper proto;
073    try {
074      proto = FilterProtos.FilterWrapper.parseFrom(pbBytes);
075    } catch (InvalidProtocolBufferException e) {
076      throw new DeserializationException(e);
077    }
078    try {
079      return new FilterWrapper(ProtobufUtil.toFilter(proto.getFilter()));
080    } catch (IOException ioe) {
081      throw new DeserializationException(ioe);
082    }
083  }
084
085  @Override
086  public void reset() throws IOException {
087    this.filter.reset();
088  }
089
090  @Override
091  public boolean filterAllRemaining() throws IOException {
092    return this.filter.filterAllRemaining();
093  }
094
095  @Override
096  public boolean filterRow() throws IOException {
097    return this.filter.filterRow();
098  }
099
100  @Override
101  public Cell getNextCellHint(Cell currentCell) throws IOException {
102    return this.filter.getNextCellHint(currentCell);
103  }
104
105  @Override
106  public boolean filterRowKey(Cell cell) throws IOException {
107    if (filterAllRemaining()) return true;
108    return this.filter.filterRowKey(cell);
109  }
110
111  @Override
112  public ReturnCode filterCell(final Cell c) throws IOException {
113    return this.filter.filterCell(c);
114  }
115
116  @Override
117  public Cell transformCell(Cell v) throws IOException {
118    return this.filter.transformCell(v);
119  }
120
121  @Override
122  public boolean hasFilterRow() {
123    return this.filter.hasFilterRow();
124  }
125
126  @Override
127  public void filterRowCells(List<Cell> kvs) throws IOException {
128    filterRowCellsWithRet(kvs);
129  }
130
131  public enum FilterRowRetCode {
132    NOT_CALLED,
133    INCLUDE,     // corresponds to filter.filterRow() returning false
134    EXCLUDE,     // corresponds to filter.filterRow() returning true
135    INCLUDE_THIS_FAMILY  // exclude other families
136  }
137  public FilterRowRetCode filterRowCellsWithRet(List<Cell> kvs) throws IOException {
138    //To fix HBASE-6429,
139    //Filter with filterRow() returning true is incompatible with scan with limit
140    //1. hasFilterRow() returns true, if either filterRow() or filterRow(kvs) is implemented.
141    //2. filterRow() is merged with filterRow(kvs),
142    //so that to make all those row related filtering stuff in the same function.
143    this.filter.filterRowCells(kvs);
144    if (!kvs.isEmpty()) {
145      if (this.filter.filterRow()) {
146        kvs.clear();
147        return FilterRowRetCode.EXCLUDE;
148      }
149      return FilterRowRetCode.INCLUDE;
150    }
151    return FilterRowRetCode.NOT_CALLED;
152  }
153
154  @Override
155  public boolean isFamilyEssential(byte[] name) throws IOException {
156    return filter.isFamilyEssential(name);
157  }
158
159  /**
160   * @param o the other filter to compare with
161   * @return true if and only if the fields of the filter that are serialized
162   * are equal to the corresponding fields in other.  Used for testing.
163   */
164  @Override
165  boolean areSerializedFieldsEqual(Filter o) {
166    if (o == this) return true;
167    if (!(o instanceof FilterWrapper)) return false;
168
169    FilterWrapper other = (FilterWrapper)o;
170    return this.filter.areSerializedFieldsEqual(other.filter);
171  }
172}