org.apache.solr.client.solrj.TestSolrJErrorHandling.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.client.solrj.TestSolrJErrorHandling.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.apache.solr.client.solrj;

import java.io.BufferedOutputStream;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.invoke.MethodHandles;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.io.IOUtils;
import org.apache.solr.SolrJettyTestBase;
import org.apache.solr.SolrTestCaseJ4.SuppressSSL;
import org.apache.solr.client.solrj.impl.BinaryRequestWriter;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.request.RequestWriter;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrInputDocument;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@SuppressSSL(bugUrl = "https://issues.apache.org/jira/browse/SOLR-5776")
public class TestSolrJErrorHandling extends SolrJettyTestBase {
    private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    List<Throwable> unexpected = new CopyOnWriteArrayList<>();

    @BeforeClass
    public static void beforeTest() throws Exception {
        createJetty(legacyExampleCollection1SolrHome());
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        unexpected.clear();
    }

    public String getChain(Throwable th) {
        StringBuilder sb = new StringBuilder(40);
        Throwable lastCause = null;
        do {
            if (lastCause != null)
                sb.append("->");
            sb.append(th.getClass().getSimpleName());
            lastCause = th;
            th = th.getCause();
        } while (th != null);
        sb.append("(" + lastCause.getMessage() + ")");
        return sb.toString();
    }

    public void showExceptions() throws Exception {
        if (unexpected.isEmpty())
            return;

        Map<String, Integer> counts = new HashMap<>();

        // dedup in case there are many clients or many exceptions
        for (Throwable e : unexpected) {
            String chain = getChain(e);
            Integer prev = counts.put(chain, 1);
            if (prev != null) {
                counts.put(chain, prev + 1);
            }
        }

        StringBuilder sb = new StringBuilder("EXCEPTION LIST:");
        for (Map.Entry<String, Integer> entry : counts.entrySet()) {
            sb.append("\n\t").append(entry.getValue()).append(") ").append(entry.getKey());
        }

        log.error(sb.toString());
    }

    @Test
    public void testWithXml() throws Exception {
        HttpSolrClient client = (HttpSolrClient) getSolrClient();
        client.setRequestWriter(new RequestWriter());
        client.deleteByQuery("*:*"); // delete everything!
        doIt(client);
    }

    @Test
    public void testWithBinary() throws Exception {
        HttpSolrClient client = (HttpSolrClient) getSolrClient();
        client.setRequestWriter(new BinaryRequestWriter());
        client.deleteByQuery("*:*"); // delete everything!
        doIt(client);
    }

    Iterator<SolrInputDocument> manyDocs(final int base, final int numDocs) {
        return new Iterator<SolrInputDocument>() {
            int count = 0;

            @Override
            public boolean hasNext() {
                return count < numDocs;
            }

            @Override
            public SolrInputDocument next() {
                int id = base + count++;
                if (count == 1) { // first doc is legit, and will increment a counter
                    return sdoc("id", "test", "count_i", map("inc", 1));
                }
                // include "ignore_exception" so the log doesn't fill up with known exceptions, and change the values for each doc
                // so binary format won't compress too much
                return sdoc("id", Integer.toString(id), "ignore_exception_field_does_not_exist_" + id,
                        "fieldval" + id);
            }

            @Override
            public void remove() {
            }
        };
    };

    void doThreads(final HttpSolrClient client, final int numThreads, final int numRequests) throws Exception {
        final AtomicInteger tries = new AtomicInteger(0);

        List<Thread> threads = new ArrayList<>();

        for (int i = 0; i < numThreads; i++) {
            final int threadNum = i;
            threads.add(new Thread() {
                int reqLeft = numRequests;

                @Override
                public void run() {
                    try {
                        while (--reqLeft >= 0) {
                            tries.incrementAndGet();
                            doSingle(client, threadNum);
                        }
                    } catch (Throwable e) {
                        // Allow thread to exit, we should have already recorded the exception.
                    }
                }
            });
        }

        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            thread.join();
        }

        showExceptions();

        int count = getCount(client);
        if (count > tries.get()) {
            fail("Number of requests was " + tries.get() + " but final count was " + count);
        }

        assertEquals(tries.get(), getCount(client));

        assertTrue("got unexpected exceptions. ", unexpected.isEmpty());
    }

    int getCount(HttpSolrClient client) throws IOException, SolrServerException {
        client.commit();
        QueryResponse rsp = client.query(params("q", "id:test", "fl", "count_i", "wt", "json"));
        int count = ((Number) rsp.getResults().get(0).get("count_i")).intValue();
        return count;
    }

    // this always failed with the Jetty 9.3 snapshot
    void doIt(HttpSolrClient client) throws Exception {
        client.deleteByQuery("*:*");
        doThreads(client, 10, 100);
        // doSingle(client, 1);
    }

    void doSingle(HttpSolrClient client, int threadNum) {
        try {
            client.add(manyDocs(threadNum * 1000000, 1000));
        } catch (HttpSolrClient.RemoteSolrException e) {
            String msg = e.getMessage();
            assertTrue(msg, msg.contains("field_does_not_exist"));
        } catch (Throwable e) {
            unexpected.add(e);
            log.error("unexpected exception:", e);
            fail("FAILING unexpected exception: " + e);
        }
    }

    /***
    @Test
    public void testLive() throws Exception {
      HttpSolrClient client = new HttpSolrClient("http://localhost:8983/techproducts/solr/");
      client.add( sdoc() );
      doiIt(client);
    }
    ***/

    String getJsonDocs(int numDocs) {
        StringBuilder sb = new StringBuilder(numDocs * 20);
        sb.append("[");
        for (int i = 0; i < numDocs; i++) {
            sb.append("{ id : '" + i + "' , unknown_field_" + i + " : 'unknown field value' }");
        }
        sb.append("]");
        return sb.toString();
    }

    byte[] whitespace(int n) {
        byte[] arr = new byte[n];
        Arrays.fill(arr, (byte) ' ');
        return arr;
    }

    String getResponse(InputStream is) throws Exception {
        StringBuilder sb = new StringBuilder();
        byte[] buf = new byte[100000];
        for (;;) {
            int n = 0;
            try {
                n = is.read(buf);
            } catch (IOException e) {
                // a real HTTP client probably wouldn't try to read past the end and would thus
                // not get an exception until the *next* http request.
                log.error("CAUGHT IOException, but already read " + sb.length() + " : " + getChain(e));
            }
            if (n <= 0)
                break;
            sb.append(new String(buf, 0, n, StandardCharsets.UTF_8));
            log.info("BUFFER=" + sb.toString());
            break; // for now, assume we got whole response in one read... otherwise we could block when trying to read again
        }
        return sb.toString();
    }

    @Test
    public void testHttpURLConnection() throws Exception {

        String bodyString = getJsonDocs(200000); // sometimes succeeds with this size, but larger can cause OOM from command line

        HttpSolrClient client = (HttpSolrClient) getSolrClient();

        String urlString = client.getBaseURL() + "/update";

        HttpURLConnection conn = null;
        URL url = new URL(urlString);

        conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");

        OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8);
        writer.write(bodyString);
        writer.flush();

        int code = 1;
        try {
            code = conn.getResponseCode();
        } catch (Throwable th) {
            log.error("ERROR DURING conn.getResponseCode():", th);
        }

        /***
         java.io.IOException: Error writing to server
         at __randomizedtesting.SeedInfo.seed([2928C6EE314CD076:947A81A74F582526]:0)
         at sun.net.www.protocol.http.HttpURLConnection.writeRequests(HttpURLConnection.java:665)
         at sun.net.www.protocol.http.HttpURLConnection.writeRequests(HttpURLConnection.java:677)
         at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1533)
         at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1440)
         at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
         */

        log.info("CODE=" + code);
        InputStream is;
        if (code == 200) {
            is = conn.getInputStream();
        } else {
            log.info("Attempting to get error stream.");
            is = conn.getErrorStream();
            if (is == null) {
                log.info("Can't get error stream... try input stream?");
                is = conn.getInputStream();
            }
        }

        String rbody = IOUtils.toString(is, StandardCharsets.UTF_8);
        log.info("RESPONSE BODY:" + rbody);
    }

    @Test
    public void testRawSocket() throws Exception {

        String hostName = "127.0.0.1";
        int port = jetty.getLocalPort();

        try (Socket socket = new Socket(hostName, port);
                OutputStream out = new BufferedOutputStream(socket.getOutputStream());
                InputStream in = socket.getInputStream();) {
            byte[] body = getJsonDocs(100000).getBytes(StandardCharsets.UTF_8);
            int bodyLen = body.length;

            // bodyLen *= 10;  // make server wait for more

            byte[] whitespace = whitespace(1000000);
            bodyLen += whitespace.length;

            String headers = "POST /solr/collection1/update HTTP/1.1\n" + "Host: localhost:" + port + "\n" +
            //        "User-Agent: curl/7.43.0\n" +
                    "Accept: */*\n" + "Content-type:application/json\n" + "Content-Length: " + bodyLen + "\n"
                    + "Connection: Keep-Alive\n";

            // Headers of HTTP connection are defined to be ASCII only:
            out.write(headers.getBytes(StandardCharsets.US_ASCII));
            out.write('\n'); // extra newline separates headers from body
            out.write(body);
            out.flush();

            // Now what if I try to write more?  This doesn't seem to throw an exception!
            Thread.sleep(1000);
            out.write(whitespace); // whitespace
            out.flush();

            String rbody = getResponse(in); // This will throw a connection reset exception if you try to read past the end of the HTTP response
            log.info("RESPONSE BODY:" + rbody);
            assertTrue(rbody.contains("unknown_field"));

            /***
            // can I reuse now?
            // writing another request doesn't actually throw an exception, but the following read does
            out.write(headers);
            out.write("\n");  // extra newline separates headers from body
            out.write(body);
            out.flush();
                
            rbody = getResponse(in);
            log.info("RESPONSE BODY:" + rbody);
            assertTrue(rbody.contains("unknown_field"));
            ***/
        }
    }

}