Servlet上下文,集合和序列化

时间:2016-07-13 09:33:07

标签: servlets java-ee serialization collections

通常,我从事Java EE应用程序。今天我遇到了一个问题:在servlet上下文中序列化集合。就我而言,我的应用程序包含一个Servlet Context Listener和许多servlet。

  • 上下文侦听器在初始化时加载包含多个产品列表的ConcurrentHashMap,并使用任务调度程序刷新此列表。
  • servlets应该根据用户提供的参数访问正确的列表。

这里是我的contextInitialized Listener的代码:

public void contextInitialized(ServletContextEvent event) {
    app = event.getServletContext();
    myMap = new ConcurrentHashMap<String, Catalog>();
    myMap.put("FR", new Catalog());
    myMap.put("UK", new Catalog());
    app.setAttribute("catalogue", myMap);
    scheduler = Executors.newSingleThreadScheduledExecutor();
   scheduler.scheduleAtFixedRate(new AutomateRefresh(), 0, 60, TimeUnit.MINUTES);
}

为了显示我的问题,我创建了一个servlet,它在上下文中显示了布尔值或ConcurrentHashMap的所有内容 我发现这种结果并不奇怪:

javax.servlet.context.tempdir is equal to...
Working is equal to... true
org.apache.catalina.resources is equal to...
org.apache.tomcat.InstanceManager is equal to... 
org.apache.catalina.jsp_classpath is equal to... 
javax.websocket.server.ServerContainer is equal to... 
org.apache.jasper.compiler.TldCache is equal to... 
catalogue is equal to... 
org.apache.tomcat.JarScanner is equal to...

如您所见,我的两个自定义键(布尔工作和ConcurrentHashMap目录)存在。但是,如果没有在Listener中访问, catalog 是空的。

我发现:

  

java.util.HashMap的序列化形式不会对桶本身进行序列化,并且哈希码不是持久化状态的一部分。

     

来源:Serializing and deserializing a map with key as string

对于许多项目,可序列化和线程安全的集合很有用。我可能不是唯一一个正在寻找它的人(参见有关servlet上下文的主题数量)。

ConcurrentHashMap是线程安全的,但我无法在其他servlet中检索我的数据(在同一个应用程序中)。是否有一个Thread的实现,它是线程安全的和可序列化的(由于WebLogic服务器策略)?或者我是以错误的方式使用它?

编辑:代码&#34;显示上下文servlet&#34;

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{
    System.out.println("List of all values in the context:");
    Enumeration<?> e = getServletContext().getAttributeNames();
    while (e.hasMoreElements())
    {
        String name = (String) e.nextElement();
        System.out.print("\n" + name + " is equal to... ");

        // Get the value of the attribute
        Object value = this.getServletContext().getAttribute(name);

        if (value instanceof ConcurrentHashMap) {
            ConcurrentHashMap<String, Catalog> map = (ConcurrentHashMap<String, Catalog>) value;

            Iterator<Entry<String, Catalog>> it = map.entrySet().iterator();

            while (it.hasNext()) {
                ConcurrentHashMap.Entry<String, Catalog> entry = (ConcurrentHashMap.Entry<String, Catalog>)it.next();
                System.out.print("\t" + entry.getKey() + "=" + entry.getValue());
            }
        } else if (value instanceof Boolean) {
            System.out.print((Boolean)value);
        }
    }
}

EDIT2:就像BalusC一样,HashMap可能是null(一个新手的错误?)。

这里是任务代码。任务在Listener中。 Listener使用新的空对象初始化HashMap。任务在webapp启动时刷新对象,然后每小时刷新一次。

public class AutomateRefresh implements Runnable {
    public void run() {
        System.out.println("Scheduler trigger");

        if(app.getAttribute("catalogue") instanceof ConcurrentHashMap){
            myMap = (ConcurrentHashMap<String, Catalog>) app.getAttribute("catalogue");

            //Autorefresh
            Iterator<Entry<String, Catalog>> it = myMap.entrySet().iterator();
            while (it.hasNext()) {
                ConcurrentHashMap.Entry<String, Catalog> entry = (ConcurrentHashMap.Entry<String, Catalog>)it.next();
                ((Catalog)entry.getValue()).setValid(false);//Set as not valid anymore for further request
                try {
                    ((Catalog)entry.getValue()).refreshdb((String) entry.getKey());//TODO rework to use REST API
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                it.remove(); // avoids a ConcurrentModificationException
                app.setAttribute("catalogue", myMap);
                app.setAttribute("Working", true);
                System.out.println((String)entry.getKey() + " = " + (Catalog)entry.getValue());
            }
        }
        else{
            System.out.println("Catalogue is not an instance of ConcurrentHashMap as expected.");
            app.setAttribute("Working", false);
        }

    }
}

当任务触发时,对于存储在Context中的每个目录,任务都会更新它们存储的数据。它还在控制台中显示数据。

结果:

Refresh Catalog for UK with DB
UK = Catalog [list size is : 0 valid=true, lastToken=notoken]
Refresh Catalog for FR with DB
FR = Catalog [list size is : 30 valid=true, lastToken=notoken]

Catalog是一个带有ArrayList,boolean和String的类。一切似乎都是正确的:英国应该是空的但不是空的,FR应该包含30种产品。

我仍然无法在其他servlet中访问此数据。

1 个答案:

答案 0 :(得分:0)

我发现问题的根源,新手错误如预期的那样:

我试图以这种方式更新,假设它会直接在ConcurrentHashMap中更新对象

((Catalog)entry.getValue()).refreshdb((String) entry.getKey());

我将其替换为:

Catalog myCatalog = (Catalog)entry.getValue();
myCatalog.refreshdb((String) entry.getKey());
myMap.put((String)entry.getKey(), myCatalog);

它现在有效。

我仍然不知道为什么我的对象可以从监听器访问,他们不应该这样工作。也许我的服务器有一种奇怪的行为?无论如何,这个问题是固定的。

感谢BalusC的帮助。