8

I have a a library that is bundled as an executable jar file and added to weblogic / tomcat classpath, how can I execute a main method from the jar file when the server is starting and loading the classes from the jar file.

what I want to is to have some initialization code to be executed first thing when the jar file is loaded and server is starting without any user intervention.

Note: I know I can bundle my jar in a war file, but I have some aspectj code in my library that I want to weave all running applications in the jvm, when I bundle my jar in war file, the aspectj code will only weave into the classes in the war file so I added my library jar file in the classpath.

Thanks in advance.

Sammy
  • 4,538
  • 8
  • 33
  • 52

4 Answers4

5

Add a class inside your JAR with the following code:

public class TomcatStartupListener implements org.apache.catalina.LifecycleListener {
  public void lifecycleEvent(org.apache.catalina.LifecycleEvent event) {
    if (event.getType().equals("after_start")) {
      // call your main method here
    }
  }
}

Note: In order to compile this, you need to add <tomcat-dir>/lib/catalina.jar to your classpath. Otherwise when compiling it won't be able to find the necessary interfaces (org.apache.catalina.LifecycleListener and org.apache.catalina.LifecycleEvent). Once you're done with the compiling, put the JAR as usual under <tomcat-dir>/lib.

Now open <tomcat-dir>/conf/server.xml and add the following under the <Server> section:

<Listener className="com.yourpackage.TomcatStartupListener" />

Now whenever your Tomcat server starts, this TomcatStartupListener class inside your JAR will be called, and you can invoke your main method. There are a whole lot of other event types too! You can use any of these event types:

  • before_init
  • after_init
  • before_start
  • configure_start
  • start
  • after_start
  • before_stop
  • stop
  • configure_stop
  • after_stop
  • before_destroy
  • after_destroy

This approach is necessary because of the way the classloaders work in Tomcat (or even most JVMs). Here are the important points from that link:

There are three aspects of a class loader behavior
  Lazy Loading
  Class Caching
  Separate Namespaces

The JVM will get very heavy if all classes inside all JARs get loaded indiscriminately. So the classes inside shared JARs are loaded only on-demand. The only way for you to invoke the main method is to add the above lifecycle listener.

Subhas
  • 14,290
  • 1
  • 29
  • 37
  • Is there any global solution that work on all application servers that is not specific to tomcat? – Sammy Jun 08 '13 at 10:30
  • Nope. Oracle has its own APEX listeners, and similarly each server has its own listeners. J2EE only defines listeners for a specific web context, it doesn't specify listeners for an entire server. So each server has its own way of listening to server-wide events. – Subhas Jun 08 '13 at 11:52
  • If you're creating a JAR, then the way other libraries do it is - have **TWO JARs**. One is your main JAR. Second is a server-specific *Listener JAR*, which just has the listener class. e.g. for Tomcat you would have the above code in a `yourapp-tomcat.jar`, and for Oracle you would have different code in `yourapp-oracle.jar`. Then allow the users to use whichever extra listener JAR that corresponds to the app server that they use. – Subhas Jun 08 '13 at 11:53
2

Perhaps the simplest thing to do is to deploy a trivial servlet in a .war file that references your .jar file. The servlet can be configured to start up upon deployment/container start, and then it can invoke the class containing your main() method.

Community
  • 1
  • 1
Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • I have some aspectj code in my library that I want to weave all running applications in the jvm, when I bundle my jar in war file, the aspectj code will only weave into the classes in the war file. – Sammy May 31 '13 at 12:05
  • Guess this method won't work, as it doesn't instrument global classes or classes in other WAR files. Please see my solution below if it fits. – Subhas Jun 08 '13 at 07:25
0

As application servers / servlet containers typically have a lot of different classloaders, you'll most likely need a different strategy for weaving aspects into your code than in standalone applications.

I would recommend to add the aspects to every war file deployed at build time. This might be following a common technique - as opposed to a server specific one.

Further, I'm not sure it can actually be done (properly & supported) on a server. Typically a server is built to separate all webapps from each other. You might get it to work, but it might break on the next update of the server.

It might be easier to suggest an alternative technique if you'd state the problem that you want to solve with your proposed approach.

Edit after your comment: Consider the standard web application lifecycle: You can execute some code, e.g. in a servlet, upon it being deployed. If you insist on your code being contained in main, you can call this method from your webapp's initialization code.

Olaf Kock
  • 46,930
  • 8
  • 59
  • 90
  • i m writing a performance monitoring tool customized to our business needs, and I would like to be able to monitor all the applications deployed without having to include the jar in every war file, so far it is working great by bundling all the aspects and the needed dependencies in a jar file and adding it to the classpath of the server ( weblogic and tomcat for now), I m able to weave into all the applications deployed to the JVM. but I still need to run some initialization code from the jar file when it is loaded, without having to alter the other applications – Sammy May 31 '13 at 14:10
  • ... so that was the intent of the question is to have some initialization code that execute from the jar to perform initialization needed for the aspects. – Sammy May 31 '13 at 14:10
  • The only issue with the proposed approach is that the jar with the aspect is supposed to be loaded before any web app, so calling the main method in the servlet might be too late. – Sammy Jun 07 '13 at 19:47
0

You need to register a Java Agent. See this link: java.lang.instrument.

java.lang.instrument provides services that allow Java programming language agents to instrument programs running on the JVM.

This is the right way to do this.

Badaro
  • 576
  • 3
  • 12