001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.hbase.tool.coprocessor;
020
021import static org.junit.Assert.assertEquals;
022import static org.junit.Assert.assertTrue;
023
024import java.io.IOException;
025import java.util.List;
026
027import org.apache.hadoop.hbase.Coprocessor;
028import org.apache.hadoop.hbase.CoprocessorEnvironment;
029import org.apache.hadoop.hbase.HBaseClassTestRule;
030import org.apache.hadoop.hbase.HRegionInfo;
031import org.apache.hadoop.hbase.HTableDescriptor;
032import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
033import org.apache.hadoop.hbase.coprocessor.ObserverContext;
034import org.apache.hadoop.hbase.testclassification.SmallTests;
035import org.apache.hadoop.hbase.tool.coprocessor.CoprocessorViolation.Severity;
036import org.junit.ClassRule;
037import org.junit.Test;
038import org.junit.experimental.categories.Category;
039
040import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
041
042@Category({ SmallTests.class })
043@SuppressWarnings("deprecation")
044public class CoprocessorValidatorTest {
045  @ClassRule
046  public static final HBaseClassTestRule CLASS_RULE =
047      HBaseClassTestRule.forClass(CoprocessorValidatorTest.class);
048
049  private CoprocessorValidator validator;
050
051  public CoprocessorValidatorTest() {
052    validator = new CoprocessorValidator();
053  }
054
055  private static ClassLoader getClassLoader() {
056    return CoprocessorValidatorTest.class.getClassLoader();
057  }
058
059  private static String getFullClassName(String className) {
060    return CoprocessorValidatorTest.class.getName() + "$" + className;
061  }
062
063  @SuppressWarnings({"rawtypes", "unused"})
064  private static class TestObserver implements Coprocessor {
065    @Override
066    public void start(CoprocessorEnvironment env) throws IOException {
067    }
068
069    @Override
070    public void stop(CoprocessorEnvironment env) throws IOException {
071    }
072  }
073
074  @Test
075  public void testFilterObservers() throws Exception {
076    String filterObservers = getFullClassName("TestObserver");
077    List<String> classNames = Lists.newArrayList(
078        filterObservers, getClass().getName());
079    List<String> filteredClassNames = validator.filterObservers(getClassLoader(), classNames);
080
081    assertEquals(1, filteredClassNames.size());
082    assertEquals(filterObservers, filteredClassNames.get(0));
083  }
084
085  private List<CoprocessorViolation> validate(String className) {
086    ClassLoader classLoader = getClass().getClassLoader();
087    return validate(classLoader, className);
088  }
089
090  private List<CoprocessorViolation> validate(ClassLoader classLoader, String className) {
091    List<String> classNames = Lists.newArrayList(getClass().getName() + "$" + className);
092    return validator.validate(classLoader, classNames);
093  }
094
095  /*
096   * In this test case, we are try to load a not-existent class.
097   */
098  @Test
099  public void testNoSuchClass() throws IOException {
100    List<CoprocessorViolation> violations = validate("NoSuchClass");
101    assertEquals(1, violations.size());
102
103    CoprocessorViolation violation = violations.get(0);
104    assertEquals(Severity.ERROR, violation.getSeverity());
105    assertTrue(violation.getMessage().contains(
106        "java.lang.ClassNotFoundException: " +
107        "org.apache.hadoop.hbase.tool.coprocessor.CoprocessorValidatorTest$NoSuchClass"));
108  }
109
110  /*
111   * In this test case, we are validating MissingClass coprocessor, which
112   * references a missing class. With a special classloader, we prevent that
113   * class to be loaded at runtime. It simulates similar cases where a class
114   * is no more on our classpath.
115   * E.g. org.apache.hadoop.hbase.regionserver.wal.WALEdit was moved to
116   * org.apache.hadoop.hbase.wal, so class loading will fail on 2.0.
117   */
118  private static class MissingClass {
119  }
120
121  @SuppressWarnings("unused")
122  private static class MissingClassObserver {
123    public void method(MissingClass missingClass) {
124    }
125  }
126
127  private static class MissingClassClassLoader extends ClassLoader {
128    public MissingClassClassLoader() {
129      super(getClassLoader());
130    }
131
132    @Override
133    public Class<?> loadClass(String name) throws ClassNotFoundException {
134      if (name.equals(getFullClassName("MissingClass"))) {
135        throw new ClassNotFoundException(name);
136      }
137
138      return super.findClass(name);
139    }
140  }
141
142  @Test
143  public void testMissingClass() throws IOException {
144    MissingClassClassLoader missingClassClassLoader = new MissingClassClassLoader();
145    List<CoprocessorViolation> violations = validate(missingClassClassLoader,
146        "MissingClassObserver");
147    assertEquals(1, violations.size());
148
149    CoprocessorViolation violation = violations.get(0);
150    assertEquals(Severity.ERROR, violation.getSeverity());
151    assertTrue(violation.getMessage().contains(
152        "java.lang.ClassNotFoundException: " +
153        "org.apache.hadoop.hbase.tool.coprocessor.CoprocessorValidatorTest$MissingClass"));
154  }
155
156  /*
157   * ObsoleteMethod coprocessor implements preCreateTable method which has
158   * HRegionInfo parameters. In our current implementation, we pass only
159   * RegionInfo parameters, so this method won't be called by HBase at all.
160   */
161  @SuppressWarnings("unused")
162  private static class ObsoleteMethodObserver /* implements MasterObserver */ {
163    public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
164        HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
165    }
166  }
167
168  @Test
169  public void testObsoleteMethod() throws IOException {
170    List<CoprocessorViolation> violations = validate("ObsoleteMethodObserver");
171    assertEquals(1, violations.size());
172
173    CoprocessorViolation violation = violations.get(0);
174    assertEquals(Severity.WARNING, violation.getSeverity());
175    assertTrue(violation.getMessage().contains("was removed from new coprocessor API"));
176  }
177}