Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*******************************************************************************
* Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* s IT Solutions AT Spardat GmbH - initial API and implementation
*******************************************************************************/
/*
* @(#) $Id: GlobalEventManager.java 3088 2009-01-30 16:10:02Z gub $
*
*
*
*
*
*/
package at.spardat.xma.event.global;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import at.spardat.enterprise.exc.SysException;
import at.spardat.properties.XProperties;
import at.spardat.xma.session.XMASessionServer;
/**
* This singleton class serves for creating, sending and polling GlobalEvents.
* To send an event means to store it for this server in an GlobalEventmanager internal Map,
* for the accesss of other servers the events are also stored in an JNDI tree.
* Polling means to ask the map and the JNDI tree for undelivered events and to return
* a collection with them.
* GlobalEventListener for the server side GlobalEvents have to be registerd at this class (addGlobalEventListener()).
* GlobalEventListener for the client side GlobalEvents have to be registerd at the XMASessionClient (addGlobalEventListener()).
*
* @author s3460
* @since version_number
*/
public class GlobalEventManager {
private static final String CTX_EVENTS = "at.spardat.xma.globalevents";
private final String CTX_EVENTS_SCOPE;
private static final String COUNT = "count";
private static final long POLLING_INTERVAL_MILLIS = 5000;
private static final String ACTIVATE_GLOBAL_EVENTS = "activateGlobalEvents";
private static GlobalEventManager instance;
//stores the GlobalEventListeners
private List globalEventListeners;
//high count of events sent by this GlobalEventManager
private int highCount = 0;
/**
* server name of this GlobalEventManager instance
*/
private String _serverName;
private Context initCtx; //used to look up bindings
private Context serverCtx; //used to bind new events
/**
* serverName/highCounts
* stores the last highCoubnst received - used for pollServerSideEvents()
*/
private Map myServerHighCounts = new HashMap();
/**
* mirrors the JNDI tree
* key: servername_eventcount - value: GlobalEvent
* the events sent by this GlobalEventManager are stored imidiately in this map (for Tomcat)
*/
private Map eventMap = new HashMap();
/**
* mirrors the servers and their highCounts in the JNDI tree
* servername/highCounts
*/
private Map serverMap = new HashMap();
/**
*
*/
private long lastPollingTimeMillis = 0;
/**
* Returns a singleton GlobalEventManager object.
* Can only be called if xma.runtime.activateGlobalEvents!="false" ("false" is default).
* @return @since version_number
* @author s3460
* @exception SysException - if xma.runtime.activateGlobalEvents is set to "false"
*/
public static GlobalEventManager getInstance() {
if (instance == null) {
if(isGlobalEventActivated()){
instance = new GlobalEventManager();
}else{
throw new SysException("GlobalEventManager is used but 'xma.runtime.activateGlobalEvents'is set to 'false'");
}
}
return instance;
}
/**
* singleton Constructor
*/
private GlobalEventManager() {
super();
this.CTX_EVENTS_SCOPE = CTX_EVENTS + "." + getScopeName();
createServerName();
createInitContext();
// Creates the CTX_EVENTS it it does not exits:
createGlobalEventCtx();
}
/**
*
* @since version_number
* @author s3460
*/
private void createServerName() {
try {
//serverName ermitteln by random
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
_serverName = String.valueOf(random.nextInt(Integer.MAX_VALUE));
} catch (NoSuchAlgorithmException e) {
throw new SysException(e);
}
}
/**
* Creates the CTX_EVENTS it it does not exits.
* Creates a context for the server.
*
* @since version_number
* @author s3460
*/
private void createInitContext() {
try {
initCtx = new InitialContext();
} catch (NamingException e) {
throw new SysException(e);
}
}
/**
* creates subcontext for this server
*
* @since version_number
* @author s3460
*/
private void createServerContext(){
try {
serverCtx = initCtx.createSubcontext(CTX_EVENTS_SCOPE + "." + _serverName);
//bind initial highCount
serverCtx.bind(COUNT, new Integer(0));
//store initial highCount in map
serverMap.put(_serverName, new Integer(0));
} catch (NamingException e) {
throw new SysException(e);
}
}
/**
* Creates the CTX_EVENTS it it does not exits.
* @throws NamingException
* @since version_number
* @author s3460
*/
private void createGlobalEventCtx(){
try {
try {
initCtx.lookup("at");
} catch (NamingException e1) {
//CTX does not exist
initCtx.createSubcontext("at");
}
try {
initCtx.lookup("at.spardat");
} catch (NamingException e1) {
//CTX does not exist
initCtx.createSubcontext("at.spardat");
}
try {
initCtx.lookup("at.spardat.xma");
} catch (NamingException e1) {
//CTX does not exist
initCtx.createSubcontext("at.spardat.xma");
}
try {
initCtx.lookup(CTX_EVENTS);
} catch (NamingException e1) {
//CTX does not exist
initCtx.createSubcontext(CTX_EVENTS);
}
try {
initCtx.lookup(CTX_EVENTS_SCOPE);
} catch (NamingException e1) {
//CTX does not exist
initCtx.createSubcontext(CTX_EVENTS_SCOPE);
}
} catch (NamingException e) {
throw new SysException(e);
}
}
/**
* @return expiration in millisec: now + 1 day
* @since version_number
* @author s3460
*/
private long getDefaultExpiration() {
//TODO default OK ?
return System.currentTimeMillis() + 60 * 60 * 24 * 1000;
}
/**
* The added GlobalEventListener will be notifyed by server events (recipient = RECIPIENT_ALL_SERVERS).
*
* @param listener
* @since version_number
* @author s3460
*/
public void addGlobalEventListener(GlobalEventListener listener) {
if (globalEventListeners == null){
globalEventListeners = new ArrayList(1);
}
if (!globalEventListeners.contains(listener)) {
globalEventListeners.add(listener);
}
}
/**
* removes the GlobalEventListener.
* @param listener
* @return true: if a listener was removed, false otherwise.
* @since version_number
* @author s3460
*/
public boolean removeGlobalEventListener(GlobalEventListener listener) {
boolean removed = false;
if (globalEventListeners != null) {
removed = globalEventListeners.remove(listener);
if (globalEventListeners.size() == 0)
globalEventListeners = null;
}
return removed;
}
/**
* creates a GlobalEvent with the given name, the default recipient type
* (RECIPIENT_ALL_CLIENTS_ALL_SERVERS) and the default expiration date (now + 1 day).
* @param name type of GlobalEvent
* @return a new created GlobalEvent
* @since version_number
* @author s3460
*/
public GlobalEvent createGlobalEvent(String name) {
return createGlobalEvent(name, GlobalEvent.RECIPIENT_ALL_CLIENTS_ALL_SERVERS, getDefaultExpiration());
}
/**
* creates a GlobalEvent with the given name, recipient type and the default expiration date (now + 1 day).
* @param name type of GlobalEvent
* @param recipient type of recipient: RECIPIENT_ALL_SERVERS, RECIPIENT_ALL_CLIENTS, RECIPIENT_ALL_CLIENTS_ALL_SERVERS
* @return a new created GlobalEvent
* @since version_number
* @author s3460
*/
public GlobalEvent createGlobalEvent(String name, int recipient) {
return createGlobalEvent(name, recipient, getDefaultExpiration());
}
/**
* creates a GlobalEvent with the given name, recipient type and the given expiration date.
* @param name type of GlobalEvent
* @param recipient type of recipient: RECIPIENT_ALL_SERVERS, RECIPIENT_ALL_CLIENTS, RECIPIENT_ALL_CLIENTS_ALL_SERVERS
* @param expiresMilliSec milli seconds after 1970
* @return a new created GlobalEvent
* @since version_number
* @author s3460
*/
public GlobalEvent createGlobalEvent(String name, int recipient, long expiresMilliSec) {
return createGlobalEvent(name, recipient, expiresMilliSec,null);
}
/**
* creates a GlobalEvent with the given name, recipient type, the given expiration date and the given session id.
*
* The session ID should be given if the creator of the event is the only recipient.
* So when the session ID is stated then as recipients only GlobalEvent.RECIPIENT_BY_ID (only the listeners of the client with this session ID are called)
* or RECIPIENT_ALL_SERVERS (only the listeners of all servers and the listeners of the client with this ID are called) make sense.
* (The session ID it will be evaluated by the GlobalEvent.isEventForClient(String idSession) method.)
* @param name type of GlobalEvent
* @param recipient type of recipient: RECIPIENT_ALL_SERVERS, RECIPIENT_ALL_CLIENTS, RECIPIENT_ALL_CLIENTS_ALL_SERVERS
* @param expiresMilliSec milli seconds after 1970
* @param sessionId - usually session id of creator.
* @return a new created GlobalEvent
* @since version_number
* @author s3460
*/
public GlobalEvent createGlobalEvent(String name, int recipient, long expiresMilliSec, String sessionId) {
return new GlobalEvent(name, recipient, _serverName, expiresMilliSec, sessionId);
}
/**
* creates a GlobalEvent with the given name, the default recipient type
* (RECIPIENT_ALL_CLIENTS_ALL_SERVERS) and the time from now how long this event is valid.
*
* @param name type of GlobalEvent
* @param timeToLiveMilliSec -
* milli seconds (from now) in which this event expires.
* @return a new created GlobalEvent
* @since version_number
* @author s3460
*/
public GlobalEvent createGlobalEventTTL(String name, long timeToLiveMilliSec) {
return createGlobalEventTTL(name,GlobalEvent.RECIPIENT_ALL_CLIENTS_ALL_SERVERS, timeToLiveMilliSec);
}
/**
* creates a GlobalEvent with the given name, the given recipient type
* and the time from now how long this event is valid.
*
* @param name type of GlobalEvent
* @param recipient type of recipient: RECIPIENT_ALL_SERVERS, RECIPIENT_ALL_CLIENTS, RECIPIENT_ALL_CLIENTS_ALL_SERVERS
* @param timeToLiveMilliSec milli seconds (from now) in which this event expires.
* @return a new created GlobalEvent
* @since version_number
* @author s3460
*/
public GlobalEvent createGlobalEventTTL(String name, int recipient, long timeToLiveMilliSec) {
return createGlobalEventTTL(name,recipient, timeToLiveMilliSec, null);
}
/**
* creates a GlobalEvent with the given name, the given recipient type
* and the time from now how long this event is valid.
*
* The session ID should be given if the creator of the event is the only recipient.
* So when the session ID is stated then as recipients only GlobalEvent.RECIPIENT_BY_ID (only the listeners of the client with this session ID are called)
* or RECIPIENT_ALL_SERVERS (only the listeners of all servers and the listeners of the client with this ID are called) make sense.
* (The session ID it will be evaluated by the GlobalEvent.isEventForClient(String idSession) method.)
*
* @param name type of GlobalEvent
* @param recipient type of recipient: RECIPIENT_ALL_SERVERS, RECIPIENT_ALL_CLIENTS, RECIPIENT_ALL_CLIENTS_ALL_SERVERS
* @param timeToLiveMilliSec milli seconds (from now) in which this event expires.
* @param sessionId - usually session id of creator.
* @return a new created GlobalEvent
* @since version_number
* @author s3460
*/
public GlobalEvent createGlobalEventTTL(String name, int recipient, long timeToLiveMilliSec, String sessionId) {
long expire = System.currentTimeMillis() + timeToLiveMilliSec;
return createGlobalEvent(name,recipient, expire, sessionId);
}
/**
* Sends this given event to the global data structure.
* The event is then stored an can be retrieved by pollEvents().
* @param event
* @since version_number
* @author s3460
* @throws RuntimeException if XProperty 'at.spardat.xma.activateGlobalEvents=true' is not set.
*/
public synchronized void send(GlobalEvent event) {
if(!isGlobalEventActivated()){
throw new RuntimeException("Cant send a GlobalEvent as XProperty 'at.spardat.xma.activateGlobalEvents=true' is not set.");
}
if(highCount == 0){//first call to send()
createServerContext();
}
event.setCount(highCount + 1);
try {
Integer count = new Integer(event.getCount());
serverCtx.rebind(COUNT, count);
//bind event object as map:
serverCtx.bind(String.valueOf(event.getCount()), event.toMap());
//reassign highCount in serverMap (imidiately for Tomcat)
serverMap.put(_serverName,count);
//store new event imidiately in eventMap (for Tomcat)
eventMap.put(_serverName + "_" + event.getCount(), event);
highCount++;
} catch (NamingException e) {
throw new SysException(e);
}
}
/**
* Looks up the new highCount and the new events between oldCount and new
* highCount. Returns the new highCount.
*
* @param serverName
* @param oldCount
* @param globalEvents -
* Collection of GlobalEvent with objects bound between oldCount
* and jndi highCount
* @return new HighCount or null if server "serverName" exists not anymore
* @since version_number
* @author s3460
*/
private Integer pollFromEventMap(String serverName, int oldCount, Collection globalEvents) {
// lookup new highCount
Integer newCount = (Integer) serverMap.get(serverName);
if(newCount == null){
// server exists not any more
return null;
}
// lookup new events
for (int i = oldCount + 1; i <= newCount.intValue(); i++) {
GlobalEvent event = (GlobalEvent) eventMap.get(serverName + "_" + i);
if(event != null){
globalEvents.add(event);
}
}
return newCount;
}
/**
* Returns a Collection of GlobalEvents with objects bound between oldCount (stored in map)
* and the new highCount of each server (stored in serverMap).
* The param map is updated to the new highCounts and to new servers.
* Usually the parameter map is stored in a session.
*
* This call only accesses serverMap and eventMap (not JNDI).
*
* @param map -
* serverName/highCount (String/Integer).
* @return Collection of GlobalEvents
* @since version_number
* @author s3460
*/
private synchronized Collection pollFromEventMap(Map map) {
Collection coll = new ArrayList();
pollNewSevers(map);
//Iterates over server and polls the events of each server.
for (Iterator iter = map.keySet().iterator(); iter.hasNext();) {
String server = (String) iter.next();
Integer i = (Integer) map.get(server);
i = pollFromEventMap(server, i.intValue(), coll); //returns new highCount
if(i != null){
map.put(server, i); //reassign new highCount
}else{
iter.remove(); //server exists not any more
}
}
return coll;
}
/**
* Returns a Collection of GlobalEvents for the client.
* Server side events are transmitted to the registered server side GlobalEventListener.
* Only events are returned which were not already transmitted to the client or the server.
*
* @return a Collection of GlobalEvents.
* @since version_number
* @author s3460
*/
public Collection pollEvents(){
//tests if last polling befor this one was more than POLLING_INTERVAL_MILLIS ago.
if(pollingIntervalElapsed()){
refreshFromJNDI();
}
//poll events for this server and transmitt events to server side listener
pollServerSideEventsInternal();
//poll events for client (of actual session)
Collection coll = pollFromEventMap(
((XMASessionServer) XMASessionServer
.getXMASession()).getServerHighCounts());
//remove events which are not for clients and not for this client
for (Iterator iter = coll.iterator(); iter.hasNext();) {
GlobalEvent event = (GlobalEvent) iter.next();
if(!event.isEventForClient() && !event.isEventForClient(((XMASessionServer) XMASessionServer
.getXMASession()).getHttpSession().getId())){
iter.remove();
}
}
//return null if coll is empty
if(coll.size()>0){
return coll;
}
else{
return null;
}
}
/**
* removes expired events from eventMap.
* @param map
* @since version_number
* @author s3460
*/
private void cleanUpEventMap(){
for (Iterator iter = this.eventMap.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
GlobalEvent event = (GlobalEvent) eventMap.get(key);
if(event.getExpiresMilliSec() <= System.currentTimeMillis()){
iter.remove();
}
}
}
/**
* removes expired events from JNDI context.
* @param ctx
* @since version_number
* @author s3460
*/
private void cleanUpSubCtx(Context ctx){
try {
for (NamingEnumeration iter = ctx.listBindings(""); iter.hasMore();) {
Binding element = (Binding) iter.next();
Object obj = element.getObject();
//GlobalEvents are stored as map in JNDI tree.
if (obj instanceof Map) {
GlobalEvent event = new GlobalEvent((Map)obj);
if((event).getExpiresMilliSec() <= System.currentTimeMillis()){
ctx.unbind(element.getName());
}
}
}
} catch (NamingException e) {
// TODO logging
e.printStackTrace();
}
}
/**
* Iterates over serverMap and adds new servers to map.
* The highcount for the new server in map is set to zero,
* because the client has got not event of the new server yet.
*
* @param map -
* serverName/highCount (String/Integer)
* @since version_number
* @author s3460
*/
private void pollNewSevers(Map map) {
for (Iterator iter = serverMap.keySet().iterator(); iter.hasNext();) {
String server = (String) iter.next();
if(!map.containsKey(server)){
map.put(server, new Integer(0));
}
}
}
/**
* Polls for new GlobalEvents with myServerHighCounts and iterates over
* the registered sever side GlobalEventsListers.
*
* Usually this method is implicitly called in pollEvents().
* But pollEvents() itself is only called if an XMA remote call is answered by the server.
* To poll for server side events independently from XMA remote calls this method can be used.
*
* @since version_number
* @author s3460
*/
public void pollServerSideEvents(){
// tests if last polling befor this one was more than POLLING_INTERVAL_MILLIS ago.
if(pollingIntervalElapsed()){
refreshFromJNDI();
}
//polls new events for the server and iterates over server side listener
pollServerSideEventsInternal();
}
/**
* Polls for new GlobalEvents with myServerHighCounts and iterates over
* the registered sever side GlobalEventsListers.
*
* @since version_number
* @author s3460
*/
private void pollServerSideEventsInternal(){
Collection coll = pollFromEventMap(this.myServerHighCounts);
for (Iterator iter = coll.iterator(); iter.hasNext();) {
GlobalEvent event = (GlobalEvent) iter.next();
//only events for server are transmited to listener
if(event.isEventForServer()){
iterateListener(event);
}
}
}
/**
* iterates over the registered GlobalEventsListers and calls globalEvent().
* @param event
* @since version_number
* @author s3460
*/
private void iterateListener(GlobalEvent event){
if (globalEventListeners != null) {
for (Iterator iter = globalEventListeners.iterator(); iter.hasNext();) {
GlobalEventListener element = (GlobalEventListener) iter.next();
try {
element.globalEvent(event);
} catch (Exception e) {
// TODO logging
e.printStackTrace();
}
}
}
}
/**
* tests if last polling befor this one was more than POLLING_INTERVAL_MILLIS ago.
* @return
* @since version_number
* @author s3460
*/
private synchronized boolean pollingIntervalElapsed(){
if(lastPollingTimeMillis + POLLING_INTERVAL_MILLIS < System.currentTimeMillis()){
lastPollingTimeMillis = System.currentTimeMillis();
return true;
}else{
return false;
}
}
/**
* Iterates over bindings of CTX_EVENTS (= iterates over sever contexts).
* New contexts are added to serverMap.
* Expireds events (bound to a server ctx) are removed.
*
* @since version_number
* @author s3460
*/
private void refreshServerMapAndCleanJNDI(){
//add the other servers bound by JNDI
try {
for (NamingEnumeration iter = initCtx.listBindings(CTX_EVENTS_SCOPE); iter.hasMore();) {
Binding element = (Binding) iter.next();
Object obj = element.getObject();
if(obj instanceof Context) {
cleanUpSubCtx((Context)obj);//clean up expired events from this ctx
if (!serverMap.containsKey(element.getName())) {//add new server
serverMap.put(element.getName(), new Integer(0));
}
}
}
} catch (NamingException e) {
// TODO logging
//throw new SysException(e);
}
}
/**
* Iterates over serverMap and looks up the events of each server.
* New events are added to eventMap.
* New highCounts are reassigned to serverMap.
* Non existing servers are removed from serverMap.
*
* @since version_number
* @author s3460
*/
private void refreshEventMapAndHighCounts(){
// Iterates over serverMap and polls the events of each server.
for (Iterator iter = serverMap.keySet().iterator(); iter.hasNext();) {
String server = (String) iter.next();
Integer i = (Integer) serverMap.get(server);
i = refreshEventMap(server, i.intValue()); //returns new highCount
if(i != null){
serverMap.put(server, i); //reassign new highCount
}else{
iter.remove(); //server exists not any more
}
}
}
/**
* Looks up the events of server serverName from oldCount + 1 to newHighCount
* (which is saved in JNDI).
* New events are stored in eventMap, the new highCount is returned.
*
* @param serverName
* @param oldCount
* @return new highCount for server serverName
* @since version_number
* @author s3460
*/
private Integer refreshEventMap(String serverName, int oldCount) {
//own events are already added (in send())
if(_serverName.equalsIgnoreCase(serverName)){
return new Integer(highCount);
}else{//lookup events of other servers by JNDI
//lookup new highCount
Integer newCount = null;
try {
newCount = (Integer) initCtx.lookup(CTX_EVENTS_SCOPE + "." + serverName + "."
+ COUNT);
} catch (NamingException e) {
// server exists not any more
return null;
//throw new SysException(e);
}
//lookup new events
for (int i = oldCount + 1; i <= newCount.intValue(); i++) {
try {
Object jndiObj = initCtx.lookup(CTX_EVENTS_SCOPE + "."
+ serverName + "." + String.valueOf(i));
if(jndiObj instanceof Map){
GlobalEvent event = new GlobalEvent((Map) jndiObj);
// store new events in eventMap
eventMap.put(serverName + "_" + event.getCount(),event);
}
} catch (NamingException e) {
// event can already be removed if expired
//TODO log;
//e.printStackTrace();
}
}
return newCount;
}
}
/**
* Refreshes eventMap and serverMap with the actual structure stored in JNDI.
* Removes non existing servers from serverMap.
* Removes expired events from eventMap.
* @since version_number
* @author s3460
*/
private synchronized void refreshFromJNDI(){
refreshServerMapAndCleanJNDI();
refreshEventMapAndHighCounts();
cleanUpEventMap();
}
/**
*
* @return the name set in 'xma.runtime.activateGlobalEvents'
* @since version_number
* @author S3460
* @throws SysException if 'xma.runtime.activateGlobalEvents' is set to false (default).
*/
private static String getScopeName(){
XProperties props = XProperties.getNodeOfPackage("xma.runtime");
return props.get(ACTIVATE_GLOBAL_EVENTS, "false");
}
/**
* Queries for the property: at.spardat.xma.activateGlobalEvents=true
* Is called after every RPC before GlobalEventManager.getInstance().pollEvents()
* to ommit unneccesary JNDI queries.
* Should also be called before every GlobalEventManager.getInstance().pollServerSideEvents().
* @return true if at.spardat.xma.activateGlobalEvents != "false" is set.
* @since version_number
* @author s3460
*/
public static boolean isGlobalEventActivated(){
return !"false".equals(getScopeName());
}
}