/* ===============================================================================
*
* 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.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.log4j.Logger;
import org.infoglue.cms.controllers.kernel.impl.simple.TransactionHistoryController;
import org.infoglue.cms.security.InfoGluePrincipal;
import org.infoglue.deliver.util.ioqueue.PublicationQueue;
import org.infoglue.deliver.util.ioqueue.PublicationQueueBean;
/**
* This class is a class that sends a update-message to all registered instances of delivery-engine.
*
* @author Mattias Bogeblad
*
*/
public class RemoteCacheUpdater implements NotificationListener
{
private final static Logger logger = Logger.getLogger(RemoteCacheUpdater.class.getName());
//A list of system changes that will either be published upon the next publication or on a manual publishing of them.
private static List waitingSystemNotificationMessages = new ArrayList();
/**
* Default Constructor
*/
public RemoteCacheUpdater()
{
}
/**
* This method sets the context/arguments the listener should operate with. Could be debuglevels and stuff
* like that.
*/
public void setContextParameters(Map map)
{
}
public static List getSystemNotificationMessages()
{
return waitingSystemNotificationMessages;
}
public static void clearSystemNotificationMessages()
{
synchronized(waitingSystemNotificationMessages)
{
waitingSystemNotificationMessages.clear();
}
}
/**
* This method gets called when a new NotificationMessage is available.
* The writer just calls the transactionHistoryController which stores it.
*/
public void notify(NotificationMessage notificationMessage)
{
try
{
logger.info("Update Remote caches....");
updateRemoteCaches(notificationMessage);
logger.info("Done updating remote caches....");
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
* This method serializes the notificationMessage and calls the remote actions.
* As a content-tool can have several preview instances we iterate through the instances that have
* been indexed in the propertyfile starting with 0.
*/
public void updateRemoteCaches(NotificationMessage notificationMessage) throws Exception
{
throw new Exception("Wrong - this message handler should not be called...");
/*
Hashtable hashedMessage = notificationMessageToHashtable(notificationMessage);
List urls = new ArrayList();
if(notificationMessage.getType() == NotificationMessage.PUBLISHING || notificationMessage.getType() == NotificationMessage.UNPUBLISHING || notificationMessage.getType() == NotificationMessage.SYSTEM)
{
urls.addAll(CmsPropertyHandler.getPublicDeliveryUrls());
if(notificationMessage.getType() == NotificationMessage.SYSTEM)
{
urls.addAll(CmsPropertyHandler.getInternalDeliveryUrls());
}
}
else
{
urls.addAll(CmsPropertyHandler.getInternalDeliveryUrls());
}
Iterator urlsIterator = urls.iterator();
while(urlsIterator.hasNext())
{
String deliverUrl = (String)urlsIterator.next();
String address = deliverUrl + "/" + CmsPropertyHandler.getCacheUpdateAction();
logger.info("Updating cache at " + address);
try
{
String response = postToUrl(address, hashedMessage);
}
catch(Exception e)
{
logger.warn("Error updating cache at " + address + ":" + e.getMessage());
}
}
*/
}
/**
* This method serializes the notificationMessage and calls the remote actions.
* As a content-tool can have several preview instances we iterate through the instances that have
* been indexed in the propertyfile starting with 0.
*/
public void updateRemoteCaches(Hashtable internalMessage, Hashtable publicMessage) throws Exception
{
if(internalMessage != null && internalMessage.size() > 0)
{
List internalUrls = CmsPropertyHandler.getInternalDeliveryUrls();
Iterator urlsIterator = internalUrls.iterator();
while(urlsIterator.hasNext())
{
String deliverUrl = (String)urlsIterator.next();
String address = deliverUrl + "/" + CmsPropertyHandler.getCacheUpdateAction();
logger.info("Updating cache at " + address);
if(address.indexOf("@") > -1)
{
logger.warn("Skipping updating cache at " + address + ". You probably have not defined the correct addresses in application settings.");
}
else
{
try
{
String response = postToUrl(address, internalMessage);
}
catch(Exception e)
{
logger.error("Error updating cache at " + address + ":" + e.getMessage());
}
}
}
}
if(publicMessage != null && publicMessage.size() > 0)
{
List publicUrls = CmsPropertyHandler.getPublicDeliveryUrls();
Iterator urlsIterator = publicUrls.iterator();
while(urlsIterator.hasNext())
{
String deliverUrl = (String)urlsIterator.next();
String address = deliverUrl + "/" + CmsPropertyHandler.getCacheUpdateAction();
logger.info("Updating cache at " + address);
if(address.indexOf("@") > -1)
{
logger.warn("Skipping updating cache at " + address + ". You probably have not defined the correct live addresses in application settings.");
}
else
{
try
{
String response = postToUrl(address, publicMessage);
}
catch(Exception e)
{
NotificationMessage notificationMessage = new NotificationMessage("Publishing notification failed", "Publication", "SYSTEM", NotificationMessage.LIVE_NOTIFICATION_QUEUED, "" + publicMessage.get("0.objectId"), "" + deliverUrl);
TransactionHistoryController.getController().create(notificationMessage);
logger.error("Error updating cache at " + address + ":" + e.getMessage() + ". Adding it to the queue-thread.");
PublicationQueueBean pqb = new PublicationQueueBean();
pqb.setUrlAddress(address);
pqb.setRequestParameters(publicMessage);
pqb.setSerializedParameters(toEncodedString(publicMessage));
PublicationQueue.getPublicationQueue().addPublicationQueueBean(deliverUrl, pqb);
}
}
}
}
}
/**
* This method post information to an URL and returns a string.It throws
* an exception if anything goes wrong.
* (Works like most 'doPost' methods)
*
* @param urlAddress The address of the URL you would like to post to.
* @param inHash The parameters you would like to post to the URL.
* @return The result of the postToUrl method as a string.
* @exception java.lang.Exception
*/
private String postToUrl(String urlAddress, Hashtable inHash) throws Exception
{
URL url = new URL(urlAddress);
URLConnection urlConn = url.openConnection();
urlConn.setConnectTimeout(10000);
urlConn.setReadTimeout(10000);
urlConn.setAllowUserInteraction(false);
urlConn.setDoOutput (true);
urlConn.setDoInput (true);
urlConn.setUseCaches (false);
urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
PrintWriter printout = new PrintWriter(urlConn.getOutputStream(), true);
String argString = "";
if(inHash != null)
{
argString = toEncodedString(inHash);
}
printout.print(argString);
printout.flush();
printout.close ();
InputStream inStream = null;
inStream = urlConn.getInputStream();
InputStreamReader inStreamReader = new InputStreamReader(inStream);
BufferedReader buffer = new BufferedReader(inStreamReader);
StringBuffer strbuf = new StringBuffer();
String line;
while((line = buffer.readLine()) != null)
{
strbuf.append(line);
}
String readData = strbuf.toString();
buffer.close();
return readData;
}
/**
* Encodes a hash table to an URL encoded string.
*
* @param inHash The hash table you would like to encode
* @return A URL encoded string.
*/
private String toEncodedString(Hashtable inHash) throws Exception
{
StringBuffer buffer = new StringBuffer();
Enumeration names = inHash.keys();
while(names.hasMoreElements())
{
String name = names.nextElement().toString();
String value = inHash.get(name).toString();
buffer.append(URLEncoder.encode(name, "UTF-8") + "=" + URLEncoder.encode(value, "UTF-8"));
if(names.hasMoreElements())
{
buffer.append("&");
}
}
return buffer.toString();
}
/**
* As the name indicates, this method takes a URL-encoded string and
* converts it to a normal javastring. That is, all Mime-encoding is
* removed.
*
* @param encoded The encoded string.
* @return An decoded string.
*/
public String decodeHTTPEncodedString(String encoded)
{
StringBuffer decoded = new StringBuffer(encoded.length());
int i = 0;
int j = 0;
while (i < encoded.length()) {
char tecken = encoded.charAt(i);
i++;
if (tecken == '+')
tecken = ' ';
else if (tecken == '%') {
tecken = (char)Integer.parseInt(encoded.substring(i,i+2), 16);
i+=2;
}
decoded.append(tecken);
j++;
}
return new String(decoded);
}
/**
* Converts a serialized hashtable in the url-encoded format to
* a Hashtable that one can use within the program.
* A good technique when exchanging information between different
* applications.
*
* @param encodedstrang The url-encoded string.
* @return A Hashtable.
*/
public Hashtable httpEncodedStringToHashtable(String encodedstrang)
{
Hashtable anropin = new Hashtable();
StringTokenizer andsplitter = new StringTokenizer(encodedstrang,"&");
while (andsplitter.hasMoreTokens())
{
String namevaluepair = andsplitter.nextToken();
StringTokenizer equalsplitter = new StringTokenizer(namevaluepair,"=");
if (equalsplitter.countTokens() == 2)
{
String name = equalsplitter.nextToken();
String value = equalsplitter.nextToken();
anropin.put(decodeHTTPEncodedString(name),decodeHTTPEncodedString(value));
}
}
return anropin;
}
public static synchronized void pushAndClearSystemNotificationMessages(InfoGluePrincipal principal)
{
List localMessages = new ArrayList();
List messages = RemoteCacheUpdater.getSystemNotificationMessages();
synchronized(messages)
{
localMessages.addAll(messages);
messages.clear();
}
Iterator localMessagesIterator = localMessages.iterator();
while(localMessagesIterator.hasNext())
{
NotificationMessage notificationMessage = (NotificationMessage)localMessagesIterator.next();
notificationMessage = new NotificationMessage("PushAndClearSystemNotificationMessages", "" + notificationMessage.getClassName(), principal.getName(), NotificationMessage.SYSTEM, notificationMessage.getObjectId(), notificationMessage.getObjectName());
ChangeNotificationController.getInstance().addNotificationMessage(notificationMessage);
}
}
public static synchronized void pushAndClearSystemNotificationMessages(String userName)
{
List localMessages = new ArrayList();
List messages = RemoteCacheUpdater.getSystemNotificationMessages();
logger.info("messages:" + messages.size());
synchronized(messages)
{
localMessages.addAll(messages);
messages.clear();
}
Iterator localMessagesIterator = localMessages.iterator();
while(localMessagesIterator.hasNext())
{
NotificationMessage notificationMessage = (NotificationMessage)localMessagesIterator.next();
notificationMessage = new NotificationMessage("PushAndClearSystemNotificationMessages", "" + notificationMessage.getClassName(), userName, NotificationMessage.SYSTEM, notificationMessage.getObjectId(), notificationMessage.getObjectName());
logger.info("notificationMessage:" + notificationMessage);
ChangeNotificationController.getInstance().addNotificationMessage(notificationMessage);
}
}
public void process() throws Exception
{
logger.info("Process inside RemoteCacheUpdater");
}
}