/* * Copyright 2013 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package org.openntf.domino.utils; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.StringReader; import java.net.URL; import java.security.AccessControlException; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Scanner; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.logging.Level; import java.util.logging.Logger; import lotus.domino.NotesException; import lotus.notes.NotesThread; import org.openntf.domino.AutoMime; import org.openntf.domino.Base; import org.openntf.domino.Database; import org.openntf.domino.DocumentCollection; import org.openntf.domino.Session; import org.openntf.domino.Session.RunContext; import org.openntf.domino.WrapperFactory; import org.openntf.domino.exceptions.DataNotCompatibleException; import org.openntf.domino.exceptions.UndefinedDelegateTypeException; import org.openntf.domino.ext.Session.Fixes; import org.openntf.domino.graph.DominoGraph; import org.openntf.domino.logging.Logging; import org.openntf.domino.session.INamedSessionFactory; import org.openntf.domino.session.ISessionFactory; import org.openntf.domino.session.NamedSessionFactory; import org.openntf.domino.session.NativeSessionFactory; import org.openntf.domino.session.PasswordSessionFactory; import org.openntf.domino.session.SessionFullAccessFactory; import org.openntf.domino.session.TrustedSessionFactory; import org.openntf.domino.types.FactorySchema; import org.openntf.domino.types.SessionDescendant; import org.openntf.domino.utils.Factory.SessionType; import org.openntf.service.IServiceLocator; import org.openntf.service.ServiceLocatorFinder; /** * The Enum Factory. Does the Mapping lotusObject <=> OpenNTF-Object */ public enum Factory { ; /** * Printer class (will be modified by XSP-environment), so that the Factory prints directly to Console (so no "HTTP JVM" Prefix is * there) * * @author Roland Praml, FOCONIS AG * */ public static class Printer { public void println(final String s) { System.out.println(s); } } public static Printer printer = new Printer(); /** * An identifier for the different session types the factory can create. * * @author Roland Praml, FOCONIS AG * */ public enum SessionType { /** * The current session. This means: * * <ul> * <li>The current XPage session, if you are IN a XPage-Thread. This is equivalent to the "session" SSJS variable</li> * <li>The current XOTS session, if you are IN a XOTS-Thread. <br> * This is either the session that {@link XotsTasklet.Interface#getSessionFactory()} can create (if the Runnable implements that * interface and provide a Factory)<br> * or the session, you specified for that runnable with the {@link XotsTasklet#session()} annotation. See {@link XotsSessionType} * for available types. * </ul> * <b>The Method will fail, if you are running in a wrongly set up Thread</b><br> * (But there should be only XPage-Threads or XOTS-Threads. TODO RPr: and maybe DominoTheads) */ CURRENT(0, "CURRENT"), /** * Returns a session with full access. This is a named session (name is equal to {@link #CURRENT}s session name) but with full * access rights. {@link #FULL_ACCESS} may provide the same session as {@link #CURRENT} if the Runnable was annotated with a * *_FULL_ACCESS {@link XotsSessionType} */ CURRENT_FULL_ACCESS(1, "CURRENT_FULL_ACCESS"), /** * Returns a named session as signer. The code-signer is either the server (if the runnable is not inside an NSF) or the signer of * that runnable. <br> * <b>Note 1:</b> This session becomes invalid, if you the classloader gets tainted by loading classes that are signed by different * users! <br/> * <b>Note 2:</b> Due a bug, we return always SessionAsSigner (@see http://www.mydominolab.com/2011/10/xpages-sessionassigner.html) */ SIGNER(2, "SIGNER"), /** * This is currently the SAME session as {@link #SIGNER} due a Bug in XPages. */ SIGNER_FULL_ACCESS(3, "SIGNER_FULL_ACCESS"), /** * Returns a NATIVE session */ NATIVE(4, "NATIVE"), /** * Returns a TRUSTED session (This does not yet work!) */ TRUSTED(5, "TRUSTED"), /** * Returns a Session with full access. */ FULL_ACCESS(6, "FULL_ACCESS"), /** * Returns a Session with full access. */ PASSWORD(7, "PASSWORD"), /** * for internal use only! */ _NAMED_internal(-1, "NAMED"), /** * for internal use only! */ _NAMED_FULL_ACCESS_internal(-1, "NAMED_FULL_ACCESS"), ; public Session get() { return Factory.getSession(this); } static int SIZE = 8; int index; String alias; SessionType(final int index, final String alias) { this.index = index; this.alias = alias; } } public static class ThreadConfig { public final Fixes[] fixes; public final AutoMime autoMime; public final boolean bubbleExceptions; public ThreadConfig(final Fixes[] fixes, final AutoMime autoMime, final boolean bubbleExceptions) { super(); this.fixes = fixes; this.autoMime = autoMime; this.bubbleExceptions = bubbleExceptions; } } // this thread config wraps everything and squelches errors (does not change default behavior public static ThreadConfig PERMISSIVE_THREAD_CONFIG = new ThreadConfig(Fixes.values(), AutoMime.WRAP_ALL, false); public static ThreadConfig STRICT_THREAD_CONFIG = new ThreadConfig(Fixes.values(), AutoMime.WRAP_32K, true); /** * Container Class for all statistic counters * * @author Roland Praml, FOCONIS AG * */ private static class Counters { /** The lotus counter. */ private final Counter lotus; /** The recycle err counter. */ private final Counter recycleErr; /** The auto recycle counter. */ private final Counter autoRecycle; /** The manual recycle counter. */ private final Counter manualRecycle; private boolean countPerThread_; private Map<Class<?>, Counter> classes; /** * Returns a counter for a certain class * * @param clazz * the class * @return a counter for the class */ public Counter forClass(final Class<?> clazz) { Counter ret = classes.get(clazz); if (ret == null) { ret = new Counter(countPerThread_); classes.put(clazz, ret); } return ret; } Counters(final boolean countPerThread) { countPerThread_ = countPerThread; lotus = new Counter(countPerThread); recycleErr = new Counter(countPerThread); autoRecycle = new Counter(countPerThread); manualRecycle = new Counter(countPerThread); classes = new ConcurrentHashMap<Class<?>, Counter>(); } } /** * We have so many threadLocals here, so that it is worth to handle them all in a container class. * * @author Roland Praml, FOCONIS AG * */ private static class ThreadVariables { private WrapperFactory wrapperFactory; private ClassLoader classLoader; private IServiceLocator serviceLocator; /** * Support for different Locale */ private Locale userLocale; /** the factories can create a new session */ public ISessionFactory[] sessionFactories = new ISessionFactory[SessionType.SIZE]; /** the sessions are stored in the sessionHolder */ private Session[] sessionHolders = new Session[SessionType.SIZE]; public INamedSessionFactory namedSessionFactory; public INamedSessionFactory namedSessionFullAccessFactory; /** These sessions will be recycled at the end of that thread. Key = UserName of session */ public Map<String, Session> ownSessions = new HashMap<String, Session>(); private List<Runnable> terminateHooks; private ThreadConfig threadConfig; public ThreadVariables(final ThreadConfig tc) { threadConfig = tc; } /** clear the object */ private void clear() { wrapperFactory = null; classLoader = null; serviceLocator = null; for (int i = 0; i < SessionType.SIZE; i++) { sessionHolders[i] = null; sessionFactories[i] = null; } userLocale = null; namedSessionFactory = null; namedSessionFullAccessFactory = null; } public void removeTerminateHook(final Runnable hook) { if (terminateHooks == null) return; terminateHooks.remove(hook); } public void addTerminateHook(final Runnable hook) { if (terminateHooks == null) { terminateHooks = new ArrayList<Runnable>(); } terminateHooks.add(hook); } public void terminate() { if (terminateHooks != null) { for (Runnable hook : terminateHooks) { hook.run(); } terminateHooks = null; } } } private static ISessionFactory[] defaultSessionFactories = new ISessionFactory[SessionType.SIZE]; private static INamedSessionFactory defaultNamedSessionFactory; private static INamedSessionFactory defaultNamedSessionFullAccessFactory; /** * Holder for variables that are different per thread */ private static ThreadLocal<ThreadVariables> threadVariables_ = new ThreadLocal<ThreadVariables>(); private static List<Runnable> globalTerminateHooks = new ArrayList<Runnable>(); private static List<Runnable> shutdownHooks = new ArrayList<Runnable>(); private static String localServerName; private static ThreadVariables getThreadVariables() { ThreadVariables tv = threadVariables_.get(); if (tv == null) throw new IllegalStateException(Factory.class.getName() + " is not initialized for this thread!"); return tv; } public static ThreadConfig getThreadConfig() { ThreadVariables tv = threadVariables_.get(); if (tv == null) return PERMISSIVE_THREAD_CONFIG; return tv.threadConfig; } private static Map<String, String> ENVIRONMENT; //private static boolean session_init = false; //private static boolean jar_init = false; private static boolean started = false; /** * load the configuration * */ private static void loadEnvironment(final Scanner scanner) { if (ENVIRONMENT == null) { ENVIRONMENT = new HashMap<String, String>(); } if (scanner != null) { while (scanner.hasNextLine()) { String nextLine = scanner.nextLine(); int i = nextLine.indexOf('='); if (i > 0) { String key = nextLine.substring(0, i).toLowerCase(); String value = nextLine.substring(i + 1); ENVIRONMENT.put(key, value); } } } try { AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { try { ClassLoader cl = Factory.class.getClassLoader(); // we MUST use the Factory-classloader to find the correct MANIFEST Enumeration<URL> resources = cl.getResources("META-INF/MANIFEST.MF"); while (resources.hasMoreElements()) { Manifest manifest = new Manifest(resources.nextElement().openStream()); // check that this is your manifest and do what you need or get the next one Attributes attrib = manifest.getMainAttributes(); String bundleName = attrib.getValue("Bundle-SymbolicName"); if (bundleName != null) { int pos; if ((pos = bundleName.indexOf(';')) != -1) { bundleName = bundleName.substring(0, pos); } if ("org.openntf.domino".equals(bundleName)) { ENVIRONMENT.put("version", attrib.getValue("Bundle-Version")); ENVIRONMENT.put("title", attrib.getValue("Implementation-Title")); ENVIRONMENT.put("url", attrib.getValue("Implementation-Vendor-URL")); return null; } } } } catch (Exception e) { e.printStackTrace(); } return null; } }); } catch (AccessControlException e) { e.printStackTrace(); } catch (PrivilegedActionException e) { e.printStackTrace(); } if (!ENVIRONMENT.containsKey("version")) { ENVIRONMENT.put("version", "0.0.0.unknown"); } } public static String getEnvironment(final String key) { if (ENVIRONMENT == null) { loadEnvironment(null); } return ENVIRONMENT.get(key); } public static String getTitle() { return getEnvironment("title"); } public static String getUrl() { return getEnvironment("url"); } public static String getVersion() { return getEnvironment("version"); } public static String getDataPath() { return getEnvironment("directory"); } public static String getProgramPath() { return getEnvironment("notesprogram"); } public static String getHTTPJVMHeapSize() { return getEnvironment("httpjvmheapsize"); } /** The Constant log_. */ private static final Logger log_ = Logger.getLogger(Factory.class.getName()); /** The lotus counter. */ private static Counters counters; public static void enableCounters(final boolean enable, final boolean perThread) { if (enable) { counters = new Counters(perThread); } else { counters = null; } } /** * Gets the lotus count. * * @return the lotus count */ public static int getLotusCount() { return counters == null ? 0 : counters.lotus.intValue(); } /** * Count a created lotus element. */ public static void countLotus(final Class<?> c) { if (counters != null) { counters.lotus.increment(); counters.forClass(c).increment(); } } /** * Gets the recycle error count. * * @return the recycle error count */ public static int getRecycleErrorCount() { return counters == null ? 0 : counters.recycleErr.intValue(); } /** * Count recycle error. */ public static void countRecycleError(final Class<?> c) { if (counters != null) counters.recycleErr.increment(); } /** * Gets the auto recycle count. * * @return the auto recycle count */ public static int getAutoRecycleCount() { return counters == null ? 0 : counters.autoRecycle.intValue(); } /** * Count auto recycle. * * @return the int */ public static int countAutoRecycle(final Class<?> c) { if (counters != null) { counters.forClass(c).decrement(); return counters.autoRecycle.increment(); } else { return 0; } } /** * Gets the manual recycle count. * * @return the manual recycle count */ public static int getManualRecycleCount() { return counters == null ? 0 : counters.manualRecycle.intValue(); } /** * Count a manual recycle */ public static int countManualRecycle(final Class<?> c) { if (counters != null) { counters.forClass(c).decrement(); return counters.manualRecycle.increment(); } else { return 0; } } /** * get the active object count * * @return The current active object count */ public static int getActiveObjectCount() { if (counters != null) { return counters.lotus.intValue() - counters.autoRecycle.intValue() - counters.manualRecycle.intValue(); } else { return 0; } } /** * Determine the run context where we are * * @return The active RunContext */ public static RunContext getRunContext() { // TODO finish this implementation, which needs a lot of work. // - ADDIN // - APPLET // - DIIOP // - DOTS // - PLUGIN // - SERVLET // - XPAGES_NSF // maybe a simple way to determine => create a Throwable and look into the stack trace RunContext result = RunContext.UNKNOWN; SecurityManager sm = System.getSecurityManager(); if (sm == null) return RunContext.CLI; Object o = sm.getSecurityContext(); if (log_.isLoggable(Level.INFO)) log_.log(Level.INFO, "SecurityManager is " + sm.getClass().getName() + " and context is " + o.getClass().getName()); if (sm instanceof lotus.notes.AgentSecurityManager) { lotus.notes.AgentSecurityManager asm = (lotus.notes.AgentSecurityManager) sm; Object xsm = asm.getExtenderSecurityContext(); if (xsm instanceof lotus.notes.AgentSecurityContext) { } Object asc = asm.getSecurityContext(); if (asc != null) { // System.out.println("Security context is " + asc.getClass().getName()); } // ThreadGroup tg = asm.getThreadGroup(); // System.out.println("ThreadGroup name: " + tg.getName()); result = RunContext.AGENT; } // com.ibm.domino.http.bootstrap.logger.RCPLoggerConfig rcplc; try { Class<?> BCLClass = Class.forName("com.ibm.domino.http.bootstrap.BootstrapClassLoader"); if (BCLClass != null) { ClassLoader cl = (ClassLoader) BCLClass.getMethod("getSharedClassLoader").invoke(null); if ("com.ibm.domino.http.bootstrap.BootstrapOSGIClassLoader".equals(cl.getClass().getName())) { result = RunContext.XPAGES_OSGI; } } } catch (Exception e) { } return result; } private static WrapperFactory DEFAULT_WRAPPER_FACTORY = new org.openntf.domino.impl.WrapperFactory(); /** * returns the wrapper factory for this thread * * @return the thread's wrapper factory */ public static WrapperFactory getWrapperFactory() { ThreadVariables tv = getThreadVariables(); WrapperFactory wf = tv.wrapperFactory; if (wf == null) { try { List<WrapperFactory> wfList = findApplicationServices(WrapperFactory.class); if (wfList.size() > 0) wf = wfList.get(0); else wf = DEFAULT_WRAPPER_FACTORY; } catch (Throwable t) { log_.log(Level.WARNING, "Getting default WrapperFactory", t); wf = DEFAULT_WRAPPER_FACTORY; } tv.wrapperFactory = wf; } return wf; } /** * Returns the wrapper factory if initialized * * @return The active WrapperFactory */ public static WrapperFactory getWrapperFactory_unchecked() { ThreadVariables tv = threadVariables_.get(); return tv == null ? null : threadVariables_.get().wrapperFactory; } // RPr: A setter is normally not needed. The wrapperFactory should be configure with an application service! // /** // * Set/changes the wrapperFactory for this thread // * // * @param wf // * The new WrapperFactory // */ // public static void setWrapperFactory(final WrapperFactory wf) { // currentWrapperFactory.set(wf); // } // --- session handling // @SuppressWarnings("rawtypes") // @Deprecated // public static org.openntf.domino.Document fromLotusDocument(final lotus.domino.Document lotus, final Base parent) { // return getWrapperFactory().fromLotus(lotus, Document.SCHEMA, (Database) parent); // } //This should not be needed any more //public static void setNoRecycle(final Base<?> base, final boolean value) { // getWrapperFactory().setNoRecycle(base, value); //} /* * (non-JavaDoc) * * @see org.openntf.domino.WrapperFactory#fromLotus(lotus.domino.Base, FactorySchema, Base) */ /** * @deprecated Use {@link WrapperFactory#fromLotus(lotus.domino.Base, FactorySchema, Base)} instead. */ @Deprecated @SuppressWarnings("rawtypes") public static <T extends Base, D extends lotus.domino.Base, P extends Base> T fromLotus(final D lotus, final FactorySchema<T, D, P> schema, final P parent) { T result = getWrapperFactory().fromLotus(lotus, schema, parent); // if (result instanceof org.openntf.domino.Session) { // ThreadVariables tv = getThreadVariables(); // org.openntf.domino.Session check = tv.sessionHolders[SessionType.CURRENT.index]; // if (check == null) { // // TODO RPr: I have really objections to this. // // Setting the first session as default session is NOT nice // log_.log(Level.WARNING, "WARNING! Setting the Session " + result // + " as CURRENT session. This means you run in a wrong initialized thread", new Throwable()); // setSession((org.openntf.domino.Session) result, SessionType.CURRENT); // } // } return result; } // RPr: Should be done directly to current wrapperFactory // public static boolean recacheLotus(final lotus.domino.Base lotus, final Base<?> wrapper, final Base<?> parent) { // return getWrapperFactory().recacheLotusObject(lotus, wrapper, parent); // } /** * From lotus wraps a given lotus collection in an org.openntf.domino collection * * @param <T> * the generic org.openntf.domino type (drapper) * @param <D> * the generic lotus.domino type (delegate) * @param <P> * the generic org.openntf.domino type (parent) * @param lotus * the object to wrap * @param schema * the generic schema to ensure type safeness (may be null) * @param parent * the parent * @return the wrapped object * @deprecated Use {@link WrapperFactory#fromLotus(Collection, FactorySchema, Base)} instead. */ @SuppressWarnings({ "rawtypes" }) @Deprecated public static <T extends Base, D extends lotus.domino.Base, P extends Base> Collection<T> fromLotus(final Collection<?> lotusColl, final FactorySchema<T, D, P> schema, final P parent) { return getWrapperFactory().fromLotus(lotusColl, schema, parent); } /** * From lotus wraps a given lotus collection in an org.openntf.domino collection * * @param <T> * the generic org.openntf.domino type (wrapper) * @param <D> * the generic lotus.domino type (delegate) * @param <P> * the generic org.openntf.domino type (parent) * @param lotus * the object to wrap * @param schema * the generic schema to ensure type safeness (may be null) * @param parent * the parent * @return the wrapped object * @deprecated Use {@link WrapperFactory#fromLotusAsVector(Collection, FactorySchema, Base)} instead. */ @Deprecated @SuppressWarnings("rawtypes") public static <T extends Base, D extends lotus.domino.Base, P extends Base> Vector<T> fromLotusAsVector(final Collection<?> lotusColl, final FactorySchema<T, D, P> schema, final P parent) { return getWrapperFactory().fromLotusAsVector(lotusColl, schema, parent); } // RPr: Deprecated, so I commented this out // /** // * From lotus. // * // * @deprecated Use {@link #fromLotus(lotus.domino.Base, FactorySchema, Base)} instead // * // * // * @param <T> // * the generic type // * @param lotus // * the lotus // * @param T // * the t // * @param parent // * the parent // * @return the t // */ // @SuppressWarnings({ "rawtypes", "unchecked" }) // @Deprecated // public static <T> T fromLotus(final lotus.domino.Base lotus, final Class<? extends Base> T, final Base parent) { // return (T) getWrapperFactory().fromLotus(lotus, (FactorySchema) null, parent); // } // // /** // * From lotus. // * // * @deprecated Use {@link #fromLotus(Collection, FactorySchema, Base)} instead // * // * @param <T> // * the generic type // * @param lotusColl // * the lotus coll // * @param T // * the t // * @param parent // * the parent // * @return the collection // */ // @SuppressWarnings({ "unchecked", "rawtypes" }) // @Deprecated // public static <T> Collection<T> fromLotus(final Collection<?> lotusColl, final Class<? extends Base> T, final Base<?> parent) { // return getWrapperFactory().fromLotus(lotusColl, (FactorySchema) null, parent); // } // // /** // * @deprecated Use {@link #fromLotusAsVector(Collection, FactorySchema, Base)} // */ // @Deprecated // @SuppressWarnings({ "unchecked", "rawtypes" }) // public static <T> Vector<T> fromLotusAsVector(final Collection<?> lotusColl, final Class<? extends org.openntf.domino.Base> T, // final org.openntf.domino.Base<?> parent) { // return getWrapperFactory().fromLotusAsVector(lotusColl, (FactorySchema) null, parent); // } /** * Wrap column values, encapsulating {@link lotus.domino.DateTime}s, {@link lotus.domino.DateRange}s, and {@link lotus.domino.Name}s. * * @param values * the values * @return a {@link java.util.Vector} with the objects from the collection appropriately wrapped. * @deprecated Use {@link WrapperFactory#wrapColumnValues(Collection, Session)} instead. */ @Deprecated public static java.util.Vector<Object> wrapColumnValues(final Collection<?> values, final org.openntf.domino.Session session) { if (values == null) { log_.log(Level.WARNING, "Request to wrapColumnValues for a collection of null"); return null; } return getWrapperFactory().wrapColumnValues(values, session); } /** * Method to unwrap a object * * @param the * object to unwrap * @return the unwrapped object * @deprecated Use {@link WrapperFactory#fromLotus(lotus.domino.Base, FactorySchema, Base)} instead. */ @Deprecated public static <T extends lotus.domino.Base> T toLotus(final T base) { return getWrapperFactory().toLotus(base); } /** * Gets the current session. Equivalent to calling {@link #getSession(SessionType)} with {@link SessionType.CURRENT}. * * @return the session */ public static org.openntf.domino.Session getSession() { return getSession(SessionType.CURRENT); } /** * Gets the session full access. * * @return the session full access * @deprecated Use {@link #getSession(SessionType)} with {@link SessionType.FULL_ACCESS} instead. */ @Deprecated public static org.openntf.domino.Session getSessionFullAccess() { return getSession(SessionType.FULL_ACCESS); } /** * Gets the trusted session. * * @return the trusted session * @deprecated Use {@link #getSession(SessionType)} with {@link SessionType.TRUSTED} instead. */ @Deprecated public static org.openntf.domino.Session getTrustedSession() { return getSession(SessionType.TRUSTED); } /** * Gets the trusted session. * * @return the trusted session * @deprecated Use {@link #getSession(SessionType)} with {@link SessionType.SIGNER} instead. */ @Deprecated public static org.openntf.domino.Session getSessionAsSigner() { return getSession(SessionType.SIGNER); } public static org.openntf.domino.Session getSession(final SessionType mode, final String paramString) { ThreadVariables tv = getThreadVariables(); org.openntf.domino.Session result = tv.sessionHolders[mode.index]; if (result == null || result.isDead()) { ISessionFactory sf = getSessionFactory(mode); if (mode == SessionType.PASSWORD) { result = ((PasswordSessionFactory) sf).createSession(paramString); result = sf.createSession(); result.setSessionType(mode); tv.sessionHolders[mode.index] = result; tv.ownSessions.put(mode.alias, result); Session currentChk = tv.sessionHolders[SessionType.CURRENT.index]; if (currentChk == null) { tv.sessionHolders[SessionType.CURRENT.index] = result; tv.ownSessions.put(SessionType.CURRENT.alias, result); } } else if (sf != null) { result = sf.createSession(); result.setSessionType(mode); tv.sessionHolders[mode.index] = result; // Per default. Session objects are not recycled by the ODA and thats OK so. // this is our own session which will be recycled in terminate tv.ownSessions.put(mode.alias, result); //TODO NTF per RPr we can remove this when we have an alternative way to designate an internal session Session currentChk = tv.sessionHolders[SessionType.CURRENT.index]; if (currentChk == null) { tv.sessionHolders[SessionType.CURRENT.index] = result; tv.ownSessions.put(SessionType.CURRENT.alias, result); } // System.out.println("TEMP DEBUG: Created new session " + System.identityHashCode(result) + " of type " + mode.name() // + " in thread " + System.identityHashCode(Thread.currentThread()) + " from TV " + System.identityHashCode(tv)); } if (result == null) { log_.severe("Unable to get the session of type " + mode.alias + ". This probably means that you are running in an unsupported configuration " + "or you forgot to set up your context at the start of the operation. " + "If you're running in XPages, check the xsp.properties of your database. " + "If you are running in an Agent, make sure you start with a call to " + "Factory.setSession() and pass in your lotus.domino.Session"); } } else { // System.out.println("TEMP DEBUG: Found an existing session " + System.identityHashCode(result) + " of type " + mode.name() // + " in thread " + System.identityHashCode(Thread.currentThread()) + " from TV " + System.identityHashCode(tv)); } return result; } /** * * @param mode * The type of session to create * @return A Session object corresponding to the given type */ public static org.openntf.domino.Session getSession(final SessionType mode) { ThreadVariables tv = getThreadVariables(); org.openntf.domino.Session result = tv.sessionHolders[mode.index]; if (result == null || result.isDead()) { // System.out.println("TEMP DEBUG: No session found of type " + mode.name() + " in thread " // + System.identityHashCode(Thread.currentThread()) + " from TV " + System.identityHashCode(tv)); ISessionFactory sf = getSessionFactory(mode); if (sf != null) { // System.out.println("TEMP DEBUG getting a session using a " + sf.getClass().getName() + " for type " + mode.toString()); result = sf.createSession(); // System.out.println("TEMP DEBUG got a session for " + result.getEffectiveUserName()); result.setSessionType(mode); tv.sessionHolders[mode.index] = result; // Per default. Session objects are not recycled by the ODA and thats OK so. // this is our own session which will be recycled in terminate tv.ownSessions.put(mode.alias, result); Session currentChk = tv.sessionHolders[SessionType.CURRENT.index]; if (currentChk == null) { tv.sessionHolders[SessionType.CURRENT.index] = result; tv.ownSessions.put(SessionType.CURRENT.alias, result); } // System.out.println("TEMP DEBUG: Created new session " + System.identityHashCode(result) + " of type " + mode.name() // + " in thread " + System.identityHashCode(Thread.currentThread()) + " from TV " + System.identityHashCode(tv)); } if (result == null) { log_.severe("Unable to get the session of type " + mode.alias + ". This probably means that you are running in an unsupported configuration " + "or you forgot to set up your context at the start of the operation. " + "If you're running in XPages, check the xsp.properties of your database. " + "If you are running in an Agent, make sure you start with a call to " + "Factory.setSession() and pass in your lotus.domino.Session"); } } else { // System.out.println("TEMP DEBUG: Found an existing session " + System.identityHashCode(result) + " of type " + mode.name() // + " in thread " + System.identityHashCode(Thread.currentThread()) + " from TV " + System.identityHashCode(tv)); } return result; } /** * Returns the current session, if available. Does never create a session * * @return the session */ public static org.openntf.domino.Session getSession_unchecked(final SessionType type) { ThreadVariables tv = threadVariables_.get(); return tv == null ? null : tv.sessionHolders[type.index]; } /** * Sets the session for a certain sessionMode * */ // public static void setSession(final lotus.domino.Session session, final SessionType mode) { // if (session instanceof org.openntf.domino.Session) { // getThreadVariables().sessionHolders[mode.index] = (org.openntf.domino.Session) session; // // throw new UnsupportedOperationException("You should not set an org.openntf.domino.session as Session"); // } else { // getThreadVariables().sessionHolders[mode.index] = fromLotus(session, Session.SCHEMA, null); // } // } public static void setSessionFactory(final ISessionFactory sessionFactory, final SessionType mode) { getThreadVariables().sessionFactories[mode.index] = sessionFactory; } public static ISessionFactory getSessionFactory(final SessionType mode) { ThreadVariables tv = threadVariables_.get(); if (tv == null || tv.sessionFactories[mode.index] == null) { return defaultSessionFactories[mode.index]; } return tv.sessionFactories[mode.index]; } /** * // * Sets the current session // * // * @param session // * the lotus session // */ // public static void setSession(final lotus.domino.Session session) { // setSession(session, SessionType.DEFAULT); // } // // /** // * Sets the current trusted session // * // * @param session // * the lotus session // */ // public static void setTrustedSession(final lotus.domino.Session session) { // setSession(session, SessionType.TRUSTED); // } // // /** // * Sets the current session with full access // * // * @param session // * the lotus session // */ // public static void setSessionFullAccess(final lotus.domino.Session session) { // setSession(session, SessionType.FULL_ACCESS); // } // /** // * clears the current session // */ // public static void clearSession() { // threadVariables.get().sessionHolder = null; // } // TODO: Determine if this is the right way to deal with Xots access to faces contexts // RPr: use getSession_unchecked().getCurrentDatabase // /** // * Returns the session's current database if available. Does never create a session. // * // * @see #getSession_unchecked() // * @return The session's current database // */ // public static Database getDatabase_unchecked() { // Session sess = getSession_unchecked(SessionType.CURRENT); // return (sess == null) ? null : sess.getCurrentDatabase(); // } // RPr: I think it is a better idea to set the currentDatabase on the currentSesssion // TODO remove that code // public static void setDatabase(final Database database) { // setNoRecycle(database, true); // currentDatabaseHolder_.set(database); // } // // public static void clearDatabase() { // currentDatabaseHolder_.set(null); // } public static ClassLoader getClassLoader() { ThreadVariables tv = getThreadVariables(); if (tv.classLoader == null) { ClassLoader loader = null; try { loader = AccessController.doPrivileged(new PrivilegedExceptionAction<ClassLoader>() { @Override public ClassLoader run() throws Exception { return Thread.currentThread().getContextClassLoader(); } }); } catch (AccessControlException e) { e.printStackTrace(); } catch (PrivilegedActionException e) { e.printStackTrace(); } setClassLoader(loader); } return tv.classLoader; } public static <T> List<T> findApplicationServices(final Class<T> serviceClazz) { ThreadVariables tv = getThreadVariables(); if (tv.serviceLocator == null) { tv.serviceLocator = ServiceLocatorFinder.findServiceLocator(); } if (tv.serviceLocator == null) { throw new IllegalStateException("No service locator available so we cannot find the application services for " + serviceClazz.getName()); } return tv.serviceLocator.findApplicationServices(serviceClazz); } public static void setClassLoader(final ClassLoader loader) { getThreadVariables().classLoader = loader; } public static void setCurrentToSession(final Session session) { // System.out.println("TEMP DEBUG Setting current session to a session for " + session.getEffectiveUserName() + " in thread " // + System.identityHashCode(Thread.currentThread())); ThreadVariables tv = getThreadVariables(); tv.sessionHolders[SessionType.CURRENT.index] = session; } // avoid clear methods // public static void clearWrapperFactory() { // currentWrapperFactory.remove(); // } // // public static void clearClassLoader() { // currentClassLoader_.remove(); // } // // public static void clearServiceLocator() { // currentServiceLocator_.remove(); // } // // public static void clearDominoGraph() { // DominoGraph.clearDocumentCache(); // } // // public static void clearNoteCoordinateBuffer() { // NoteCoordinate.clearLocals(); // } // // public static void clearBubbleExceptions() { // DominoUtils.setBubbleExceptions(null); // } /** * Begin with a clear environment. Initialize this thread * */ public static void initThread(final ThreadConfig tc) { // RPr: Method was deliberately renamed if (!started) { throw new IllegalStateException("Factory is not yet started"); } if (log_.isLoggable(Level.FINER)) { log_.log(Level.FINER, "Factory.initThread()", new Throwable()); } if (threadVariables_.get() != null) { log_.log(Level.SEVERE, "WARNING - Thread " + Thread.currentThread().getName() + " was not correctly terminated or initialized twice", new Throwable()); } // System.out.println("TEMP DEBUG: Factory thread initializing."); // Throwable t = new Throwable(); // t.printStackTrace(); threadVariables_.set(new ThreadVariables(tc)); } /** * terminate the current thread. */ public static void termThread() { // RPr: Method was deliberately renamed if (log_.isLoggable(Level.FINER)) { log_.log(Level.FINER, "Factory.termThread()", new Throwable()); } ThreadVariables tv = threadVariables_.get(); if (tv == null) { log_.log(Level.SEVERE, "WARNING - Thread " + Thread.currentThread().getName() + " was not correctly initalized or terminated twice", new Throwable()); return; } // System.out.println("TEMP DEBUG: Factory thread terminating."); // Throwable trace = new Throwable(); // trace.printStackTrace(); try { for (Runnable term : globalTerminateHooks) { term.run(); } tv.terminate(); if (tv.wrapperFactory != null) { tv.wrapperFactory.recycle(); } // System.out.println("DEBUG: cleared " + termCount + " references from the queue..."); DominoUtils.setBubbleExceptions(null); DominoGraph.clearDocumentCache(); // The last step is to recycle ALL own sessions for (Session sess : tv.ownSessions.values()) { if (sess != null) { sess.recycle(); } } } catch (Throwable t) { log_.log(Level.SEVERE, "An error occured while terminating the factory", t); } finally { tv.clear(); threadVariables_.set(null); // System.gc(); } if (counters != null) { System.out.println(dumpCounters(true)); } } private static File getConfigFileFallback() { String progpath = System.getProperty("notes.binary"); File iniFile = new File(progpath + System.getProperty("file.separator") + "notes.ini"); if (!iniFile.exists()) { // System.out.println("Inifile not found on notes.binary path: " + progpath); progpath = System.getProperty("user.dir"); iniFile = new File(progpath + System.getProperty("file.separator") + "notes.ini"); } if (!iniFile.exists()) { // System.out.println("Inifile not found on notes.binary path: " + progpath); progpath = System.getProperty("java.home"); if (progpath.endsWith("jvm")) { iniFile = new File(progpath + System.getProperty("file.separator") + ".." + System.getProperty("file.separator") + "notes.ini"); } else { iniFile = new File(progpath + System.getProperty("file.separator") + "notes.ini"); } } if (!iniFile.exists()) { progpath = System.getProperty("java.library.path"); // Otherwise the tests will not work iniFile = new File(progpath + System.getProperty("file.separator") + "notes.ini"); } if (!iniFile.exists()) { // System.out.println("Inifile still not found on user.dir path: " + progpath); if (progpath.contains("framework")) { String pp2 = progpath.replace("framework", ""); iniFile = new File(pp2 + "notes.ini"); // System.out.println("Attempting to use path: " + pp2); if (!iniFile.exists()) { Factory.println("WARNING: Unable to read environment for log setup. Please look at the following properties..."); for (Object rawName : System.getProperties().keySet()) { if (rawName instanceof String) { Factory.println((String) rawName + " = " + System.getProperty((String) rawName)); } } } } } return iniFile; } public static void startup() { synchronized (Factory.class) { NotesThread.sinitThread(); try { lotus.domino.Session sess = lotus.domino.NotesFactory.createSession(); try { startup(sess); } finally { sess.recycle(); } } catch (NotesException e) { e.printStackTrace(); } finally { NotesThread.stermThread(); } } } public static synchronized void startup(final lotus.domino.Session session) { if (session instanceof org.openntf.domino.Session) { throw new UnsupportedOperationException("Initialization must be done on the raw session! How did you get that session?"); } if (started) { Factory.println("OpenNTF Domino API is already started. Cannot start it again"); } File iniFile; try { localServerName = session.getUserName(); iniFile = new File(session.evaluate("@ConfigFile").get(0).toString()); } catch (NotesException e) { Factory.println("WARNING: @ConfigFile returned " + e.getMessage() + " Using fallback to locate notes.ini"); iniFile = getConfigFileFallback(); } Factory.println("Starting the OpenNTF Domino API... Using notes.ini: " + iniFile); try { Scanner scanner = new Scanner(iniFile); scanner.useDelimiter("\n|\r\n"); loadEnvironment(scanner); scanner.close(); } catch (FileNotFoundException e) { Factory.println("Cannot read notes.ini. Giving up"); e.printStackTrace(); } // There is NO(!) Default SessionFactory for the current session. you have to set it! defaultSessionFactories[SessionType.CURRENT.index] = null; // For CURRENT_FULL_ACCESS, we return a named session with full access = true defaultSessionFactories[SessionType.CURRENT_FULL_ACCESS.index] = new ISessionFactory() { private static final long serialVersionUID = 1L; private String getName() { return Factory.getSession(SessionType.CURRENT).getEffectiveUserName(); } @Override public Session createSession() { return Factory.getNamedSession(getName(), true); } }; String defaultApiPath = null; // maybe we set this to ODA.nsf // In XPages environment, these factories will be replaced defaultNamedSessionFactory = new NamedSessionFactory(defaultApiPath); defaultNamedSessionFullAccessFactory = new SessionFullAccessFactory(defaultApiPath); defaultSessionFactories[SessionType.SIGNER.index] = new NativeSessionFactory(defaultApiPath); defaultSessionFactories[SessionType.SIGNER_FULL_ACCESS.index] = new SessionFullAccessFactory(defaultApiPath); // This will ALWAYS return the native/trusted/full access session (not overridden in XPages) defaultSessionFactories[SessionType.NATIVE.index] = new NativeSessionFactory(defaultApiPath); defaultSessionFactories[SessionType.TRUSTED.index] = new TrustedSessionFactory(defaultApiPath); defaultSessionFactories[SessionType.FULL_ACCESS.index] = new SessionFullAccessFactory(defaultApiPath); started = true; Factory.println("OpenNTF API Version " + ENVIRONMENT.get("version") + " started"); // Start up logging try { AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { Logging.getInstance().startUp(); return null; } }); } catch (AccessControlException e) { e.printStackTrace(); } catch (PrivilegedActionException e) { e.printStackTrace(); } } public static void setNamedFactories4XPages(final INamedSessionFactory normal, final INamedSessionFactory fullaccess) { defaultNamedSessionFactory = normal; defaultNamedSessionFullAccessFactory = fullaccess; } public static synchronized void shutdown() { Factory.println("Shutting down the OpenNTF Domino API... "); Runnable[] copy = shutdownHooks.toArray(new Runnable[shutdownHooks.size()]); for (Runnable term : copy) { try { term.run(); } catch (Throwable t) { t.printStackTrace(); } } Factory.println("OpenNTF Domino API shut down"); started = false; } public static boolean isStarted() { return started; } public static boolean isInitialized() { return threadVariables_.get() != null; } public static void setUserLocale(final Locale loc) { getThreadVariables().userLocale = loc; } public static Locale getUserLocale() { return getThreadVariables().userLocale; } /** * Returns the internal locale. The Locale is retrieved by this way: * <ul> * <li>If a currentDatabase is set, the DB is queried for its locale</li> * <li>If there is no database.locale, the system default locale is returned</li> * </ul> * This locale should be used, if you write log entries in a server log for example. * * @return the currentDatabase-locale or default-locale */ public static Locale getInternalLocale() { Locale ret = null; // are we in context of an NotesSession? Try to figure out the current database. Session sess = getSession_unchecked(SessionType.CURRENT); Database db = (sess == null) ? null : sess.getCurrentDatabase(); if (db != null) ret = db.getLocale(); if (ret == null) ret = Locale.getDefault(); return ret; } /** * Returns the external locale. The Locale is retrieved by this way: * <ul> * <li>Return the external locale (= the browser's locale in most cases) if available</li> * <li>If a currentDatabase is set, the DB is queried for its locale</li> * <li>If there is no database.locale, the system default locale is returned</li> * </ul> * This locale should be used, if you generate messages for the current (browser)user. * * @return the external-locale, currentDatabase-locale or default-locale */ public static Locale getExternalLocale() { Locale ret = getUserLocale(); if (ret == null) ret = getInternalLocale(); return ret; } /** * Debug method to get statistics * */ public static String dumpCounters(final boolean details) { if (counters == null) return "Counters are disabled"; StringBuilder sb = new StringBuilder(); sb.append("LotusCount: "); sb.append(getLotusCount()); sb.append(" AutoRecycled: "); sb.append(getAutoRecycleCount()); sb.append(" ManualRecycled: "); sb.append(getManualRecycleCount()); sb.append(" RecycleErrors: "); sb.append(getRecycleErrorCount()); sb.append(" ActiveObjects: "); sb.append(getActiveObjectCount()); if (!counters.classes.isEmpty() && details) { sb.append("\n=== The following objects were left in memory ==="); for (Entry<Class<?>, Counter> e : counters.classes.entrySet()) { int i = e.getValue().intValue(); if (i != 0) { sb.append("\n" + i + "\t" + e.getKey().getName()); } } } return sb.toString(); } public static INamedSessionFactory getNamedSessionFactory(final boolean fullAccess) { ThreadVariables tv = getThreadVariables(); if (fullAccess) { return tv.namedSessionFullAccessFactory != null ? tv.namedSessionFullAccessFactory : defaultNamedSessionFullAccessFactory; } else { return tv.namedSessionFactory != null ? tv.namedSessionFactory : defaultNamedSessionFactory; } } public static org.openntf.domino.Session getNamedSession(final String name, final boolean fullAccess) { ThreadVariables tv = getThreadVariables(); String key = name.toLowerCase() + (fullAccess ? ":full" : ":normal"); Session sess = tv.ownSessions.get(key); if (sess == null || sess.isDead()) { INamedSessionFactory sf = getNamedSessionFactory(fullAccess); if (sf != null) { sess = sf.createSession(name); sess.setSessionType(fullAccess ? SessionType._NAMED_FULL_ACCESS_internal : SessionType._NAMED_internal); } tv.ownSessions.put(key, sess); } return sess; } // /** // * Gets the parent database. // * // * @param base // * the base // * @return the parent database // */ // @Deprecated // public static Database getParentDatabase(final Base<?> base) { // if (base instanceof org.openntf.domino.Database) { // return (org.openntf.domino.Database) base; // } else if (base instanceof DatabaseDescendant) { // return ((DatabaseDescendant) base).getAncestorDatabase(); // } else if (base == null) { // throw new NullPointerException("Base object cannot be null"); // } else { // throw new UndefinedDelegateTypeException("Couldn't find session for object of type " + base.getClass().getName()); // } // } /** * Gets the session. * * @param base * the base * @return the session * @deprecated Use {@link SessionDescendant#getAncestorSession()} on the object instead. */ @Deprecated public static Session getSession(final lotus.domino.Base base) { org.openntf.domino.Session result = null; if (base instanceof SessionDescendant) { result = ((SessionDescendant) base).getAncestorSession(); } else if (base instanceof org.openntf.domino.Session) { result = (org.openntf.domino.Session) base; } else if (base == null) { throw new NullPointerException("Base object cannot be null"); } if (result == null) { throw new UndefinedDelegateTypeException("Couldn't find session for object of type " + base.getClass().getName()); } return result; } // public static boolean toBoolean(Object value) { // if (value instanceof String) { // char[] c = ((String) value).toCharArray(); // if (c.length > 1 || c.length == 0) { // return false; // } else { // return c[0] == '1'; // } // } else if (value instanceof Double) { // if (((Double) value).intValue() == 0) { // return false; // } else { // return true; // } // } else { // throw new DataNotCompatibleException("Cannot convert a " + value.getClass().getName() + " to boolean primitive."); // } // } // // public static int toInt(Object value) { // if (value instanceof Integer) { // return ((Integer) value).intValue(); // } else if (value instanceof Double) { // return ((Double) value).intValue(); // } else { // throw new DataNotCompatibleException("Cannot convert a " + value.getClass().getName() + " to int primitive."); // } // } // // public static double toDouble(Object value) { // if (value instanceof Integer) { // return ((Integer) value).doubleValue(); // } else if (value instanceof Double) { // return ((Double) value).doubleValue(); // } else { // throw new DataNotCompatibleException("Cannot convert a " + value.getClass().getName() + " to double primitive."); // } // } // // public static long toLong(Object value) { // if (value instanceof Integer) { // return ((Integer) value).longValue(); // } else if (value instanceof Double) { // return ((Double) value).longValue(); // } else { // throw new DataNotCompatibleException("Cannot convert a " + value.getClass().getName() + " to long primitive."); // } // } // // public static short toShort(Object value) { // if (value instanceof Integer) { // return ((Integer) value).shortValue(); // } else if (value instanceof Double) { // return ((Double) value).shortValue(); // } else { // throw new DataNotCompatibleException("Cannot convert a " + value.getClass().getName() + " to short primitive."); // } // // } // // public static float toFloat(Object value) { // if (value instanceof Integer) { // return ((Integer) value).floatValue(); // } else if (value instanceof Double) { // return ((Double) value).floatValue(); // } else { // throw new DataNotCompatibleException("Cannot convert a " + value.getClass().getName() + " to float primitive."); // } // // } // // public static Object toPrimitive(Vector<Object> values, Class<?> ctype) { // if (ctype.isPrimitive()) { // throw new DataNotCompatibleException(ctype.getName() + " is not a primitive type."); // } // if (values.size() > 1) { // throw new DataNotCompatibleException("Cannot create a primitive " + ctype + " from data because we have a multiple values."); // } // if (values.isEmpty()) { // throw new DataNotCompatibleException("Cannot create a primitive " + ctype + " from data because we don't have any values."); // } // if (ctype == Boolean.TYPE) // return toBoolean(values.get(0)); // if (ctype == Integer.TYPE) // return toInt(values.get(0)); // if (ctype == Short.TYPE) // return toShort(values.get(0)); // if (ctype == Long.TYPE) // return toLong(values.get(0)); // if (ctype == Float.TYPE) // return toFloat(values.get(0)); // if (ctype == Double.TYPE) // return toDouble(values.get(0)); // if (ctype == Byte.TYPE) // throw new UnimplementedException("Primitive conversion for byte not yet defined"); // if (ctype == Character.TYPE) // throw new UnimplementedException("Primitive conversion for char not yet defined"); // throw new DataNotCompatibleException(""); // } // // public static String join(Collection<Object> values, String separator) { // StringBuilder sb = new StringBuilder(); // Iterator<Object> it = values.iterator(); // while (it.hasNext()) { // sb.append(String.valueOf(it.next())); // if (it.hasNext()) // sb.append(separator); // } // return sb.toString(); // } // // public static String join(Collection<Object> values) { // return join(values, ", "); // } // // public static Object toPrimitiveArray(Vector<Object> values, Class<?> ctype) throws DataNotCompatibleException { // Object result = null; // int size = values.size(); // if (ctype == Boolean.TYPE) { // boolean[] outcome = new boolean[size]; // // TODO NTF - should allow for String fields that are binary sequences: "1001001" (SOS) // for (int i = 0; i < size; i++) { // Object o = values.get(i); // outcome[i] = toBoolean(o); // } // result = outcome; // } else if (ctype == Byte.TYPE) { // byte[] outcome = new byte[size]; // // TODO // result = outcome; // } else if (ctype == Character.TYPE) { // char[] outcome = new char[size]; // // TODO How should this work? Just concatenate the char arrays for each String? // result = outcome; // } else if (ctype == Short.TYPE) { // short[] outcome = new short[size]; // for (int i = 0; i < size; i++) { // Object o = values.get(i); // outcome[i] = toShort(o); // } // result = outcome; // } else if (ctype == Integer.TYPE) { // int[] outcome = new int[size]; // for (int i = 0; i < size; i++) { // Object o = values.get(i); // outcome[i] = toInt(o); // } // result = outcome; // } else if (ctype == Long.TYPE) { // long[] outcome = new long[size]; // for (int i = 0; i < size; i++) { // Object o = values.get(i); // outcome[i] = toLong(o); // } // result = outcome; // } else if (ctype == Float.TYPE) { // float[] outcome = new float[size]; // for (int i = 0; i < size; i++) { // Object o = values.get(i); // outcome[i] = toFloat(o); // } // result = outcome; // } else if (ctype == Double.TYPE) { // double[] outcome = new double[size]; // for (int i = 0; i < size; i++) { // Object o = values.get(i); // outcome[i] = toDouble(o); // } // result = outcome; // } // return result; // } // // public static Date toDate(Object value) throws DataNotCompatibleException { // if (value == null) // return null; // if (value instanceof Long) { // return new Date(((Long) value).longValue()); // } else if (value instanceof String) { // // TODO finish // DateFormat df = new SimpleDateFormat(); // try { // return df.parse((String) value); // } catch (ParseException e) { // throw new DataNotCompatibleException("Cannot create a Date from String value " + (String) value); // } // } else if (value instanceof lotus.domino.DateTime) { // return DominoUtils.toJavaDateSafe((lotus.domino.DateTime) value); // } else { // throw new DataNotCompatibleException("Cannot create a Date from a " + value.getClass().getName()); // } // } // // public static Date[] toDates(Collection<Object> vector) throws DataNotCompatibleException { // if (vector == null) // return null; // // Date[] result = new Date[vector.size()]; // int i = 0; // for (Object o : vector) { // result[i++] = toDate(o); // } // return result; // } // // public static org.openntf.domino.DateTime[] toDateTimes(Collection<Object> vector, org.openntf.domino.Session session) // throws DataNotCompatibleException { // if (vector == null) // return null; // // org.openntf.domino.DateTime[] result = new org.openntf.domino.DateTime[vector.size()]; // int i = 0; // for (Object o : vector) { // result[i++] = session.createDateTime(toDate(o)); // } // return result; // } // // public static org.openntf.domino.Name[] toNames(Collection<Object> vector, org.openntf.domino.Session session) // throws DataNotCompatibleException { // if (vector == null) // return null; // // org.openntf.domino.Name[] result = new org.openntf.domino.Name[vector.size()]; // int i = 0; // for (Object o : vector) { // result[i++] = session.createName(String.valueOf(o)); // } // return result; // } // // public static String[] toStrings(Collection<Object> vector) throws DataNotCompatibleException { // if (vector == null) // return null; // String[] strings = new String[vector.size()]; // int i = 0; // for (Object o : vector) { // if (o instanceof DateTime) { // strings[i++] = ((DateTime) o).getGMTTime(); // } else { // strings[i++] = String.valueOf(o); // } // } // return strings; // } /** * To lotus note collection. * * @param collection * the collection * @return the org.openntf.domino. note collection * * @deprecated this should be moved to {@link CollectionUtils} */ @Deprecated public static org.openntf.domino.NoteCollection toNoteCollection(final lotus.domino.DocumentCollection collection) { org.openntf.domino.NoteCollection result = null; if (collection instanceof DocumentCollection) { org.openntf.domino.Database db = ((DocumentCollection) collection).getParent(); result = db.createNoteCollection(false); result.add(collection); } else { throw new DataNotCompatibleException("Cannot convert a non-OpenNTF DocumentCollection to a NoteCollection"); } return result; } /** * Add a hook that will run on the next "terminate" call * * @param hook * the hook that should run on next terminate * */ public static void addTerminateHook(final Runnable hook, final boolean global) { if (global) { globalTerminateHooks.add(hook); } else { getThreadVariables().addTerminateHook(hook); } } public static void removeTerminateHook(final Runnable hook, final boolean global) { if (global) { globalTerminateHooks.remove(hook); } else { getThreadVariables().removeTerminateHook(hook); } } /** * Add a hook that will run on shutdown */ public static void addShutdownHook(final Runnable hook) { shutdownHooks.add(hook); } /** * Remove a shutdown hook * * @param hook * the hook that should be removed */ public static void removeShutdownHook(final Runnable hook) { shutdownHooks.remove(hook); } public static String getLocalServerName() { return localServerName; } public static void println(String prefix, final String lines) { BufferedReader reader = new BufferedReader(new StringReader(lines)); String line; try { if (Strings.isBlankString(prefix)) { prefix = "[ODA] "; } else { prefix = "[ODA::" + prefix + "] "; } while ((line = reader.readLine()) != null) { if (line.length() > 0) printer.println(prefix + line); } } catch (IOException ioex) { } } public static void println(final Object x) { println(null, String.valueOf(x)); } public static void println(final Object source, final Object x) { if (source == null) { println(null, String.valueOf(x)); } else { String prefix; if (source instanceof String) { prefix = (String) source; } else if (source instanceof Class) { prefix = ((Class<?>) source).getSimpleName(); } else { prefix = source.getClass().getSimpleName(); } println(prefix, String.valueOf(x)); } } }