/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
package Sirius.navigator.connection.proxy;
import Sirius.navigator.connection.ConnectionSession;
import Sirius.navigator.exception.ConnectionException;
import Sirius.server.localserver.method.MethodMap;
import Sirius.server.middleware.types.Link;
import Sirius.server.middleware.types.MetaClass;
import Sirius.server.middleware.types.MetaObject;
import Sirius.server.middleware.types.Node;
import Sirius.server.newuser.User;
import Sirius.server.search.SearchOption;
import Sirius.server.search.SearchResult;
import Sirius.util.image.ImageHashMap;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import de.cismet.cids.server.actions.ServerActionParameter;
import de.cismet.cids.server.search.CidsServerSearch;
/**
* Default implementation of the connection proxy interface.
*
* @author Pascal
* @version 1.0 12/22/2002
*/
public class DefaultConnectionProxyHandler extends ConnectionProxyHandler {
//~ Instance fields --------------------------------------------------------
protected ProxyInterface proxyHandler;
protected ImageHashMap iconCache = null;
protected ClassAndMethodCache classAndMethodCache = null;
protected HashMap objectCache = new HashMap();
private final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(this.getClass());
//~ Constructors -----------------------------------------------------------
/**
* Creates a new DefaultConnectionProxyHandler object.
*
* @param connectionSession DOCUMENT ME!
*/
public DefaultConnectionProxyHandler(final ConnectionSession connectionSession) {
super(connectionSession);
proxyHandler = new DefaultConnectionProxyHandler.DefaultConnectionProxy();
}
//~ Methods ----------------------------------------------------------------
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public HashMap getClassHash() {
return classAndMethodCache.getClassHash();
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
try {
if (method.getDeclaringClass().equals(Sirius.navigator.connection.Connection.class)) {
if (method.getName().equals("getDefaultIcons")) { // NOI18N
if (iconCache == null) {
if (log.isInfoEnabled()) {
log.info("[ConnectionProxy] filling icon cache"); // NOI18N
}
iconCache = (ImageHashMap)method.invoke(connection, args);
}
return iconCache;
} else {
return method.invoke(connection, args);
}
} else if (method.getDeclaringClass().equals(Sirius.navigator.connection.proxy.ProxyInterface.class)) {
return method.invoke(proxyHandler, args);
} else {
log.error("[ConnectionProxy] undeclared method '" + method.getName() + "'"); // NOI18N
throw new RuntimeException("[ConnectionProxy] undeclared method '" + method.getName() + "'"); // NOI18N
}
} catch (final InvocationTargetException itex) {
// ok, no need to worry about
throw itex.getTargetException();
} catch (final Exception ex) {
log.error("[ConnectionProxy] unexpected invocation exception' " + ex.getMessage() + "'", ex); // NOI18N
throw new RuntimeException("[ConnectionProxy] unexpected invocation exception' " + ex.getMessage() + "'"); // NOI18N
}
}
//~ Inner Classes ----------------------------------------------------------
/**
* DOCUMENT ME!
*
* @version $Revision$, $Date$
*/
class ClassAndMethodCache {
//~ Instance fields ----------------------------------------------------
private HashMap classHash = null;
private HashMap methodHash = null;
private List lsNames = null;
//~ Constructors -------------------------------------------------------
/**
* Konstruiert einen neuen leeren ClassCache. Der ClassCache wird beim erstmaligen Ladens einer
* Sirius.Middleware.Types.Class gefuellt.
*/
public ClassAndMethodCache() // MetaService metaService)
{
classHash = new HashMap(25, 0.5f);
methodHash = new HashMap(25, 0.5f);
lsNames = new ArrayList(5);
// metaServiceRef = metaService;
}
/**
* Konstruiert einen neuen ClassCache, der mit den Classes eines bestimmten Lokalservers gefuellt wird.
*
* @param user User
* @param localServerNames DOCUMENT ME!
*/
public ClassAndMethodCache(final User user, final String[] localServerNames) {
classHash = new HashMap(50, 0.5f);
methodHash = new HashMap(25, 0.5f);
lsNames = new ArrayList((localServerNames.length + 1));
// metaServiceRef = metaService;
for (int i = 0; i < localServerNames.length; i++) {
try {
final MetaClass[] tmpClasses = connection.getClasses(user, localServerNames[i]); // .getClasses(user, localServerNames[i]);
if (tmpClasses != null) {
putClasses(tmpClasses, localServerNames[i]);
}
} catch (final Exception e) {
log.fatal("Ausnahme im ClassAndMethodCache beim Aufruf von remoteNodeRef.getClasses(...): ", e); // NOI18N
}
}
}
//~ Methods ------------------------------------------------------------
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public HashMap getClassHash() {
return classHash;
}
/**
* Laedt eine Class aus dem Cache bzw. vom Server.<br>
* Ist die Class noch nicht im Cache enthalten wird sie vom Server geladen, wurden von diesem LocalServer noch
* keine Classes geladen, so werden alle Classes dieses LocalServers gecacht.
*
* @param user User.
* @param classID Die ID der zu ladenden Class.
* @param localServerName Der LocalServer des Users.
*
* @return DOCUMENT ME!
*
* @throws ConnectionException DOCUMENT ME!
*/
public MetaClass getCachedClass(final User user, final int classID, final String localServerName)
throws ConnectionException {
final String key = new String(localServerName + classID);
// Falls noch keine Class von diesem LocalServer geladen wurde,
// -> alle Classes des LocalServer cachen
if (!lsNames.contains(localServerName)) {
final MetaClass[] tmpClasses = connection.getClasses(user, localServerName);
this.putClasses(tmpClasses, localServerName);
if (log.isDebugEnabled()) {
log.debug("<CC> Classes von neuem LocalServer " + localServerName + " gecacht");
}
}
// Falls die Class nicht im Cache enthalten ist
// -> Class vom Server laden
if (!classHash.containsKey(key)) {
final MetaClass tmpClass = connection.getMetaClass(user, classID, localServerName);
this.putClass(tmpClass, localServerName);
return tmpClass;
} else {
return (MetaClass)classHash.get(key);
}
}
/**
* Liefert alle Classes, die sich im Cache befinden.
*
* @return Ein Array von Type Sirius.Middleware.Types.Class oder null.
*/
public MetaClass[] getAllCachedClasses() {
final List classVector = new ArrayList(classHash.values());
if (classVector == null) {
return null;
}
return (MetaClass[])classVector.toArray(new MetaClass[classVector.size()]);
}
/**
* Fuegt eine Class zum ClassCache hinzu.
*
* @param cls Die zu cachende Class.
* @param lsID LocalServer ID, bildet zusammen mit der Class ID einen einduetigen Schluessel fuer die
* Hashtable
*/
protected void putClass(final MetaClass cls, final String lsID) {
final String key = String.valueOf(lsID + cls.getID());
if (!classHash.containsKey(key)) {
classHash.put(key, cls);
}
}
/**
* Fuegt ein Array von Classes zum ClassCache hinzu.
*
* @param classes Ein Array von cachenden Classes.
* @param localServerName DOCUMENT ME!
*/
protected void putClasses(final MetaClass[] classes, final String localServerName) {
lsNames.add(localServerName);
for (int i = 0; i < classes.length; i++) {
final String key = localServerName + classes[i].getID();
if (!classHash.containsKey(key)) {
classHash.put(key, classes[i]);
}
if (log.isDebugEnabled()) {
log.debug("<CMC> Class gecacht: " + classes[i].getName() + " " + classes[i].getID() + " "
+ classes[i].getDomain());
}
}
if (log.isDebugEnabled()) {
log.debug("<CMC> " + classes.length + " Classes von LocalServer " + localServerName + " gecacht.");
}
}
/**
* DOCUMENT ME!
*
* @param methodKey DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public Sirius.server.localserver.method.Method getCachedMethod(final String methodKey) {
return (Sirius.server.localserver.method.Method)methodHash.get(methodKey);
}
/**
* DOCUMENT ME!
*
* @param method DOCUMENT ME!
* @param localServerName DOCUMENT ME!
*/
protected void putMethod(final Sirius.server.localserver.method.Method method, final String localServerName) {
final String key = String.valueOf(localServerName + method.getID());
if (!methodHash.containsKey(key)) {
methodHash.put(key, method);
if (log.isDebugEnabled()) {
log.debug("<CMC> method '" + key + "' gecacht.");
}
}
}
/**
* DOCUMENT ME!
*
* @param methodMap DOCUMENT ME!
*/
protected void putMethods(final MethodMap methodMap) {
if (methodMap != null) {
methodHash.putAll(methodMap);
if (log.isDebugEnabled()) {
final Iterator iterator = methodMap.keySet().iterator();
if (iterator.hasNext()) {
if (log.isDebugEnabled()) {
log.debug("<CMC> method '" + iterator.next() + " gecacht."); // NOI18N
}
}
}
}
}
}
/**
* DOCUMENT ME!
*
* @version $Revision$, $Date$
*/
protected class DefaultConnectionProxy implements ProxyInterface {
//~ Methods ------------------------------------------------------------
@Override
public void setProperty(final String name, final String value) {
if (log.isDebugEnabled()) {
log.debug("[ProxyInterface] setting propety '" + name + "' to '" + value + "'");
}
}
@Override
public ConnectionSession getSession() {
return session;
}
@Override
public Node[] getRoots() throws ConnectionException {
return connection.getRoots(session.getUser());
}
@Override
public Node[] getRoots(final String domain) throws ConnectionException {
return connection.getRoots(session.getUser(), domain);
}
@Override
public Node[] getChildren(final Node node) throws ConnectionException {
final Node[] c = connection.getChildren(node, session.getUser());
if ((node.getDynamicChildrenStatement() != null) && node.isSqlSort()) {
return c;
}
return Sirius.navigator.tools.NodeSorter.sortNodes(c);
}
@Override
public Node getNode(final int nodeID, final String domain) throws ConnectionException {
return connection.getNode(session.getUser(), nodeID, domain);
}
@Override
public Node addNode(final Node node, final Link parent) throws ConnectionException {
return connection.addNode(node, parent, session.getUser());
}
@Override
public boolean deleteNode(final Node node) throws ConnectionException {
return connection.deleteNode(node, session.getUser());
}
@Override
public boolean addLink(final Node from, final Node to) throws ConnectionException {
return connection.addLink(from, to, session.getUser());
}
@Override
public boolean deleteLink(final Node from, final Node to) throws ConnectionException {
return connection.deleteLink(from, to, session.getUser());
}
@Override
public Node[] getClassTreeNodes() throws ConnectionException {
return connection.getClassTreeNodes(session.getUser());
}
@Override
public MetaClass[] getClasses() throws ConnectionException {
final String[] domains = connection.getDomains();
final ArrayList classes = new ArrayList();
for (int i = 0; i < domains.length; i++) {
MetaClass[] classArray = new MetaClass[0];
try {
classArray = this.getClasses(domains[i]);
} catch (final Exception t) {
log.error("Fehler im DefaultConnectionProxyHandler bei getClasses", t);
}
for (int j = 0; j < classArray.length; j++) {
classes.add(classArray[j]);
}
}
return (MetaClass[])classes.toArray(new MetaClass[classes.size()]);
}
@Override
public MetaClass[] getClasses(final String domain) throws ConnectionException {
return connection.getClasses(session.getUser(), domain);
}
/**
* DOCUMENT ME!
*
* @throws ConnectionException DOCUMENT ME!
*/
public void initClassAndMethodCache() throws ConnectionException {
classAndMethodCache = new ClassAndMethodCache(session.getUser(), connection.getDomains());
}
@Override
public MetaClass getMetaClass(final int classID, final String domain) throws ConnectionException {
if (classAndMethodCache == null) {
if (log.isInfoEnabled()) {
log.info("[ConnectionProxy] filling meta class cache"); // NOI18N
}
classAndMethodCache = new ClassAndMethodCache(session.getUser(), connection.getDomains());
}
final MetaClass metaClass = classAndMethodCache.getCachedClass(session.getUser(), classID, domain);
if (log.isDebugEnabled()) {
log.debug("getgetMetaClass(): classID=" + classID + ", domain=" + domain);
log.debug("MetaClass: " + metaClass + "\nMetaClass.getName(): " + metaClass.getName()
+ "\nMetaClass.getEditor(): " + metaClass.getEditor() + "\nMetaClass.getComplexEditor(): "
+ metaClass.getComplexEditor());
}
return metaClass;
}
@Override
public MetaClass getMetaClass(final String classKey) throws ConnectionException {
try {
final StringTokenizer tokenizer = new StringTokenizer(classKey, "@"); // NOI18N
final int classID = Integer.valueOf(tokenizer.nextToken()).intValue();
final String domain = tokenizer.nextToken();
return this.getMetaClass(classID, domain);
} catch (final ConnectionException cexp) {
throw cexp;
} catch (final Exception t) {
log.error("malformed classKey: '" + classKey + "' (classId@domain expected)"); // NOI18N
throw new ConnectionException("malformed class key: '" + classKey + "' (classId@domain expected)",
ConnectionException.ERROR,
t); // NOI18N
}
}
@Override
public Sirius.server.localserver.method.Method getMethod(final String methodKey) throws ConnectionException {
if (classAndMethodCache == null) {
if (log.isInfoEnabled()) {
log.info("[ConnectionProxy] filling meta class cache"); // NOI18N
}
classAndMethodCache = new ClassAndMethodCache(session.getUser(), connection.getDomains());
}
return classAndMethodCache.getCachedMethod(methodKey);
}
@Override
public MetaObject getMetaObject(final int objectID, final int classID, final String domain)
throws ConnectionException {
if (classAndMethodCache == null) {
initClassAndMethodCache();
}
if (log.isDebugEnabled()) {
log.debug("getMetaObject(): objectID=" + objectID + ", classID=" + classID + ", domain=" + domain); // NOI18N
}
final MetaObject metaObject = connection.getMetaObject(session.getUser(), objectID, classID, domain);
if (metaObject != null) {
if (log.isDebugEnabled()) {
log.debug(" MetaObject: " + metaObject + " MetaObject.getName(): " + metaObject.getName()
+ " MetaObject.getEditor(): " + metaObject.getEditor()
+ " MetaObject.getComplexEditor(): " + metaObject.getComplexEditor());
}
// set Classes in SubObjects as well
metaObject.setAllClasses(classAndMethodCache.getClassHash());
}
return metaObject;
}
@Override
public MetaObject getMetaObject(final String objectId) throws ConnectionException {
try {
final StringTokenizer tokenizer = new StringTokenizer(objectId, "@"); // NOI18N
final int objectID = Integer.valueOf(tokenizer.nextToken()).intValue();
final int classID = Integer.valueOf(tokenizer.nextToken()).intValue();
final String domain = tokenizer.nextToken();
return this.getMetaObject(objectID, classID, domain);
} catch (final ConnectionException cexp) {
throw cexp;
} catch (final Exception t) {
log.error("malformed object id: '" + objectId + "' (objectID@classID@domain expected)"); // NOI18N
throw new ConnectionException("malformed object id: '" + objectId
+ "' (objectID@classID@domain expected)",
ConnectionException.ERROR,
t); // NOI18N
}
}
@Override
public MetaObject[] getMetaObjectByQuery(final String query, final int sig) throws ConnectionException {
if (classAndMethodCache == null) {
initClassAndMethodCache();
}
if (log.isDebugEnabled()) {
log.debug("getMetaObjectByQuery"); // NOI18N
}
try {
final MetaObject[] obs = connection.getMetaObjectByQuery(session.getUser(), query);
for (int i = 0; i < obs.length; i++) {
if (obs[i] != null) {
obs[i].setAllClasses(classAndMethodCache.getClassHash());
}
}
return obs;
} catch (Exception t) {
log.warn("Fehler in getMetaObjectByQuery", t);
}
return null;
}
@Override
public MetaObject insertMetaObject(final MetaObject MetaObject, final String domain)
throws ConnectionException {
return connection.insertMetaObject(session.getUser(), MetaObject, domain);
}
@Override
public int updateMetaObject(final MetaObject MetaObject, final String domain) throws ConnectionException {
return connection.updateMetaObject(session.getUser(), MetaObject, domain);
}
@Override
public int deleteMetaObject(final MetaObject MetaObject, final String domain) throws ConnectionException {
return connection.deleteMetaObject(session.getUser(), MetaObject, domain);
}
@Override
public MetaObject getInstance(final MetaClass c) throws ConnectionException {
MetaObject MetaObject = null;
MetaObject = connection.getInstance(session.getUser(), c);
MetaObject.setAllClasses(classAndMethodCache.getClassHash());
return MetaObject;
}
@Override
public Collection customServerSearch(final CidsServerSearch serverSearch) throws ConnectionException {
return connection.customServerSearch(session.getUser(), serverSearch);
}
@Override
public Object executeTask(
final String taskname,
final String taskdomain,
final Object body,
final ServerActionParameter... params) throws ConnectionException {
return connection.executeTask(session.getUser(), taskname, taskdomain, body, params);
}
}
}