/** * 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.avro; import org.apache.avro.Protocol.Message; import org.apache.avro.Schema.Field; import org.apache.avro.generic.GenericData; import org.apache.avro.generic.GenericRecord; import org.apache.avro.ipc.SocketServer; import org.apache.avro.ipc.SocketTransceiver; import org.apache.avro.ipc.Transceiver; import org.apache.avro.ipc.generic.GenericRequestor; import org.apache.avro.ipc.generic.GenericResponder; import org.apache.avro.util.Utf8; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Random; public class TestProtocolGeneric { private static final Logger LOG = LoggerFactory.getLogger(TestProtocolGeneric.class); protected static final File FILE = new File("../../../share/test/schemas/simple.avpr"); protected static final Protocol PROTOCOL; static { try { PROTOCOL = Protocol.parse(FILE); } catch (IOException e) { throw new RuntimeException(e); } } private static boolean throwUndeclaredError; protected static class TestResponder extends GenericResponder { public TestResponder() { super(PROTOCOL); } public Object respond(Message message, Object request) throws AvroRemoteException { GenericRecord params = (GenericRecord)request; if ("hello".equals(message.getName())) { LOG.info("hello: "+params.get("greeting")); return new Utf8("goodbye"); } if ("echo".equals(message.getName())) { Object record = params.get("record"); LOG.info("echo: "+record); return record; } if ("echoBytes".equals(message.getName())) { Object data = params.get("data"); LOG.info("echoBytes: "+data); return data; } if ("error".equals(message.getName())) { if (throwUndeclaredError) throw new RuntimeException("foo"); GenericRecord error = new GenericData.Record(PROTOCOL.getType("TestError")); error.put("message", new Utf8("an error")); throw new AvroRemoteException(error); } throw new AvroRuntimeException("unexpected message: "+message.getName()); } } protected static SocketServer server; protected static Transceiver client; protected static GenericRequestor requestor; @Before public void testStartServer() throws Exception { if (server != null) return; server = new SocketServer(new TestResponder(), new InetSocketAddress(0)); server.start(); client = new SocketTransceiver(new InetSocketAddress(server.getPort())); requestor = new GenericRequestor(PROTOCOL, client); } @Test public void testHello() throws IOException { GenericRecord params = new GenericData.Record(PROTOCOL.getMessages().get("hello").getRequest()); params.put("greeting", new Utf8("bob")); Utf8 response = (Utf8)requestor.request("hello", params); assertEquals(new Utf8("goodbye"), response); } @Test public void testEcho() throws IOException { GenericRecord record = new GenericData.Record(PROTOCOL.getType("TestRecord")); record.put("name", new Utf8("foo")); record.put("kind", new GenericData.EnumSymbol (PROTOCOL.getType("Kind"), "BAR")); record.put("hash", new GenericData.Fixed (PROTOCOL.getType("MD5"), new byte[]{0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5})); GenericRecord params = new GenericData.Record(PROTOCOL.getMessages().get("echo").getRequest()); params.put("record", record); Object echoed = requestor.request("echo", params); assertEquals(record, echoed); } @Test public void testEchoBytes() throws IOException { Random random = new Random(); int length = random.nextInt(1024*16); GenericRecord params = new GenericData.Record(PROTOCOL.getMessages().get("echoBytes").getRequest()); ByteBuffer data = ByteBuffer.allocate(length); random.nextBytes(data.array()); data.flip(); params.put("data", data); Object echoed = requestor.request("echoBytes", params); assertEquals(data, echoed); } @Test public void testError() throws IOException { GenericRecord params = new GenericData.Record(PROTOCOL.getMessages().get("error").getRequest()); AvroRemoteException error = null; try { requestor.request("error", params); } catch (AvroRemoteException e) { error = e; } assertNotNull(error); assertEquals("an error", ((GenericRecord)error.getValue()).get("message").toString()); } @Test public void testUndeclaredError() throws IOException { this.throwUndeclaredError = true; RuntimeException error = null; GenericRecord params = new GenericData.Record(PROTOCOL.getMessages().get("error").getRequest()); try { requestor.request("error", params); } catch (RuntimeException e) { error = e; } finally { this.throwUndeclaredError = false; } assertNotNull(error); assertTrue(error.toString().contains("foo")); } @Test /** Construct and use a different protocol whose "hello" method has an extra argument to check that schema is sent to parse request. */ public void testHandshake() throws IOException { Protocol protocol = new Protocol("Simple", "org.apache.avro.test"); List<Field> fields = new ArrayList<Field>(); fields.add(new Schema.Field("extra", Schema.create(Schema.Type.BOOLEAN), null, null)); fields.add(new Schema.Field("greeting", Schema.create(Schema.Type.STRING), null, null)); Protocol.Message message = protocol.createMessage("hello", null /* doc */, Schema.createRecord(fields), Schema.create(Schema.Type.STRING), Schema.createUnion(new ArrayList<Schema>())); protocol.getMessages().put("hello", message); Transceiver t = new SocketTransceiver(new InetSocketAddress(server.getPort())); try { GenericRequestor r = new GenericRequestor(protocol, t); GenericRecord params = new GenericData.Record(message.getRequest()); params.put("extra", Boolean.TRUE); params.put("greeting", new Utf8("bob")); Utf8 response = (Utf8)r.request("hello", params); assertEquals(new Utf8("goodbye"), response); } finally { t.close(); } } @Test /** Construct and use a different protocol whose "echo" response has an extra field to check that correct schema is used to parse response. */ public void testResponseChange() throws IOException { List<Field> fields = new ArrayList<Field>(); for (Field f : PROTOCOL.getType("TestRecord").getFields()) fields.add(new Field(f.name(), f.schema(), null, null)); fields.add(new Field("extra", Schema.create(Schema.Type.BOOLEAN), null, true)); Schema record = Schema.createRecord("TestRecord", null, "org.apache.avro.test", false); record.setFields(fields); Protocol protocol = new Protocol("Simple", "org.apache.avro.test"); List<Field> params = new ArrayList<Field>(); params.add(new Field("record", record, null, null)); Protocol.Message message = protocol.createMessage("echo", null, Schema.createRecord(params), record, Schema.createUnion(new ArrayList<Schema>())); protocol.getMessages().put("echo", message); Transceiver t = new SocketTransceiver(new InetSocketAddress(server.getPort())); try { GenericRequestor r = new GenericRequestor(protocol, t); GenericRecord args = new GenericData.Record(message.getRequest()); GenericRecord rec = new GenericData.Record(record); rec.put("name", new Utf8("foo")); rec.put("kind", new GenericData.EnumSymbol (PROTOCOL.getType("Kind"), "BAR")); rec.put("hash", new GenericData.Fixed (PROTOCOL.getType("MD5"), new byte[]{0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5})); rec.put("extra", Boolean.TRUE); args.put("record", rec); GenericRecord response = (GenericRecord)r.request("echo", args); assertEquals(rec, response); } finally { t.close(); } } @AfterClass public static void testStopServer() throws IOException { client.close(); server.close(); } }