001/**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020package org.apache.hadoop.hbase.coprocessor;
021
022import java.io.IOException;
023import java.util.Arrays;
024import java.util.List;
025import java.util.Optional;
026
027import org.apache.hadoop.fs.Path;
028import org.apache.hadoop.hbase.Cell;
029import org.apache.hadoop.hbase.CellUtil;
030import org.apache.hadoop.hbase.KeyValue;
031import org.apache.hadoop.hbase.client.RegionInfo;
032import org.apache.hadoop.hbase.util.Bytes;
033import org.apache.hadoop.hbase.wal.WALEdit;
034import org.apache.hadoop.hbase.wal.WALKey;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038/**
039 * Class for testing WALObserver coprocessor. It will monitor WAL writing and restoring, and modify
040 * passed-in WALEdit, i.e, ignore specified columns when writing, or add a KeyValue. On the other
041 * side, it checks whether the ignored column is still in WAL when Restoreed at region reconstruct.
042 */
043public class SampleRegionWALCoprocessor implements WALCoprocessor, RegionCoprocessor,
044    WALObserver, RegionObserver {
045
046  private static final Logger LOG = LoggerFactory.getLogger(SampleRegionWALCoprocessor.class);
047
048  private byte[] tableName;
049  private byte[] row;
050  private byte[] ignoredFamily;
051  private byte[] ignoredQualifier;
052  private byte[] addedFamily;
053  private byte[] addedQualifier;
054  private byte[] changedFamily;
055  private byte[] changedQualifier;
056
057  private boolean preWALWriteCalled = false;
058  private boolean postWALWriteCalled = false;
059  private boolean preWALRestoreCalled = false;
060  private boolean postWALRestoreCalled = false;
061  private boolean preWALRollCalled = false;
062  private boolean postWALRollCalled = false;
063
064  /**
065   * Set values: with a table name, a column name which will be ignored, and
066   * a column name which will be added to WAL.
067   */
068  public void setTestValues(byte[] tableName, byte[] row, byte[] igf, byte[] igq,
069      byte[] chf, byte[] chq, byte[] addf, byte[] addq) {
070    this.row = row;
071    this.tableName = tableName;
072    this.ignoredFamily = igf;
073    this.ignoredQualifier = igq;
074    this.addedFamily = addf;
075    this.addedQualifier = addq;
076    this.changedFamily = chf;
077    this.changedQualifier = chq;
078    preWALWriteCalled = false;
079    postWALWriteCalled = false;
080    preWALRestoreCalled = false;
081    postWALRestoreCalled = false;
082    preWALRollCalled = false;
083    postWALRollCalled = false;
084  }
085
086  @Override public Optional<WALObserver> getWALObserver() {
087    return Optional.of(this);
088  }
089
090  @Override
091  public Optional<RegionObserver> getRegionObserver() {
092    return Optional.of(this);
093  }
094
095  @Override
096  public void postWALWrite(ObserverContext<? extends WALCoprocessorEnvironment> env,
097      RegionInfo info, WALKey logKey, WALEdit logEdit) throws IOException {
098    postWALWriteCalled = true;
099  }
100
101  @Override
102  public void preWALWrite(ObserverContext<? extends WALCoprocessorEnvironment> env,
103      RegionInfo info, WALKey logKey, WALEdit logEdit) throws IOException {
104    // check table name matches or not.
105    if (!Bytes.equals(info.getTable().toBytes(), this.tableName)) {
106      return;
107    }
108    preWALWriteCalled = true;
109    // here we're going to remove one keyvalue from the WALEdit, and add
110    // another one to it.
111    List<Cell> cells = logEdit.getCells();
112    Cell deletedCell = null;
113    for (Cell cell : cells) {
114      // assume only one kv from the WALEdit matches.
115      byte[] family = CellUtil.cloneFamily(cell);
116      byte[] qulifier = CellUtil.cloneQualifier(cell);
117
118      if (Arrays.equals(family, ignoredFamily) &&
119          Arrays.equals(qulifier, ignoredQualifier)) {
120        LOG.debug("Found the KeyValue from WALEdit which should be ignored.");
121        deletedCell = cell;
122      }
123      if (Arrays.equals(family, changedFamily) &&
124          Arrays.equals(qulifier, changedQualifier)) {
125        LOG.debug("Found the KeyValue from WALEdit which should be changed.");
126        cell.getValueArray()[cell.getValueOffset()] =
127            (byte) (cell.getValueArray()[cell.getValueOffset()] + 1);
128      }
129    }
130    if (null != row) {
131      cells.add(new KeyValue(row, addedFamily, addedQualifier));
132    }
133    if (deletedCell != null) {
134      LOG.debug("About to delete a KeyValue from WALEdit.");
135      cells.remove(deletedCell);
136    }
137  }
138
139  /**
140   * Triggered before  {@link org.apache.hadoop.hbase.regionserver.HRegion} when WAL is
141   * Restoreed.
142   */
143  @Override
144  public void preWALRestore(ObserverContext<? extends RegionCoprocessorEnvironment> env,
145    RegionInfo info, WALKey logKey, WALEdit logEdit) throws IOException {
146    preWALRestoreCalled = true;
147  }
148
149  @Override
150  public void preWALRoll(ObserverContext<? extends WALCoprocessorEnvironment> ctx,
151      Path oldPath, Path newPath) throws IOException {
152    preWALRollCalled = true;
153  }
154
155  @Override
156  public void postWALRoll(ObserverContext<? extends WALCoprocessorEnvironment> ctx,
157      Path oldPath, Path newPath) throws IOException {
158    postWALRollCalled = true;
159  }
160
161  /**
162   * Triggered after {@link org.apache.hadoop.hbase.regionserver.HRegion} when WAL is
163   * Restoreed.
164   */
165  @Override
166  public void postWALRestore(ObserverContext<? extends RegionCoprocessorEnvironment> env,
167      RegionInfo info, WALKey logKey, WALEdit logEdit) throws IOException {
168    postWALRestoreCalled = true;
169  }
170
171  public boolean isPreWALWriteCalled() {
172    return preWALWriteCalled;
173  }
174
175  public boolean isPostWALWriteCalled() {
176    return postWALWriteCalled;
177  }
178
179  public boolean isPreWALRestoreCalled() {
180    LOG.debug(SampleRegionWALCoprocessor.class.getName() +
181      ".isPreWALRestoreCalled is called.");
182    return preWALRestoreCalled;
183  }
184
185  public boolean isPostWALRestoreCalled() {
186    LOG.debug(SampleRegionWALCoprocessor.class.getName() +
187      ".isPostWALRestoreCalled is called.");
188    return postWALRestoreCalled;
189  }
190
191  public boolean isPreWALRollCalled() {
192    return preWALRollCalled;
193  }
194
195  public boolean isPostWALRollCalled() {
196    return postWALRollCalled;
197  }
198}