Monday, November 21, 2016

Quickly debug your Solr add-on

Let’s say you need to write a component, or a request handler, or in general some piece of custom code that needs to be plugged in Solr. 

You will probably write unit tests, integration tests, everything to make sure things behave as you would expect; but while developing, it is (at least for me) tremendously useful a productive and debug environment where it is possible to follow step by step what's happening in my code or within the hosting framework (Solr, in this case), taking a deep look at how actually things work behind the scenes. 


The following are a simple sequence of steps I’m usually doing to quickly have such environment.
All you need is:

  • Java (of course)
  • Eclipse or Intellij (I will use Eclipse in this post) equipped with Maven or Gradle
The approach is the same you would use for running integration tests with Solr, as described here

The difference, this time, is that you will use a "dummy" integration test that starts up Solr and just wait. 
I know, you’re thinking that is not an elegant solution…yeah, I agree with you, but I never told you I’m going to tell you about an elegant solution…just keep in mind that everything can be setup in 5 minutes…this is the real focus here, and in my opinion the most important thing; on top of that you can spend the next decade writing down the most beatiful-x-unit-oriented test suite, but you can do that beside that "dummy" test.

Step #1: Create a new Maven project


The pom.xml (you can find the complete pom.xml in github) has two interesting sections; the first is a central area where we declare the main dependencies versions as general properties:

<properties>
     <jdk.version>1.8</jdk.version>
     ...
     <solr.version>6.2.1<solr.version>
</properties>

I used to declare this section because it is a central point where I can easily control the main artifacts versions (e.g. if I want to switch to Solr 5.5 it's just a matter of replacing 6.2.1 with 5.5)


The second section includes the dependencies:

<dependency>
     <groupId>org.apache.solr</groupId>
     <artifactId>solr-core</artifactId>
     <version>${solr.version}</version>
</dependency>
<dependency>
     <groupId>org.apache.solr</groupId>
     <artifactId>solr-test-framework</artifactId>
     <version>${solr.version}</version>
</dependency>
...

Step #2: Write your add-on

The code I'm working on while writing this post is a custom RequestHandler (a nice subject for a next post); something like this:
public class CompositeSearchHandler extends SearchHandler { 
    @Override
    public void init(NamedList args) {
       super.init(args);
    }
 
    @Override
    public void handleRequestBody(
       final SolrQueryRequest request, 
       final SolrQueryResponse response) throws Exception {
  
      Arrays.stream(refNames(request.getParams()))
         .forEach(refName -> {
            requestHandler(refName).handleRequest(request, response);
         });
 ...
}
It's not important here what my custom code is actually doing; what I want is
  • put a debug breakpoint on the red line
  • run the component within Solr 
  • debug step by step what happens, starting from the red line and going in the Solr codebase (RequestHandlerBase, in this case)

Step #3: Write a base (abstract) Solr IntegrationTest 


This will be the superclass of my "dummy" test: it will just provide a Solr environment. The core method is decorated with the @BeforeClass annotation:
public abstract class BaseIntegrationTest extends SolrJettyTestBase {
    protected static JettySolrRunner SOLR;

    @BeforeClass
    public static void init() { 
       System.setProperty(
              "solr.data.dir"initCoreDataDir.getAbsolutePath());
   
       try {
            SOLR = createJetty(
                 "path-to-solr-home",
                 JettyConfig.builder()
                    .setPort(8983)
                    .setContext("/solr")
                    .stopAtShutdown(true)
                    .build());  
      } catch (final Exception exception) {
        throw new RuntimeException(exception);
      }
 }

Step #4: Write the "dummy" test


I guess no explanation is needed here....
public abstract class StartDevSolr extends BaseIntegrationTest {
    @Test
    public void start() throws IOException {
        System.out.printn("Press any key to stop Solr");
        System.in.read(); 
    }

Step #5: Done! Start debugging


Right click on the test case and "Debug as -> JUnit test". As you can see an embedded Solr is started, and your classes are there, loaded within the same JVM. Now it is up to you. In my example, the request handler answers on the "/sample" endpoint so all what I need is a command line like this:
> curl "http://127.0.0.1:8983/solr/gazza/sample?q=something" 
and as expected, this is what I see in Eclipse:


Enjoy!
Post a Comment