| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to you under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.accumulo.server.rpc; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.fail; |
| |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.accumulo.core.trace.wrappers.RpcServerInvocationHandler; |
| import org.apache.thrift.ProcessFunction; |
| import org.apache.thrift.TBase; |
| import org.apache.thrift.TException; |
| import org.apache.thrift.protocol.TProtocol; |
| import org.junit.Test; |
| |
| import com.google.common.collect.Sets; |
| |
| /** |
| * Verification that RpcWrapper correctly mangles Exceptions to work around Thrift. |
| */ |
| public class RpcWrapperTest { |
| |
| private static final String RTE_MESSAGE = "RpcWrapperTest's RuntimeException Message"; |
| |
| /** |
| * Given a method name and whether or not the method is oneway, construct a ProcessFunction. |
| * |
| * @param methodName |
| * The service method name. |
| * @param isOneway |
| * Is the method oneway. |
| * @return A ProcessFunction. |
| */ |
| private fake_proc<FakeService> createProcessFunction(String methodName, boolean isOneway) { |
| return new fake_proc<>(methodName, isOneway); |
| } |
| |
| @Test |
| public void testSomeOnewayMethods() { |
| Map<String,ProcessFunction<FakeService,?>> procs = new HashMap<>(); |
| procs.put("foo", createProcessFunction("foo", true)); |
| procs.put("foobar", createProcessFunction("foobar", false)); |
| procs.put("bar", createProcessFunction("bar", true)); |
| procs.put("barfoo", createProcessFunction("barfoo", false)); |
| |
| Set<String> onewayMethods = RpcWrapper.getOnewayMethods(procs); |
| assertEquals(Sets.newHashSet("foo", "bar"), onewayMethods); |
| } |
| |
| @Test |
| public void testNoOnewayMethods() { |
| Map<String,ProcessFunction<FakeService,?>> procs = new HashMap<>(); |
| procs.put("foo", createProcessFunction("foo", false)); |
| procs.put("foobar", createProcessFunction("foobar", false)); |
| procs.put("bar", createProcessFunction("bar", false)); |
| procs.put("barfoo", createProcessFunction("barfoo", false)); |
| |
| Set<String> onewayMethods = RpcWrapper.getOnewayMethods(procs); |
| assertEquals(Collections.<String>emptySet(), onewayMethods); |
| } |
| |
| @Test |
| public void testAllOnewayMethods() { |
| Map<String,ProcessFunction<FakeService,?>> procs = new HashMap<>(); |
| procs.put("foo", createProcessFunction("foo", true)); |
| procs.put("foobar", createProcessFunction("foobar", true)); |
| procs.put("bar", createProcessFunction("bar", true)); |
| procs.put("barfoo", createProcessFunction("barfoo", true)); |
| |
| Set<String> onewayMethods = RpcWrapper.getOnewayMethods(procs); |
| assertEquals(Sets.newHashSet("foo", "foobar", "bar", "barfoo"), onewayMethods); |
| } |
| |
| @Test |
| public void testNoExceptionWrappingForOneway() throws Throwable { |
| final Object[] args = new Object[0]; |
| |
| final FakeService impl = new FakeServiceImpl(); |
| |
| // "short" names throw RTEs and are oneway, while long names do not throw exceptions and are not |
| // oneway. |
| RpcServerInvocationHandler<FakeService> handler = |
| RpcWrapper.getInvocationHandler(impl, Sets.newHashSet("foo", "bar")); |
| |
| // Should throw an exception, but not be wrapped because the method is oneway |
| try { |
| handler.invoke(impl, FakeServiceImpl.class.getMethod("foo"), args); |
| fail("Expected an exception"); |
| } catch (RuntimeException e) { |
| assertEquals(RTE_MESSAGE, e.getMessage()); |
| } |
| |
| // Should not throw an exception |
| handler.invoke(impl, FakeServiceImpl.class.getMethod("foobar"), args); |
| } |
| |
| @Test |
| public void testExceptionWrappingForNonOneway() throws Throwable { |
| final Object[] args = new Object[0]; |
| |
| final FakeService impl = new FakeServiceImpl(); |
| |
| // "short" names throw RTEs and are not oneway, while long names do not throw exceptions and are |
| // oneway. |
| RpcServerInvocationHandler<FakeService> handler = |
| RpcWrapper.getInvocationHandler(impl, Sets.newHashSet("foobar", "barfoo")); |
| |
| // Should throw an exception, but not be wrapped because the method is oneway |
| try { |
| handler.invoke(impl, FakeServiceImpl.class.getMethod("foo"), args); |
| fail("Expected an exception"); |
| } catch (TException e) { |
| // The InvocationHandler should take the exception from the RTE and make it a TException |
| assertEquals(RTE_MESSAGE, e.getMessage()); |
| } |
| |
| // Should not throw an exception |
| handler.invoke(impl, FakeServiceImpl.class.getMethod("foobar"), args); |
| } |
| |
| // |
| // Some hacked together classes/interfaces that mimic what Thrift is doing. |
| // |
| |
| /** |
| * Some fake fields for our fake arguments. |
| */ |
| private static class fake_fields implements org.apache.thrift.TFieldIdEnum { |
| @Override |
| public short getThriftFieldId() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public String getFieldName() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public int hashCode() { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| /** |
| * A fake thrift service |
| */ |
| interface FakeService { |
| void foo(); |
| |
| String foobar(); |
| |
| int bar(); |
| |
| long barfoo(); |
| } |
| |
| /** |
| * An implementation of the fake thrift service. The "short" names throw RTEs, while long names do |
| * not. |
| */ |
| public static class FakeServiceImpl implements FakeService { |
| @Override |
| public void foo() { |
| throw new RuntimeException(RTE_MESSAGE); |
| } |
| |
| @Override |
| public String foobar() { |
| return ""; |
| } |
| |
| @Override |
| public int bar() { |
| throw new RuntimeException(RTE_MESSAGE); |
| } |
| |
| @Override |
| public long barfoo() { |
| return 0; |
| } |
| } |
| |
| /** |
| * A fake ProcessFunction implementation for testing that allows injection of method name and |
| * oneway. |
| */ |
| private static class fake_proc<I extends FakeService> |
| extends org.apache.thrift.ProcessFunction<I,foo_args> { |
| final private boolean isOneway; |
| |
| public fake_proc(String methodName, boolean isOneway) { |
| super(methodName); |
| this.isOneway = isOneway; |
| } |
| |
| @Override |
| protected boolean isOneway() { |
| return isOneway; |
| } |
| |
| @SuppressWarnings("rawtypes") |
| @Override |
| public TBase getResult(I iface, foo_args args) throws TException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public foo_args getEmptyArgsInstance() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public int hashCode() { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| /** |
| * Fake arguments for our fake service. |
| */ |
| private static class foo_args implements org.apache.thrift.TBase<foo_args,fake_fields> { |
| |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public boolean equals(Object o) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public int hashCode() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public int compareTo(foo_args o) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public void read(TProtocol iprot) throws TException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public void write(TProtocol oprot) throws TException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public fake_fields fieldForId(int fieldId) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean isSet(fake_fields field) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Object getFieldValue(fake_fields field) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public void setFieldValue(fake_fields field, Object value) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public TBase<foo_args,fake_fields> deepCopy() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public void clear() { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| } |