/* * Copyright 2014 * * 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.impl; import java.util.HashSet; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.openntf.domino.utils.Factory; /** * A common Base class for those org.openntf.domino objects, which shouldn't be shared across threads. * * @param <T> * the generic type * @param <D> * the delegate type * @param <P> * the parent type * */ public abstract class BaseNonThreadSafe<T extends org.openntf.domino.Base<D>, D extends lotus.domino.Base, P extends org.openntf.domino.Base<?>> extends Base<T, D, P> { /** The Constant log_. */ private static final Logger log_ = Logger.getLogger(BaseNonThreadSafe.class.getName()); /** The delegate_. */ protected transient D delegate_; /** The Thread, in which wrapper was generated */ private transient Thread _myThread = Thread.currentThread(); static { Factory.addTerminateHook(new Runnable() { @Override public void run() { setAllowAccessAcrossThreads(false); } }, true); } /** * Instantiates a new base. * * @param delegate * the delegate * @param parent * the parent (may be null) * @param classId * the class id */ protected BaseNonThreadSafe(final D delegate, final P parent, final int classId) { super(delegate, parent, classId); } /** * Constructor for deserialization * * @param classId */ protected BaseNonThreadSafe(final int classId) { super(classId); } /** * Sets the delegate on init or if resurrect occurred * * @param delegate * the delegate * @param cppId * the cpp-id */ @Override protected final void setDelegate(final D delegate, final boolean fromResurrect) { delegate_ = delegate; if (fromResurrect) { // an other object is set now, so we must recache that object getFactory().recacheLotusObject(delegate, this, parent); if (log_.isLoggable(Level.FINEST)) { log_.log(Level.FINE, "Object of class " + this.getClass().getName() + " was recached. Changes may be lost", new Throwable()); } } } private static Set<Thread> _allowDirtyAccess4ThreadList = null; /** * Enables Thread support (sharing Notes-objects across threads). But you _SHOULD_ really avoid this! <br/> * <br/> * * <b>NOTE:</b> Sharing Notes-objects across threads is supported for objects of class {@link BaseThreadSafe}, and there are no problems * to be expected. <br/> * <br/> * * To share objects not extending BaseThreadSafe, you have to explicitly allow it by calling setAllowAccessAcrossThreads(true), which * allows objects wrapped in the current thread being accessed in other threads. But it not recommended to do so. Or rather it is * strongly recommended NOT to do so. Neither the ODA is tested, nor IBM recommend this. You should also read the paragraph <a * href="http://www-01.ibm.com/support/knowledgecenter/SSVRGU_8.5.3/com.ibm.designer.domino.main.doc/H_NOTESTHREAD_CLASS_JAVA.html" * >Multithreading issues</a> before using it. * * @param allow * enables thread support (and thread problems) */ protected static void setAllowAccessAcrossThreads(final boolean allow) { if (!allow && _allowDirtyAccess4ThreadList == null) return; Thread curr = Thread.currentThread(); synchronized (BaseNonThreadSafe.class) { if (allow) { if (_allowDirtyAccess4ThreadList == null) _allowDirtyAccess4ThreadList = new HashSet<Thread>(); _allowDirtyAccess4ThreadList.add(curr); } else { _allowDirtyAccess4ThreadList.remove(curr); if (_allowDirtyAccess4ThreadList.isEmpty()) _allowDirtyAccess4ThreadList = null; } } } /** * @param t * the Thread in which the Notes object was wrapped * @return true if it is allowed */ protected static boolean isAllowAccessAcrossThreads(final Thread t) { return _allowDirtyAccess4ThreadList != null && _allowDirtyAccess4ThreadList.contains(t); } /** * Gets the delegate without Resurrect * * @return the delegate */ @Override protected D getDelegate_unchecked() { if (_myThread != Thread.currentThread() && !isAllowAccessAcrossThreads(_myThread)) throw new IllegalStateException("Notes-Object of type " + this.getClass().getName() + " is used across threads! This Thread: " + Thread.currentThread() + " correct Thread: " + _myThread); return delegate_; } }