7

Good morning, I am currently developing a java web application that exposes a web service interface. In order to keep a global object in memory, I use the following class as a Singleton:

public class SingletonMap {
    private static final SingletonMap instance = new SingletonMap();
    private static HashMap couponMap = null;
    private static long creationTime;

    private SingletonMap() {
        creationTime = System.currentTimeMillis();
        couponMap = new HashMap();
    }

    public static synchronized SingletonMap getInstance() {
        return instance;
    }

    public static long getCreationTime() {
        return creationTime;
    }
}

I am using the above class in order to have the same instance of the HashMap for all the threads of the web service. The Web service class that maintains the SingletonMap object is the following:

@WebService()
public class ETL_WS {
    private String TOMCAT_TEMP_DIR;
    private final int BUFFER_SIZE = 10000000;
    private static SingletonMap couponMap;
    private static SingletonProductMap productCategoryMap;
    private String dbTable = "user_preferences";

    public ETL_WS() {
        Context context = null;
        try {
            context = (Context) new InitialContext().lookup("java:comp/env");
            this.TOMCAT_TEMP_DIR = (String) context.lookup("FILE_UPLOAD_TEMP_DIR");
        }catch(NamingException e) {
        System.err.println(e.getMessage());
    }

    public long getCouponMapCreationTime() {
        return couponMap.getCreationTime();
    }

}

The reason i have the method getCouponMapCreationTime() is to check that all the threads of the web service are accessing the same object. Is the above approach correct? How about performance overheads? Do you think I need the Singleton properties, or could I just use a static HashMap for all the threads? If I use a static HashMap, is it going to be garbage collected in case no thread is active?

Thank you for your time.

nick.katsip
  • 868
  • 3
  • 12
  • 32

2 Answers2

11

A JAX-WS web service is by itself a Singleton. This means that all the request will be handled using a single web service instance (like a Servlet).

So, any member of the class will be 'shared' between all the request. In your case, you do not need to make your members (i.e. couponMap) an static attributes.

Conclusion: Don't worry, all your threads (request) will be accessing the same 'couponMap'. Because you don't need the getCouponMapCreationTime anymore, I think that you can eliminate the SingletonMap abstraction and use directly a Map in your web service class.

But I have something very important to add. If several threads (request) will be accessing your Map you have to make it thread-safe!!! There are a lot of way to do this, but I will give an idea: Use a ConcurrentHashMap instead of a HashMap. This will make all your get(), put(), remove() operations thread-safe! If you need a larger scope you can use synchronized blocks, but please avoid synchronize methods because the scoop is too large and always synchronize over this object.

ggarciao
  • 1,618
  • 16
  • 29
  • I see. And what if there is a big pause between client requests, will the object be garbage collected, or not? I mean, if the Web service is idle for some time, does the JVM run the garbage collector and cleans my instance? Sorry If I am so irritating, but I am new to this field, and I have never before deployed a web service with a persistent in-memory (RAM) object. – nick.katsip Jun 19 '12 at 09:20
  • 2
    No, the object will be never garbage collected because the framework keeps a reference to it. These kind of frameworks (servlet, jaxws, etc) always keep one single instance of the service object, but what they actually create/destroy is threads (using thread pool policies). But do not worry about this, this will no impact your in-memory shared data!!! – ggarciao Jun 19 '12 at 09:28
  • Thank you ggarciao. Your quick answers helped me a lot. – nick.katsip Jun 19 '12 at 09:30
  • Sorry to bother you again but i tried declaring the private ConcurrentHashMap couponMap; (and even as a static), but it seems that the object dies whenever a client gets his response. – nick.katsip Jun 19 '12 at 12:02
  • That is weird ... I will need more info about your configuration: what endpoint publisher are you using: Spring? Apache CXF? Can you add the endpoint configuration to your question? – ggarciao Jun 19 '12 at 15:35
  • 2
    I don't believe it's true that an @WebService instance is a singleton. Or, at least, not that it must be true per the spec. And at least on one JEE server, it is not true. In fact, this seems to imply that the norm is for a JAX-WS service to *not* be a singleton: http://stackoverflow.com/a/7010266/796761 – dbreaux Oct 20 '14 at 14:43
4

JAX-WS has its own patterns for creating singletons, you don't need to use static fields. You use the @Inject annotation into each service. See this blog post: http://weblogs.java.net/blog/jitu/archive/2010/02/19/jax-ws-cdi-java-ee-6-0 (but don't use @SessionScoped, use @Singleton)

Some other points:

  1. HashMap isn't thread-safe, you need ConcurrentHashMap.

  2. This catch(NamingException e) { System.err.println(e.getMessage()); is unhelpful. Rethrow it as a RuntimeException. You can't recover from it.

  3. Don't worry about performance overhead at this stage. Measure it once you have something working.

artbristol
  • 32,010
  • 5
  • 70
  • 103
  • 1
    Thank you for your quick answer. I checked the link you posted, but is it sure that all the threads will use the same instance of my SingletonMap class? In fact, the SingletonMap object that I am interesed in using should be initilized once (lazy), used by all the web service threads, and destroyed only when the web service goes down. P.S.: Thank you for the points :) – nick.katsip Jun 19 '12 at 08:03