Search code examples
javamavenzeromqjeromqapache-commons-daemon

java.lang.ClassNotFoundException: org.zeromq.ZContext when trying to start windows service


I have a basic Maven java app that I created and it depends on JeroMQ which is a full Java implemenetation of ZeroMQ. Since I also need to wrap this java app as a windows service, I chose to use Apache Commons Daemon and specifically, followed this excellent example: http://web.archive.org/web/20090228071059/http://blog.platinumsolutions.com/node/234 Here's what the Java code looks like:

package com.org.SubscriberACD;

import java.nio.charset.Charset;

import org.zeromq.ZContext;
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Socket;

/**
 * JeroMQ Subscriber for Apache Commons Daemon
 *
 */
public class Subscriber 
{
    /**
    * Single static instance of the service class
    */
    private static Subscriber subscriber_service = new Subscriber();

    /**
     * Static method called by prunsrv to start/stop
     * the service.  Pass the argument "start"
     * to start the service, and pass "stop" to
     * stop the service.
     */
    public static void windowsService(String args[]) {
       String cmd = "start";
       if(args.length > 0) {
          cmd = args[0];
       }

       if("start".equals(cmd)) {
          subscriber_service.start();
       }
       else {
          subscriber_service.stop();
       }
    }

    /**
     * Flag to know if this service
     * instance has been stopped.
     */
    private boolean stopped = false;


    /**
     * Start this service instance
     */
    public void start() {

       stopped = false;

       System.out.println("My Service Started "
                          + new java.util.Date());

       ZContext context = new ZContext();

       Socket subscriber = context.createSocket(ZMQ.SUB);
       subscriber.connect("tcp://localhost:5556");
       String subscription = "MySub";
       subscriber.subscribe(subscription.getBytes(Charset.forName("UTF-8")));

       while(!stopped) {
          System.out.println("My Service Executing "
                              + new java.util.Date());

          String topic = subscriber.recvStr();
          if (topic == null)
              break;
          String data = subscriber.recvStr();
          assert(topic.equals(subscription));
          System.out.println(data);

          synchronized(this) {
             try {
                this.wait(60000);  // wait 1 minute
             }
             catch(InterruptedException ie){}
          }
       }

       subscriber.close();
       context.close();
       context.destroy();

       System.out.println("My Service Finished "
                           + new java.util.Date());
    }

    /**
     * Stop this service instance
     */
    public void stop() {
       stopped = true;
       synchronized(this) {
          this.notify();
       }
    }
 }

Then I created the following folder structure just like the tutorial suggested:

E:\SubscriberACD
   \bin
        \subscriberACD.exe
        \subscriberACDw.exe
   \classes
        \com\org\SubscriberACD\Subscriber.class
   \logs

I then navigated to the bin directory and issued the following command to install the service:

subscriberACD.exe //IS//SubscriberACD --Install=E:\SubscriberACD\bin\subscriberACD.exe --Descriptio
n="Subscriber using Apache Commons Daemon" --Jvm=c:\glassfish4\jdk7\jre
\bin\server\jvm.dll --Classpath=E:\SubscriberACD\classes --StartMode=jvm
 --StartClass=com.org.SubscriberACD.Subscriber --StartMethod=windowsSer
vice --StartParams=start --StopMode=jvm --StopClass=com.org.SubscriberA
CD.Subscriber --StopMethod=windowsService --StopParams=stop --LogPath=E:\SubscriberACD\logs --StdOutput=auto --StdError=auto

The install works fine since I can see it in Windows Services. However, when I try to start it from there, I get an error saying "Windows cannot start the SubscriberACD on Local Computer".

I checked the error logs and see the following entry:

2016-04-14 14:38:40 Commons Daemon procrun stderr initialized
Exception in thread "main" ror: org/zeromq/ZContext
    at com.org.SubscriberACD.Subscriber.start(Subscriber.java:57)
    at com.org.SubscriberACD.Subscriber.windowsService(Subscriber.java:33)
Caused by: java.lang.ClassNotFoundException: org.zeromq.ZContext
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
    ... 2 more

It's worth noting that JeroMQ is currently a jar under my Maven Dependencies. I configured it from my POM.xml file.

I think the problem might be that my service doesn't have access to the JeroMQ jar that is under my Maven Dependencies. My assumption is that the class file doesn't contain the dependencies. So what I tried was exporting my entire project as a jar and stuck that baby under E:\SubscriberACD\classes\ So my structure now looks like this:

E:\SubscriberACD
   \bin
        \subscriberACD.exe
        \subscriberACDw.exe
   \classes
        \com\org\SubscriberACD\
             \Subscriber.class
        \Subscriber.jar
   \logs

However, that didn't fix the issue. Can anyone shed some light on this?


Solution

  • Change your --Classpath argument to :

    --Classpath=E:\SubscriberACD\classes\your-jar-filename.jar 
    

    You almost certainly have other jarfiles you'll need, so just append them to the end of the --Classpath using ; (semi-colon) delimiters...

    --Classpath=E:\SubscriberACD\classes\your-jar-filename.jar;e:\other-dir\classes\some-other.jar;etc...