Class loader - EAR with EJBs, WARs, and Singleton classes

Discussions

General J2EE: Class loader - EAR with EJBs, WARs, and Singleton classes

  1. Weblogic 6.1 SP2
    We have one application .ear file that contains all of our utility classes, CMP beans (.jar files), and our WAR files. We package everything in an EAR file to support hot-deployment and so that our WAR and EJB files can easily see the utility classes (specified in the class-path of the EJB's manifest file).
    Here's the problem, we have a Singleton Logger (part of our utility package in the EAR) that needs to be initialized when the server comes up. The ONE Singleton instance needs to be accessible by the EJBs and Servlets in the .EAR file.

    I came up with a few ideas of how to do this, but none of them worked properly because of classloader issues (I think). Here's what I thought of:

    1.) Have the Startup class call an EJB that performs the Singleton initialization. This, I thought, would allow the Singleton instance to be created in the Class-Loader responsible for the EJBs. Therefore, all EJBs and Servlets (since the WAR classloader extends the EJB classloader) would be able to access this ONE Singleton. There were problems with this...it seemed like two Singleton instances were created somehow (possibly, two classloaders?)

    2.) Have a load-on-startup Servlet that does the initialization. This way, the Singleton would be created in the Classloader responsible for the .EAR file. However, this did not work because the Singleton was created in the Classloader responsible for the .WAR file. This classloader does extend from the EJB classloader, but, the EJB classloader doesn't know about the WAR classloader. So, when my EJB accesses the Singleton, it's a different instance then the one created by the Servlet.

    So, the question is, how do I create ONE Singleton that is accessible by all EJBs and Servlets. NOTE: I cannot specify my Singleton in the System classpath.

    The log class is in a jar file in the ear, and not in the war or EJB. As the spec says, I put my utility classes in a jar file in a directory called library in the .ear file.

    I do not want the Singleton to be remoteable or accessible by other servers in the cluster. I simply want there to be one Singleton per Weblogic Server. So, anything running in one instance of Weblogic Server will use the same singleton. If a second server is running on a different machine, then beans and servlets running in that server will use a different singleton. Again, one singleton instance per Weblogic server.

    Thanks for any help you gurus may provide!

    Dan
  2. Hi Dan

    I have been solving just about the same problem as you outline in your problem statement. It is not what I consider a pritty solution but it works fine for the time being.

    In the application we are developing we are using a single Log4J instance for logging purpose through out all of the application, i.e. the Log4J singleton is shared by both the EJB and WAR layer of the J2EE app.

    The application may be deployed in either mixed (the EJB layer is made up of EJB-JAR's and the WAR layer is in exploded format) or exploded format (the whole application is in exploded format) and is running on a single WebLogic 6.1 sp.2. on Windows 2000

    To solve the problem we ended up using your 2) idea. We have a load-on-startup servlet doing all the initialization of the Log4J servlet (in the init() method of the servlet) by simply instantiating a single utility class located in the EJB layer of the application. The utility class contains a reference to the Log4J instance the must be used through out the entire application.

    Please note that is of extreme importance that the utility class is located in the EJB layer part of the application and NOT in the WAR layer part of the app.

    Also note that the Log4.property configuration file is stored in the WAR layer of the application (The WEB-INF/classes directory) and NOT in the EJB layer due to a bug in the BEA app. server.

    The load-on-startup servlet located in the WAR layer of the application looks something like this
    public class Log4jServlet extends HttpServlet {
     
      public void init() {
        // Create a sinbleton instance of the Log4J logger if necessecary.
        ServletContext servletContext = getServletContext();
        String prefix = servletContext.getRealPath("/");
        synchronized(Category.class) {
          Category category = Log4jSingleton.getLog();
          if(category == null) {
            // Instantiate a Log4j Singleton classe.
            category =
            Category.getInstance(Log4jServlet.class.getName());
            PropertyConfigurator.configure(prefix +
                      "/WEB-INF/classes/com/log4j.properties");
            Log4jSingleton.setLog(category);
            
            // Output startup text.
            Log4jSingleton.getLog().info("Initialized Log4J.");
          }
        }
      }
    }


    and the utility class located in the EJB layer of the application looks something like this:
    public class Log4jSingleton {
      private static Category log = null;
      
      private Log4jSingleton() {}

      public static void setLog(Category newLog) {
        log = newLog;
      }
      
      public static Category getLog() {
        return log;
      }
    }

    As I said previously this is not a pritty solution but it will do for the time being until I come up with a better idea.

    Hopes this give you some inspiration :-) . Please let me know if you come up with a better idea.

    Kind regards
    Jorgen

  3. Thanks for your reply, but I'm a little confused. What do you mean by the following: "Please note that is of extreme importance that the utility class is located in the EJB layer part of the application and NOT in the WAR layer part of the app."

    How do I ensure that the utility classes are part of the EJB layer? I have a .ear file with multiple .jar files (1 bean / jar file) and a .war file. My utility classes are in a jar called utility.jar in a subdirectory called library of the .ear file.

    Thanks.
    Dan
  4. Dan, why does the logging utility class need to be initialized on server startup? I usually implement singleton classes using a static initializer block, such as...

    static {

    //peform initializtion here
    }

    Using static blocks are efficient and clean because they are executed automatically by the ClassLoader when the class gets loaded; and the block only executes once.

    Also, how did 2 singleton classes get generated when you ran your test? I'm wondering because I also have a similar situation. I have an EAR file with the following structure...

    employee.jar
    employeeSearchEJB.jar
    webApp1.war
    webApp2.war
    META-INF
     application.xml

    In the employee.jar archive, there exists a class called LogManager, which is a singleton and allows logging of application events and errors. When a servlet in webApp1.war invokes LogManager, it forces the class to initialize via a static block, and I use a System.out.println() line to indicate the initialization to the console. When a servlet from webApp2.war invokes LogManager, the static block does NOT execute again, which is what I would expect from a singleton class. Are you saying that you have 2 instances of a singleton running inside the EAR?

    SAF
  5. I seem to be having the same problem described in this posting. I seems that a new instance of the utility class is created each time I enter a new WAR within the EAR.

    Like mentioned, the setup of our app is...

    EAR
    - WAR1
    - WAR2
    - JAR

    We call a static initializer in a class within the common JAR.
  6. I know that the thread started out referring to WLS 6.1. I am not sure about 6.1 but, in WLS 8.1, you can put the log4j jar in APP_INF/lib and the properties in APP-INF/classes.

    This will let you use log4j from Servlets, POJOs and the EJB code. The appenders can be used to control the CATEGORY and location of the log file if you NEED to send the messages to different files.
  7. ClassWorlds[ Go to top ]

    Hey, guys--I'm not sure if this thread has gone stale or not, but I thought I'd mention ClassWorlds to y'all.

    ClassWorlds is an Open Source project at the Codehaus that seeks to replace the hierarchical Class Loader structure with a structure that more closely resembles a directed graph. I'm using it in a handful of my projects, and I think it could easily answer the questions you've put forth.