package xdi2.transport.registry.impl.uri;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import xdi2.core.syntax.XDIArc;
import xdi2.messaging.container.MessagingContainer;
import xdi2.messaging.container.exceptions.Xdi2MessagingException;
import xdi2.messaging.container.factory.impl.uri.UriMessagingContainerFactory;
import xdi2.transport.exceptions.Xdi2TransportException;
import xdi2.transport.registry.MessagingContainerMount;
import xdi2.transport.registry.MessagingContainerRegistry;
import xdi2.transport.registry.impl.AbstractMessagingContainerRegistry;
/**
* Registry to mount and unmount messaging containers.
*
* @author markus
*/
public class UriMessagingContainerRegistry extends AbstractMessagingContainerRegistry implements MessagingContainerRegistry, ApplicationContextAware {
private static final Logger log = LoggerFactory.getLogger(UriMessagingContainerRegistry.class);
public static final boolean DEFAULT_CHECKDISABLED = true;
public static final boolean DEFAULT_CHECKEXPIRED = true;
private Map<String, UriMessagingContainerMount> messagingContainerMounts;
private Map<String, UriMessagingContainerFactoryMount> messagingContainerFactoryMounts;
private ApplicationContext applicationContext;
private boolean checkDisabled;
private boolean checkExpired;
public UriMessagingContainerRegistry() {
this.messagingContainerMounts = new HashMap<String, UriMessagingContainerMount> ();
this.messagingContainerFactoryMounts = new HashMap<String, UriMessagingContainerFactoryMount> ();
this.applicationContext = null;
this.checkDisabled = DEFAULT_CHECKDISABLED;
this.checkExpired = DEFAULT_CHECKEXPIRED;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (log.isDebugEnabled()) log.debug("Setting application context.");
this.applicationContext = applicationContext;
}
public synchronized void init() throws Xdi2TransportException {
// no application context?
if (this.applicationContext == null) {
log.info("No application context. No messaging containers loaded.");
return;
}
// look up and mount all messaging containers
log.info("Mounting messaging containers...");
Map<String, MessagingContainer> messagingContainers = this.applicationContext.getBeansOfType(MessagingContainer.class);
for (Map.Entry<String, MessagingContainer> entry : messagingContainers.entrySet()) {
String path = entry.getKey();
MessagingContainer messagingContainer = entry.getValue();
if (! path.startsWith("/")) continue;
this.mountMessagingContainer(path, messagingContainer);
}
// look up and mount all messaging container factories
log.info("Mounting messaging container factories...");
Map<String, UriMessagingContainerFactory> messagingContainerFactorys = this.applicationContext.getBeansOfType(UriMessagingContainerFactory.class);
for (Map.Entry<String, UriMessagingContainerFactory> entry : messagingContainerFactorys.entrySet()) {
String path = entry.getKey();
UriMessagingContainerFactory messagingContainerFactory = entry.getValue();
if (! path.startsWith("/")) continue;
if (! path.endsWith("/*")) continue;
this.mountMessagingContainerFactory(path, messagingContainerFactory);
}
// done
log.info("Done. " + this.messagingContainerMounts.size() + " messaging containers and " + this.messagingContainerFactoryMounts.size() + " messaging container factories mounted.");
}
public synchronized void shutdown() {
int size = this.messagingContainerMounts.size();
// unmount all our messaging containers
List<UriMessagingContainerMount> tempList = this.getMessagingContainerMounts();
for (MessagingContainerMount messagingContainerMount : tempList) {
this.unmountMessagingContainer(messagingContainerMount.getMessagingContainer());
}
tempList.clear();
// done
log.info("Done. " + size + " messaging containers were shut down.");
}
/*
* Mounting and unmounting
*/
/**
* Mount a messaging container in the registry.
* @param messagingContainer The messaging container to mount.
*/
public synchronized MessagingContainerMount mountMessagingContainer(String messagingContainerPath, MessagingContainer messagingContainer) throws Xdi2TransportException {
if (messagingContainerPath == null) throw new NullPointerException("Cannot mount a messaging container without path.");
if (log.isDebugEnabled()) log.debug("Mounting messaging container " + messagingContainer.getClass().getSimpleName() + " at path " + messagingContainerPath);
// already mounted?
if (this.messagingContainerMounts.containsKey(messagingContainerPath)) {
throw new Xdi2TransportException("Messaging target " + this.messagingContainerMounts.get(messagingContainerPath).getMessagingContainer().getClass().getCanonicalName() + " already mounted at path " + messagingContainerPath + ".");
}
// init messaging container
try {
messagingContainer.init();
} catch (Exception ex) {
log.warn("Exception while initializing messaging container " + messagingContainer.getClass().getCanonicalName() + ": " + ex.getMessage(), ex);
throw new Xdi2TransportException("Exception while initializing messaging container " + messagingContainer.getClass().getCanonicalName() + ": " + ex.getMessage(), ex);
}
// mount messaging container
while (messagingContainerPath.startsWith("/")) messagingContainerPath = messagingContainerPath.substring(1);
messagingContainerPath = "/" + messagingContainerPath;
UriMessagingContainerMount messagingContainerMount = new UriMessagingContainerMount(messagingContainerPath, messagingContainer);
this.messagingContainerMounts.put(messagingContainerPath, messagingContainerMount);
// done
log.info("Messaging target " + messagingContainer.getClass().getCanonicalName() + " mounted at path " + messagingContainerPath + ".");
return messagingContainerMount;
}
/**
* Mount a messaging container factory in the registry.
* @param messagingContainerFactory The messaging container factory to mount.
*/
public synchronized UriMessagingContainerFactoryMount mountMessagingContainerFactory(String messagingContainerFactoryPath, UriMessagingContainerFactory messagingContainerFactory) throws Xdi2TransportException {
if (messagingContainerFactoryPath == null) throw new NullPointerException("Cannot mount a messaging container factory without path.");
if (log.isDebugEnabled()) log.debug("Mounting messaging container factory " + messagingContainerFactory.getClass().getSimpleName() + " at path " + messagingContainerFactoryPath);
// already mounted?
if (this.messagingContainerFactoryMounts.containsKey(messagingContainerFactoryPath)) {
throw new Xdi2TransportException("Messaging target factory " + this.messagingContainerFactoryMounts.get(messagingContainerFactoryPath).getClass().getCanonicalName() + " already mounted at path " + messagingContainerFactoryPath + ".");
}
// mount messaging container factory
while (messagingContainerFactoryPath.startsWith("/")) messagingContainerFactoryPath = messagingContainerFactoryPath.substring(1);
if (messagingContainerFactoryPath.endsWith("/*")) messagingContainerFactoryPath = messagingContainerFactoryPath.substring(0, messagingContainerFactoryPath.length() - 2);
messagingContainerFactoryPath = "/" + messagingContainerFactoryPath;
UriMessagingContainerFactoryMount messagingContainerFactoryMount = new UriMessagingContainerFactoryMount(messagingContainerFactoryPath, messagingContainerFactory);
this.messagingContainerFactoryMounts.put(messagingContainerFactoryPath, messagingContainerFactoryMount);
// init messaging container factory
try {
messagingContainerFactory.init();
} catch (Exception ex) {
log.warn("Exception while initializing messaging container factory " + messagingContainerFactory.getClass().getCanonicalName() + ": " + ex.getMessage(), ex);
throw new Xdi2TransportException("Exception while initializing messaging container factory " + messagingContainerFactory.getClass().getCanonicalName() + ": " + ex.getMessage(), ex);
}
// done
log.info("Messaging target factory " + messagingContainerFactory.getClass().getCanonicalName() + " mounted at path " + messagingContainerFactoryPath + ".");
return messagingContainerFactoryMount;
}
/**
* Unmounts a messaging container from the registry.
* @param messagingContainer The messaging container to unmount.
*/
public synchronized void unmountMessagingContainer(MessagingContainer messagingContainer) {
if (log.isDebugEnabled()) log.debug("Unmounting messaging container " + messagingContainer.getClass().getSimpleName());
// shutdown messaging container
try {
messagingContainer.shutdown();
} catch (Exception ex) {
log.warn("Exception while shutting down messaging container " + messagingContainer.getClass().getCanonicalName() + ": " + ex.getMessage(), ex);
}
// unmount messaging container
for (Iterator<Entry<String, UriMessagingContainerMount>> messagingContainerMounts = this.messagingContainerMounts.entrySet().iterator(); messagingContainerMounts.hasNext(); ) {
UriMessagingContainerMount messagingContainerMount = messagingContainerMounts.next().getValue();
if (messagingContainerMount.getMessagingContainer() == messagingContainer) messagingContainerMounts.remove();
}
// done
log.info("Messaging target " + messagingContainer.getClass().getCanonicalName() + " unmounted.");
}
/**
* Unmounts a messaging container factory from the registry.
* @param messagingContainerFactory The messaging container factory to unmount.
*/
public synchronized void unmountMessagingContainerFactory(UriMessagingContainerFactory messagingContainerFactory) {
if (log.isDebugEnabled()) log.debug("Unmounting messaging container factory " + messagingContainerFactory.getClass().getSimpleName());
// shutdown messaging container factory
try {
messagingContainerFactory.shutdown();
} catch (Exception ex) {
log.warn("Exception while shutting down messaging container factory " + messagingContainerFactory.getClass().getCanonicalName() + ": " + ex.getMessage(), ex);
}
// unmount messaging container factory
for (Iterator<Entry<String, UriMessagingContainerFactoryMount>> messagingContainerFactoryMounts = this.messagingContainerFactoryMounts.entrySet().iterator(); messagingContainerFactoryMounts.hasNext(); ) {
UriMessagingContainerFactoryMount messagingContainerFactoryMount = messagingContainerFactoryMounts.next().getValue();
if (messagingContainerFactoryMount.getMessagingContainerFactory() == messagingContainerFactory) messagingContainerFactoryMounts.remove();
}
// done
log.info("Messaging target factory " + messagingContainerFactory.getClass().getCanonicalName() + " unmounted.");
}
/*
* Lookup
*/
public synchronized UriMessagingContainerMount lookup(String requestPath) throws Xdi2TransportException, Xdi2MessagingException {
if (log.isDebugEnabled()) log.debug("Looking up messaging container for request path " + requestPath);
// look at messaging containers
String messagingContainerPath = this.findMessagingContainerPath(requestPath);
MessagingContainer messagingContainer = messagingContainerPath == null ? null : this.getMessagingContainer(messagingContainerPath);
if (log.isDebugEnabled()) log.debug("messagingContainerPath=" + messagingContainerPath + ", messagingContainer=" + (messagingContainer == null ? null : messagingContainer.getClass().getSimpleName()));
// look at messaging container factorys
String messagingContainerFactoryPath = this.findMessagingContainerFactoryPath(requestPath);
UriMessagingContainerFactory messagingContainerFactory = messagingContainerFactoryPath == null ? null : this.getMessagingContainerFactory(messagingContainerFactoryPath);
if (log.isDebugEnabled()) log.debug("messagingContainerFactoryPath=" + messagingContainerFactoryPath + ", messagingContainerFactory=" + (messagingContainerFactory == null ? null : messagingContainerFactory.getClass().getSimpleName()));
// what did we find?
if (messagingContainerFactory != null) {
if (messagingContainer == null) {
// if we don't have a messaging container, see if the messaging container factory can create one
messagingContainerFactory.mountMessagingContainer(this, messagingContainerFactoryPath, requestPath, this.isCheckDisabled(), this.isCheckExpired());
} else {
// if we do have a messaging container, see if the messaging container factory wants to modify or remove it
messagingContainerFactory.updateMessagingContainer(this, messagingContainerFactoryPath, requestPath, this.isCheckDisabled(), this.isCheckExpired(), messagingContainer);
}
// after the messaging container factory did its work, look for the messaging container again
messagingContainerPath = this.findMessagingContainerPath(requestPath);
messagingContainer = messagingContainerPath == null ? null : this.getMessagingContainer(messagingContainerPath);
if (log.isDebugEnabled()) log.debug("messagingContainerPath=" + messagingContainerPath + ", messagingContainer=" + (messagingContainer == null ? null : messagingContainer.getClass().getSimpleName()));
}
// done
return new UriMessagingContainerMount(messagingContainerPath, messagingContainer);
}
@Override
public synchronized UriMessagingContainerMount lookup(XDIArc ownerPeerRootXDIArc) throws Xdi2TransportException, Xdi2MessagingException {
if (log.isDebugEnabled()) log.debug("Looking up messaging container for owner peer root " + ownerPeerRootXDIArc);
// look at messaging containers
for (UriMessagingContainerMount messagingContainerMount : this.getMessagingContainerMounts()) {
String requestPath = messagingContainerMount.getMessagingContainerPath();
if (! ownerPeerRootXDIArc.equals(messagingContainerMount.getMessagingContainer().getOwnerPeerRootXDIArc())) continue;
return this.lookup(requestPath);
}
// look at messaging container factorys
for (UriMessagingContainerFactoryMount messagingContainerFactoryMount : this.getMessagingContainerFactoryMounts()) {
String requestPath = messagingContainerFactoryMount.getMessagingContainerFactory().getRequestPath(messagingContainerFactoryMount.getMessagingContainerFactoryPath(), ownerPeerRootXDIArc);
if (requestPath == null) continue;
return this.lookup(requestPath);
}
// done
return null;
}
/*
* MessagingContainers
*/
@Override
public synchronized List<UriMessagingContainerMount> getMessagingContainerMounts() {
return new ArrayList<UriMessagingContainerMount> (this.messagingContainerMounts.values());
}
@Override
public synchronized int getNumMessagingContainers() {
return this.messagingContainerMounts.size();
}
public synchronized String findMessagingContainerPath(String requestPath) {
if (! requestPath.startsWith("/")) requestPath = "/" + requestPath;
if (log.isDebugEnabled()) log.debug("Finding messaging container for path: " + requestPath);
String longestMessagingContainerPath = null;
for (Map.Entry<String, UriMessagingContainerMount> messagingContainerMount : this.messagingContainerMounts.entrySet()) {
if (requestPath.startsWith(messagingContainerMount.getKey()) && (longestMessagingContainerPath == null || messagingContainerMount.getKey().length() > longestMessagingContainerPath.length())) {
longestMessagingContainerPath = messagingContainerMount.getKey();
}
}
if (log.isDebugEnabled()) log.debug("Longest matching path of messaging container: " + longestMessagingContainerPath);
return longestMessagingContainerPath;
}
public synchronized MessagingContainer getMessagingContainer(String messagingContainerPath) {
if (! messagingContainerPath.startsWith("/")) messagingContainerPath = "/" + messagingContainerPath;
MessagingContainerMount messagingContainerMount = this.messagingContainerMounts.get(messagingContainerPath);
return messagingContainerMount == null ? null : messagingContainerMount.getMessagingContainer();
}
public synchronized String[] getMessagingContainerPaths() {
return this.messagingContainerMounts.keySet().toArray(new String[this.messagingContainerMounts.size()]);
}
/*
* MessagingContainerFactorys
*/
@Override
public synchronized List<UriMessagingContainerFactoryMount> getMessagingContainerFactoryMounts() {
return new ArrayList<UriMessagingContainerFactoryMount> (this.messagingContainerFactoryMounts.values());
}
@Override
public synchronized int getNumMessagingContainerFactorys() {
return this.messagingContainerFactoryMounts.size();
}
public synchronized String findMessagingContainerFactoryPath(String requestPath) {
if (! requestPath.startsWith("/")) requestPath = "/" + requestPath;
if (log.isDebugEnabled()) log.debug("Finding messaging container factory for path: " + requestPath);
String longestMessagingContainerFactoryPath = null;
for (Map.Entry<String, UriMessagingContainerFactoryMount> messagingContainerFactoryMount : this.messagingContainerFactoryMounts.entrySet()) {
if (requestPath.startsWith(messagingContainerFactoryMount.getKey()) && (longestMessagingContainerFactoryPath == null || messagingContainerFactoryMount.getKey().length() > longestMessagingContainerFactoryPath.length())) {
longestMessagingContainerFactoryPath = messagingContainerFactoryMount.getKey();
}
}
if (log.isDebugEnabled()) log.debug("Longest matching path of messaging container factory: " + longestMessagingContainerFactoryPath);
return longestMessagingContainerFactoryPath;
}
public synchronized UriMessagingContainerFactory getMessagingContainerFactory(String messagingContainerFactoryPath) {
if (! messagingContainerFactoryPath.startsWith("/")) messagingContainerFactoryPath = "/" + messagingContainerFactoryPath;
UriMessagingContainerFactoryMount messagingContainerFactoryMount = this.messagingContainerFactoryMounts.get(messagingContainerFactoryPath);
return messagingContainerFactoryMount == null ? null : messagingContainerFactoryMount.getMessagingContainerFactory();
}
public synchronized String[] getMessagingContainerFactoryPaths() {
return this.messagingContainerFactoryMounts.keySet().toArray(new String[this.messagingContainerFactoryMounts.size()]);
}
/*
* Getters and setters
*/
public boolean isCheckDisabled() {
return this.checkDisabled;
}
public void setCheckDisabled(boolean checkDisabled) {
this.checkDisabled = checkDisabled;
}
public boolean isCheckExpired() {
return this.checkExpired;
}
public void setCheckExpired(boolean checkExpired) {
this.checkExpired = checkExpired;
}
}