org.vootoo.client.netty.NettySolrClientTest.java Source code

Java tutorial

Introduction

Here is the source code for org.vootoo.client.netty.NettySolrClientTest.java

Source

/*
 * 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.vootoo.client.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalEventLoopGroup;
import io.netty.channel.local.LocalServerChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.BinaryResponseParser;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.JavaBinCodec;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.junit.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vootoo.client.netty.connect.SimpleConnectionPool;
import org.vootoo.client.netty.protocol.SolrProtocol;
import org.vootoo.client.netty.util.ByteStringer;
import org.vootoo.client.netty.util.ProtobufUtil;
import org.vootoo.common.MemoryOutputStream;
import org.vootoo.common.VootooException;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.util.UUID;
import java.util.concurrent.RejectedExecutionException;

/**
 * @author chenlb on 2015-05-28 10:12.
 */
public class NettySolrClientTest {

    private static final Logger logger = LoggerFactory.getLogger(NettySolrClientTest.class);

    static final String PORT = System.getProperty("port", "test_port");
    static final LocalAddress addr = new LocalAddress(PORT);

    static EventLoopGroup serverGroup = new LocalEventLoopGroup();
    static EventLoopGroup clientGroup = new NioEventLoopGroup(); // NIO event loops are also OK
    static Bootstrap client;
    static Channel serverChannel;

    protected static class MockResolver implements JavaBinCodec.ObjectResolver {
        @Override
        public Object resolve(Object o, JavaBinCodec codec) throws IOException {
            return o;
        }
    }

    protected static void writeResponse(NamedList<Object> values, OutputStream out) throws IOException {
        JavaBinCodec codec = new JavaBinCodec(new MockResolver());
        codec.marshal(values, out);
    }

    protected static class RequestParams {
        public static final String ErrorCode = "_ErrorCode_";
        public static final String ErrorMsg = "_ErrorMsg_";
        public static final String ErrorMeta = "_ErrorMeta_";
        public static final String ExceptionClass = "_ExceptionClass_";
        public static final String WriteResponseId = "_WriteResponseId_";
    }

    protected static class MockSolrServerHandler extends SimpleChannelInboundHandler<SolrProtocol.SolrRequest> {

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, SolrProtocol.SolrRequest solrRequest)
                throws Exception {
            ProtobufRequestGetter requestGetter = new ProtobufRequestGetter(solrRequest);
            SolrParams solrParams = requestGetter.getSolrParams();

            SolrProtocol.SolrResponse.Builder protocolResponseBuilder = SolrProtocol.SolrResponse.newBuilder();
            protocolResponseBuilder.setRid(solrRequest.getRid());

            Integer errorCode = solrParams.getInt(RequestParams.ErrorCode);
            if (errorCode != null) {

                SolrProtocol.ExceptionBody.Builder exceptionBody = SolrProtocol.ExceptionBody.newBuilder();
                exceptionBody.setCode(errorCode);

                String msg = solrParams.get(RequestParams.ErrorMsg);
                if (msg != null) {
                    exceptionBody.setMessage(msg);
                }

                String[] metas = solrParams.getParams(RequestParams.ErrorMeta);
                if (metas != null) {
                    for (String meta : metas) {
                        String[] ms = meta.split(":");
                        if (ms != null && ms.length > 1) {
                            exceptionBody
                                    .addMetadata(SolrProtocol.KeyValue.newBuilder().setKey(ms[0]).setValue(ms[1]));
                        } else {
                            exceptionBody.addMetadata(
                                    SolrProtocol.KeyValue.newBuilder().setKey(meta).setValue("notvalue"));
                        }
                    }
                }

                errorCode = VootooException.VootooErrorCode.getErrorCode(errorCode).code;

                if (errorCode == 500 || errorCode < 100) {
                    logger.info("add exception trace test");
                    exceptionBody.setTrace(SolrException.toStr(new RuntimeException("mock trace info")));
                }

                protocolResponseBuilder.addExceptionBody(exceptionBody);
            }

            String[] exceptionClass = solrParams.getParams(RequestParams.ExceptionClass);
            if (exceptionClass != null && exceptionClass.length > 0) {
                for (String excClass : exceptionClass) {
                    SolrProtocol.ExceptionBody.Builder exceptionBody = SolrProtocol.ExceptionBody.newBuilder();
                    Throwable throwable = null;
                    try {
                        Class clazz = Class.forName(excClass);
                        Constructor<Throwable> constructor = clazz.getConstructor(String.class);
                        throwable = constructor.newInstance("mock test ExceptionClass for junit");
                        ProtobufUtil.getErrorInfo(throwable, exceptionBody);
                    } catch (Throwable t) {
                        ProtobufUtil.getErrorInfo(t, exceptionBody);
                    }

                    protocolResponseBuilder.addExceptionBody(exceptionBody);
                }
            }

            String responseId = solrParams.get(RequestParams.WriteResponseId);
            if (responseId != null) {
                //write query response
                SolrDocument sd = new SolrDocument();
                sd.addField("id", responseId);
                SolrDocumentList sdl = new SolrDocumentList();
                sdl.add(sd);

                sdl.setNumFound(1);
                sdl.setMaxScore(1.0f);
                sdl.setStart(0);

                NamedList<Object> values = new SimpleOrderedMap<>();
                NamedList<Object> responseHeader = new SimpleOrderedMap<>();
                values.add("responseHeader", responseHeader);

                values.add("response", sdl);

                responseHeader.add("status", 0);
                responseHeader.add("QTime", 10);
                responseHeader.add("METHOD", requestGetter.getMethod());

                MemoryOutputStream responeOutput = new MemoryOutputStream();
                writeResponse(values, responeOutput);

                SolrProtocol.ResponseBody.Builder responseBodyBuilder = SolrProtocol.ResponseBody.newBuilder();
                responseBodyBuilder
                        .setBody(ByteStringer.wrap(responeOutput.getBuffer(), 0, responeOutput.getCount()));
                responseBodyBuilder.setContentType(BinaryResponseParser.BINARY_CONTENT_TYPE);

                protocolResponseBuilder.setResponseBody(responseBodyBuilder);
            }

            ctx.writeAndFlush(protocolResponseBuilder.build());
        }
    }

    protected static class MockSolrServerChannelInitializer extends ChannelInitializer<Channel> {

        @Override
        protected void initChannel(Channel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();

            pipeline.addLast("frame-decoder", new LengthFieldBasedFrameDecoder(50 * 1024 * 1024, 0, 4, 0, 4));
            pipeline.addLast("frame-encoder", new LengthFieldPrepender(4));

            pipeline.addLast("pb-decoder", new ProtobufDecoder(SolrProtocol.SolrRequest.getDefaultInstance()));
            pipeline.addLast("pb-encoder", new ProtobufEncoder());

            // add mock channel handler
            pipeline.addLast(new MockSolrServerHandler());
        }
    }

    @BeforeClass
    public static void start_local_netty() throws InterruptedException {
        ServerBootstrap sb = new ServerBootstrap();
        sb.group(serverGroup).channel(LocalServerChannel.class)
                .handler(new ChannelInitializer<LocalServerChannel>() {
                    @Override
                    public void initChannel(LocalServerChannel ch) throws Exception {
                        ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
                    }
                }).childHandler(new MockSolrServerChannelInitializer());

        //client
        client = new Bootstrap();
        client.group(clientGroup).channel(LocalChannel.class).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000);

        // Start the server.
        ChannelFuture serverFuture = sb.bind(addr).sync();

        serverChannel = serverFuture.channel();
    }

    @AfterClass
    public static void close_group() {
        if (serverChannel != null) {
            logger.info("server channel closing ...");
            serverChannel.close();
            logger.info("server channel closed");
        }
        if (serverGroup != null) {
            logger.info("server group closing ...");
            serverGroup.shutdownGracefully();
            logger.info("server group closed");
        }
        if (clientGroup != null) {
            logger.info("client group closing ...");
            clientGroup.shutdownGracefully();
            logger.info("client group closed");
        }
    }

    protected NettySolrClient createNettysolrClient() {
        //LocalSimpleChannelPool channelPool = new LocalSimpleChannelPool(client, 1, addr, 3000);
        SimpleConnectionPool connectionPool = new SimpleConnectionPool(client, addr);
        NettySolrClient solrClient = new NettySolrClient(connectionPool);
        return solrClient;
    }

    protected String createDocId() {
        return UUID.randomUUID().toString();
    }

    protected void assertIdResult(QueryResponse queryResponse, String idValue) {
        SolrDocumentList results = queryResponse.getResults();
        Assert.assertEquals(results.getNumFound(), 1);
        Assert.assertEquals(idValue, results.get(0).getFieldValue("id"));
    }

    protected SolrClient solrClient = null;

    @Before
    public void create_netty_solr_client() {
        solrClient = createNettysolrClient();
    }

    @After
    public void shutdown_netty_solr_client() {
        if (solrClient != null) {
            try {
                solrClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void test_netty_client_query() {
        String id = createDocId();
        SolrQuery solrQuery = new SolrQuery("*:*");
        solrQuery.set(RequestParams.WriteResponseId, id);

        try {
            QueryResponse query = solrClient.query(solrQuery);
            //System.out.println(query);
            Assert.assertEquals("GET", query.getResponseHeader().get("METHOD"));
            assertIdResult(query, id);
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
        }
    }

    protected void assertVootooException(Throwable e, int code, SolrQuery solrQuery, boolean hasTrace) {
        Assert.assertTrue(e instanceof VootooException);
        Assert.assertEquals(((VootooException) e).code(), code);
        Assert.assertEquals(e.getMessage(), solrQuery.get(RequestParams.ErrorMsg));
        Assert.assertEquals(hasTrace, ((VootooException) e).getRemoteTrace() != null);
    }

    @Test
    public void test_netty_client_solr_exception() {
        SolrQuery solrQuery = new SolrQuery("*:*");
        solrQuery.add(RequestParams.ErrorCode, "503");
        solrQuery.add(RequestParams.ErrorMsg, "test 503 solr exception");
        solrQuery.add(RequestParams.ErrorMeta, "myKey:otherV");

        try {
            solrClient.query(solrQuery);

            Assert.fail("has Exception, but not throw");
        } catch (Exception e) {
            assertVootooException(e, 503, solrQuery, false);
        }

        solrQuery = new SolrQuery("*:*");
        solrQuery.add(RequestParams.ErrorCode, "429");
        solrQuery.add(RequestParams.ErrorMsg, "test 429 VootooException too many request");

        try {
            solrClient.query(solrQuery);
            Assert.fail("has Exception, but not throw");
        } catch (Exception e) {
            assertVootooException(e, 429, solrQuery, false);
        }

        //unknow
        int unknownCode = 1000000002;
        solrQuery = new SolrQuery("*:*");
        solrQuery.add(RequestParams.ErrorCode, String.valueOf(unknownCode));
        solrQuery.add(RequestParams.ErrorMsg, "test VootooException code=" + unknownCode);

        try {
            solrClient.query(solrQuery);
            Assert.fail("has Exception, but not throw");
        } catch (Exception e) {
            Assert.assertTrue(e instanceof SolrServerException);
            Throwable throwable = ((SolrServerException) e).getCause();
            assertVootooException(throwable, VootooException.VootooErrorCode.UNKNOWN.code, solrQuery, true);

            VootooException ve = (VootooException) throwable;
            Assert.assertEquals(ve.getUnknownCode(), unknownCode);
            //System.out.println(ve.getRemoteServer());
            //System.out.println(ve.getRemoteTrace());
        }
    }

    @Test
    public void test_in_query_other_exception() {
        SolrQuery solrQuery = new SolrQuery("*:*");
        solrQuery.add(RequestParams.ExceptionClass, RejectedExecutionException.class.getName());
        //solrQuery.add(RequestParams.ExceptionClass, SolrException.class.getName());

        try {
            solrClient.query(solrQuery);
            Assert.fail("has Exception, but not throw");
        } catch (Exception e) {
            Assert.assertTrue(e instanceof VootooException);

            VootooException ve = (VootooException) e;
            Assert.assertEquals(ve.code(), SolrException.ErrorCode.SERVER_ERROR.code);
            Assert.assertEquals(ve.getMessage(), "mock test ExceptionClass for junit");
            Assert.assertTrue(ve.getRemoteTrace() != null);
        }
    }
}