blob: 82b94e70976aa7fab5942eda153ffe91acc95747 [file] [log] [blame]
/*
* 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();
}
}
}