/* =============================================================================== * * Part of the InfoGlue Content Management Platform (www.infoglue.org) * * =============================================================================== * * Copyright (C) * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2, as published by the * Free Software Foundation. See the file LICENSE.html for more information. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc. / 59 Temple * Place, Suite 330 / Boston, MA 02111-1307 / USA. * * =============================================================================== */ package org.infoglue.cms.util; import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import org.infoglue.cms.controllers.kernel.impl.simple.LuceneController; public class ChangeNotificationController { private final static Logger logger = Logger.getLogger(ChangeNotificationController.class.getName()); //The singleton private static ChangeNotificationController instance = null; //TEST private static class ThreadLocalNotifications extends ThreadLocal implements NotificationListener { public Object initialValue() { return new ArrayList(); } public List getList() { return (List) super.get(); } public void setContextParameters(Map map) { } public void notify(NotificationMessage message) { list.getList().add(message); } public void process() { logger.info("Process inside ChangeNotificationController"); } } private static ThreadLocalNotifications list = new ThreadLocalNotifications(); public void put(NotificationMessage notificationMessage) { list.getList().add(notificationMessage); } public void notifyListeners() { if(list.getList().size() > 0) logger.info("Now as the transaction is done and there are items in the notification list - let's notify the deliver app and other listeners..."); //This part asks the listeners to do it's stuff before we send the info to deliver. try { synchronized (listeners) { Iterator i = listeners.iterator(); while(i.hasNext()) { try { NotificationListener nl = (NotificationListener)i.next(); if(!unregisteredlisteners.contains(nl)) { logger.info("Notifying the listener to process:" + nl.getClass().getName()); nl.process(); } } catch(ConcurrentModificationException e) { logger.error("One of the listeners threw an exception but we carry on with the others. Error: " + e.getMessage()); } catch(Exception e) { logger.error("One of the listeners threw an exception but we carry on with the others. Error: " + e.getMessage(), e); } } listeners.removeAll(unregisteredlisteners); } synchronized (unregisteredlisteners) { unregisteredlisteners.clear(); } } catch (Exception e) { logger.error("Error calling listeners to process:" + e.getMessage(), e); } //Prepare and push notifications to deliver List internalMessageList = new ArrayList(); List publicMessageList = new ArrayList(); Iterator iterator = list.getList().iterator(); while(iterator.hasNext()) { NotificationMessage notificationMessage = (NotificationMessage)iterator.next(); if(notificationMessage.getType() == NotificationMessage.PUBLISHING || notificationMessage.getType() == NotificationMessage.UNPUBLISHING || notificationMessage.getType() == NotificationMessage.SYSTEM) { publicMessageList.add(notificationMessage); if(notificationMessage.getType() == NotificationMessage.SYSTEM) { internalMessageList.add(notificationMessage); } } else { internalMessageList.add(notificationMessage); } iterator.remove(); } Hashtable internalMessage = null; Hashtable publicMessage = null; if(internalMessageList.size() > 0) { internalMessage = new Hashtable(); Iterator internalMessageListIterator = internalMessageList.iterator(); int i = 0; while(internalMessageListIterator.hasNext()) { NotificationMessage notificationMessage = (NotificationMessage)internalMessageListIterator.next(); internalMessage.put(i + ".userName", notificationMessage.getSystemUserName()); internalMessage.put(i + ".timestamp", notificationMessage.getTimestamp()); internalMessage.put(i + ".className", notificationMessage.getClassName()); internalMessage.put(i + ".objectId", notificationMessage.getObjectId()); internalMessage.put(i + ".objectName", notificationMessage.getObjectName()); internalMessage.put(i + ".typeId", "" + notificationMessage.getType()); if(notificationMessage.getExtraInformation() != null) { for(String key : notificationMessage.getExtraInformation().keySet()) { internalMessage.put(i + "." + key, notificationMessage.getExtraInformation().get(key)); } } i++; } } if(publicMessageList.size() > 0) { publicMessage = new Hashtable(); Iterator publicMessageListIterator = publicMessageList.iterator(); int i = 0; while(publicMessageListIterator.hasNext()) { NotificationMessage notificationMessage = (NotificationMessage)publicMessageListIterator.next(); //For mixed env where we want to do cms upgrade first - remove when we release a new version if(i == 0) { publicMessage.put("userName", notificationMessage.getSystemUserName()); publicMessage.put("timestamp", notificationMessage.getTimestamp()); publicMessage.put("className", notificationMessage.getClassName()); publicMessage.put("objectId", notificationMessage.getObjectId()); publicMessage.put("objectName", notificationMessage.getObjectName()); publicMessage.put("typeId", "" + notificationMessage.getType()); } publicMessage.put(i + ".userName", notificationMessage.getSystemUserName()); publicMessage.put(i + ".timestamp", notificationMessage.getTimestamp()); publicMessage.put(i + ".className", notificationMessage.getClassName()); publicMessage.put(i + ".objectId", notificationMessage.getObjectId()); publicMessage.put(i + ".objectName", notificationMessage.getObjectName()); publicMessage.put(i + ".typeId", "" + notificationMessage.getType()); i++; } } try { new RemoteCacheUpdater().updateRemoteCaches(internalMessage, publicMessage); } catch (Exception e) { e.printStackTrace(); } } //TEST /** * A factory method that makes sure we operate on a singeton. * We assign a couple of standard listeners like a logger and a transactionHistoryLogger. */ public static ChangeNotificationController getInstance() { if(instance == null) { instance = new ChangeNotificationController(); //instance.registerListener(new FileLogger()); String logTransactions = CmsPropertyHandler.getLogTransactions(); if(logTransactions == null || !logTransactions.equalsIgnoreCase("false")) instance.registerListener(new TransactionHistoryWriter()); String internalSearchEngine = CmsPropertyHandler.getInternalSearchEngine(); if(internalSearchEngine.equalsIgnoreCase("lucene")) instance.registerListener(LuceneController.getController()); //instance.registerListener(new RemoteCacheUpdater()); instance.registerListener(list); //instance.registerListener(new WorkflowEngine()); } return instance; } //-------------------- The object stuff ---------------------// //List of all listeners. private List listeners = new ArrayList(); //List of all listeners that shall be unregistered //(to avoid concurrent modification exceptions, and deadlocks) private List unregisteredlisteners = new ArrayList(); /** * The standard constructor is private to force use of factory-method. */ private ChangeNotificationController() { } /** * This method registers a new listener to be notified when a new notifiation is available. */ public void registerListener(NotificationListener notificationListener) { synchronized (listeners) { this.listeners.add(notificationListener); } } /** * This method unregisters an existing listener. */ public void unregisterListener(NotificationListener notificationListener) { this.unregisteredlisteners.add(notificationListener); } /** * This method gets called when a new notification has come. * It then iterates through the listeners and notifies them. */ public void addNotificationMessage(NotificationMessage notificationMessage) { if(logger.isInfoEnabled()) logger.info("Got a new notification:" + notificationMessage.getName() + ":" + notificationMessage.getType() + ":" + notificationMessage.getObjectId() + ":" + notificationMessage.getObjectName()); synchronized (listeners) { Iterator i = listeners.iterator(); while(i.hasNext()) { try { NotificationListener nl = (NotificationListener)i.next(); if(!unregisteredlisteners.contains(nl)) { if(logger.isInfoEnabled()) logger.info("Notifying the listener:" + nl.getClass().getName()); nl.notify(notificationMessage); } } catch(Exception e) { logger.error("One of the listeners threw an exception but we carry on with the others. Error: " + e.getMessage(), e); } } listeners.removeAll(unregisteredlisteners); } synchronized (unregisteredlisteners) { unregisteredlisteners.clear(); } } }