/* * Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton, * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY. * * THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS. * WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE * IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR * CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. * NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. * DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, * OR MODIFICATIONS. * THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION, * USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS * PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY * AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM */ package org.csstudio.dal.proxy; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import org.apache.log4j.Logger; import org.csstudio.dal.EventSystemListener; import org.csstudio.dal.RemoteException; import org.csstudio.dal.SimpleProperty; import org.csstudio.dal.context.AbstractApplicationContext; import org.csstudio.dal.context.ConnectionException; import org.csstudio.dal.context.Identifier; import org.csstudio.dal.context.IdentifierUtilities; import org.csstudio.dal.context.PlugContext; import org.csstudio.dal.context.PlugEvent; import org.csstudio.dal.device.AbstractDevice; import org.csstudio.dal.impl.PropertyUtilities; import org.csstudio.dal.spi.AbstractFactory; import org.csstudio.dal.spi.AbstractFactorySupport; import org.csstudio.dal.spi.Plugs; import com.cosylab.util.ListenerList; /** * This is abstract plug class which helps plug implementators write own * plug. This plug has implemented support for following features: proxy * object sharing. * * @author ikriznar */ public abstract class AbstractPlug implements PlugContext { /** * By default caching of proxy references is enabled. */ private static final boolean DEFAULT_CACHING_ENABLED=true; static class AbstractProxyHolder { int count; protected String id; public AbstractProxyHolder(String id) { count = 1; this.id=id; } static String toID(Proxy<?> p) { return toID(p.getUniqueName(),p.getClass()); } static String toID(String name, Class<?> type) { return name+"::"+type.getName(); } } class PropertyProxyHolder extends AbstractProxyHolder { PropertyProxyHolder(PropertyProxy<?,?> p) { super(toID(p)); proxy = p; } PropertyProxy<?,?> proxy; } class DirectoryProxyHolder extends AbstractProxyHolder { DirectoryProxyHolder(DirectoryProxy<?> p) { super(toID(p)); proxy = p; } DirectoryProxy<?> proxy; } class DeviceProxyHolder extends AbstractProxyHolder { DeviceProxyHolder(DeviceProxy<?> p) { super(toID(p)); proxy = p; } DeviceProxy<?> proxy; } private Map<String, PropertyProxyHolder> propertyCache; private Map<String, DirectoryProxyHolder> directoryCache; private Map<String, DeviceProxyHolder> deviceCache; private boolean cachingEnabled = DEFAULT_CACHING_ENABLED; private Properties configuration; private Map<Class<?extends AbstractDevice>, Class<?extends AbstractDevice>> deviceImplementationClasses = new HashMap<Class<?extends AbstractDevice>, Class<?extends AbstractDevice>>(); private Map<Class<?extends SimpleProperty<?>>, Class<?extends SimpleProperty<?>>> propertyImplementationClasses = new HashMap<Class<?extends SimpleProperty<?>>, Class<?extends SimpleProperty<?>>>(); private Map<Class<?extends AbstractDevice>, Class<?extends DeviceProxy<?>>> deviceProxiesImplementationClasses = new HashMap<Class<?extends AbstractDevice>, Class<?extends DeviceProxy<?>>>(); private Map<Class<?extends SimpleProperty<?>>, Class<?extends PropertyProxy<?,?>>> propertyProxiesImplementationClasses = new HashMap<Class<?extends SimpleProperty<?>>, Class<?extends PropertyProxy<?,?>>>(); protected ListenerList plugListeners; private Identifier identifier; private Logger logger; protected boolean debug = false; // TODO (jpenning) no longer in use? protected AbstractApplicationContext applicationContext; /** * Constructs a new plug instance which is associated with one specific * application context. * * @param applicationContext */ protected AbstractPlug(AbstractApplicationContext applicationContext) { this(applicationContext.getConfiguration()); this.applicationContext = applicationContext; } /** * Creates new plug instance with proxy caching enabled. */ protected AbstractPlug(Properties configuration) { this(configuration,DEFAULT_CACHING_ENABLED); } /** * Creates new plug instance. * @param cachingEnabled disables proxy caching if <code>false</code> */ protected AbstractPlug(Properties configuration, boolean cachingEnabled) { super(); // at the moment we do not use weak reference propertyCache = new HashMap<String, PropertyProxyHolder>(100); directoryCache = new HashMap<String, DirectoryProxyHolder>(100); deviceCache = new HashMap<String, DeviceProxyHolder>(100); this.cachingEnabled = cachingEnabled; if (configuration == null) { this.configuration = new Properties(); } else { this.configuration = (Properties)configuration.clone(); } getLogger().info("'"+getPlugType()+"' started."); } /** * Creates new proxy for remote object. The proxy object should be * returned immediatelly, even if this means that connection process to * remote entity was initiated in parallel and is not finished when proxy * object is returned.<p>Design contract is that in case when * lookup of remote entity takes long time, than proxy object is returned * immediatelly even if not connected and if connection fails, it may * fail later when appropriate property is already created and delivered * to application layer. In this case appropriate event must be fired by * the proxy.</p> * * @param uniqueName unique remote name or remote entity * @param type implementation class of returned proxy. If type is simply * PropertyProxy than plug decides which proxy implementation to * use. * * @return proxy for remote property * */ protected abstract <T extends PropertyProxy<?,?>> T createNewPropertyProxy( String uniqueName, Class<T> type) throws ConnectionException; /** * Creates new directory for proxy. This method is called only if * <code>createNewPropertyProxy</code> returns object that does not * imlement DirectoryProxy also.<p>Design contract is that in case * when lookup of remote entity takes long time, than proxy object is * returned immediatelly even if not connected and if connection fails, * it may fail later when appropriate property is already created and * delivered to application layer. In this case appropriate event must be * fired by the proxy.</p> * * @param uniqueName * * @return directory for proxy */ protected abstract DirectoryProxy<?> createNewDirectoryProxy(String uniqueName) throws ConnectionException; /** * Creates new proxy for remote object. The proxy object should be * returned immediatelly, even if this means that connection process to * remote entity was initiated in parallel and is not finished when proxy * object is returned.<p>Design contract is that in case when * lookup of remote entity takes long time, than proxy object is returned * immediatelly even if not connected and if connection fails, it may * fail later when appropriate property is already created and delivered * to application layer. In this case appropriate event must be fired by * the proxy.</p> * * @param <T> exact DeviceProxy type * @param uniqueName unique remote name or remote entity * @param type implementation class of returned proxy. If type is simply * PropertyProxy than plug decides which proxy implementation to * use. * * @return proxy for remote property * */ protected abstract <T extends DeviceProxy<?>> T createNewDeviceProxy( String uniqueName, Class<T> type) throws ConnectionException; /** * Returns plug type string, which is distinguishing for plug which * creates proxies for particular communication layer.<p>For * example plug that connects to EPICS device my return string "EPICS".</p> * * @return plug distinguishing type name */ @Override public abstract String getPlugType(); /** * Returns property implementation class * * @param uniquePropertyName property name * @return property implementation class * @throws RemoteException if remote request was issued and did not sucseede */ protected abstract Class<?extends SimpleProperty<?>> getPropertyImplementationClass (String uniquePropertyName) throws RemoteException; /** * Returns property proxy implementation class * * @param uniquePropertyName property name * @return property proxy implementation class * @throws RemoteException if remote request was issued and was not successfull */ protected abstract Class<?extends PropertyProxy<?,?>> getPropertyProxyImplementationClass(String uniquePropertyName) throws RemoteException; /** * Returns proxy object for unique name. Object is obtained by * procedure in following order: * <ol> * <li>Internal cache of used objects is checked if proxy * with this name already exists.</li> * <li>Internal cache of object no longer used but not yet * deleted is checked if proxy with this name already exists.</li> * <li>Only if upper methods fails, new proxy is created * and cached in used queue.</li> * </ol> * <p>For returned proxy use count is increased for one.</p> * * @param uniqueName an unique name of remote object * @param type class of property proxy, if <code>PropertyProxy.class</code> * than plug implementation decide which proxy will be returned. * * @return reused or new proxy object representing unique name * * @throws RemoteException if instantiation of object fails * @throws NullPointerException if unique name parameter is null */ public <TT extends PropertyProxy<?,?>> TT getPropertyProxy(String uniqueName, Class<TT> type) throws ConnectionException{ if (uniqueName == null) throw new NullPointerException("uniqueName"); try { PropertyProxy<?,?> p; if (PropertyProxy.class.equals(type)) p= getPropertyProxyFromCache(uniqueName); else p= getPropertyProxyFromCache(uniqueName,type); if (p != null) { if (!type.isInstance(p)) throw new ConnectionException(this, "PropertyProxy in cache for '" + uniqueName + "' is of type '" + p.getClass().getName() + "' and not of expected type '" + type.getName() + "'."); getLogger().debug("'"+uniqueName+"' reused (c:"+p.getConnectionInfo()+" t:"+type.getName()+")."); return type.cast(p); } TT pp = createNewPropertyProxy(uniqueName, type); getLogger().debug("'"+uniqueName+"' created (c:"+pp.getConnectionInfo()+" t:"+type.getName()+")."); putPropertyProxyToCache(pp); return pp; } catch (ConnectionException e) { getLogger().warn("'"+uniqueName+"' failed (t:"+type.getName()+").", e); throw e; } catch (Exception e) { getLogger().error("'"+uniqueName+"' connect failed, internal error possible (t:"+type.getName()+").", e); throw new ConnectionException(this, "Internal error while connecting to '"+uniqueName+"'.", e); } } /** * Returns proxy object for unique name. Object is obtained by * procedure in following order: * <ol> * <li>Internal cache of used objects is checked if proxy * with this name already exists.</li> * <li>Internal cache of object no longer used but not yet * deleted is checked if proxy with this name already exists.</li> * <li>Only if upper methods fails, new proxy is created * and cached in used queue.</li> * </ol> * <p>For returned proxy use count in increased for one.</p> * * @param uniqueName an unique name of remote object * * @return reused or new proxy object representing unique name * * @throws ConnectionException if instantiation of object fails */ public PropertyProxy<?,?> getPropertyProxy(String uniqueName) throws ConnectionException { return getPropertyProxy(uniqueName, PropertyProxy.class); } /** * Returns directory for proxy object of unique name. Object is * obtained by procedure in following order: * <ol> * <li>Internal cache of used objects is checked if proxy * with this name already exists.</li> * <li>Internal cache of object no longer used but not yet * deleted is checked if proxy with this name already exists.</li> * <li>Only if upper methods fails, new proxy is created * and cached in used queue.</li> * </ol> * <p>For returned proxy use count in increased for one.</p> * * @param uniqueName an unique name of remote object * * @return reused or new proxy object representing unique name * * @throws ConnectionException if instantiation of object fails * @throws NullPointerException if unique name parameter is null */ public DirectoryProxy<?> getDirectoryProxy(String uniqueName) throws ConnectionException { if (uniqueName == null) { throw new NullPointerException("uniqueName"); } DirectoryProxy<?> d = getDirectoryProxyFromCache(uniqueName); if (d != null) { return d; } d = createNewDirectoryProxy(uniqueName); putDirectoryProxyToCache(d); return d; } /** * Releases the proxy from cache and destroys it if proxy use count becomes 0. * * @param proxy Proxy to release */ public void releaseProxy(Proxy<?> proxy) { if (proxy == null) { throw new NullPointerException("proxy"); } boolean canDestroy = true; synchronized (this) { if (proxy instanceof DirectoryProxy) { canDestroy = releaseDirectoryProxyFromCache((DirectoryProxy<?>)proxy); } if (proxy instanceof PropertyProxy) { canDestroy &= releasePropertyProxyFromCache((PropertyProxy<?,?>)proxy); } if (proxy instanceof DeviceProxy) { canDestroy &=releaseDeviceProxyFromCache((DeviceProxy<?>)proxy); } } if (canDestroy) { try { proxy.destroy(); getLogger().debug("'"+proxy.getUniqueName()+"' destroyed (c:"+proxy.getConnectionInfo()+" t:"+proxy.getClass().getName()+")."); } catch (Exception e) { getLogger().warn("'"+proxy.getUniqueName()+"' destroyed with error (c:"+proxy.getConnectionInfo()+" t:"+proxy.getClass().getName()+").",e); } } else { getLogger().debug("'"+proxy.getUniqueName()+"' release indicated (c:"+proxy.getConnectionInfo()+" t:"+proxy.getClass().getName()+")."); } } /** * Destroys immediatelly all proxies which are no longer in use. */ public void flushCache() { // TODO: nothing to do at the moment, there is no intermediate cache } /** * Returns PropertyProxy from cache. Warning: this operation * increases use count by 1. * * @param uniqueName * * @return property proxy from cache or <code>null</code> is not cached */ protected synchronized PropertyProxy<?,?> getPropertyProxyFromCache( String uniqueName) { PropertyProxyHolder h = propertyCache.get(uniqueName); if (h == null) { return null; } h.count++; return h.proxy; } /** * Returns PropertyProxy from cache. Warning: this operation * increases use count by 1. * * @param uniqueName * @param type * * @return property proxy from cache or <code>null</code> is not cached */ protected synchronized PropertyProxy<?,?> getPropertyProxyFromCache( String uniqueName, Class<?> type) { PropertyProxyHolder h = propertyCache.get(AbstractProxyHolder.toID(uniqueName,type)); if (h == null) { return null; } h.count++; return h.proxy; } /** * Returns DirectoryProxy from cache. Warning: this operation * increases use count by 1. * * @param uniqueName * * @return property proxy from cache or <code>null</code> is not cached */ protected synchronized DirectoryProxy<?> getDirectoryProxyFromCache( String uniqueName) { DirectoryProxyHolder h = directoryCache.get(uniqueName); if (h == null) { return null; } h.count++; return h.proxy; } /** * Returns DirectoryProxy from cache. Warning: this operation * increases use count by 1. * * @param uniqueName * @param type * * @return property proxy from cache or <code>null</code> is not cached */ protected synchronized DirectoryProxy<?> getDirectoryProxyFromCache( String uniqueName, Class<?> type) { DirectoryProxyHolder h = directoryCache.get(AbstractProxyHolder.toID(uniqueName,type)); if (h == null) { return null; } h.count++; return h.proxy; } /** * Stores PropertyProxy in cache with use count set to 1. * * @param proxy */ protected synchronized void putPropertyProxyToCache(PropertyProxy<?,?> proxy) { PropertyProxyHolder h = new PropertyProxyHolder(proxy); propertyCache.put(h.id, h); if (!propertyCache.containsKey(proxy.getUniqueName())) { propertyCache.put(proxy.getUniqueName(), h); } } /** * Stores PropertyProxy in cache with use count set to 1. * * @param proxy */ protected synchronized void putDirectoryProxyToCache(DirectoryProxy<?> proxy) { DirectoryProxyHolder h = new DirectoryProxyHolder(proxy); directoryCache.put(h.id, h); if (!directoryCache.containsKey(proxy.getUniqueName())) { directoryCache.put(proxy.getUniqueName(), h); } } /** * Reduces use count for DirectoryProxy for one and removes it form * cache if use count is 0. * * @param proxy * * @return <code>true</code> if cache does not hold any reference to proxy. * This can happend if proxy was not present in cache or proxy * use count was reduced to 0. */ protected synchronized boolean releaseDirectoryProxyFromCache( DirectoryProxy<?> proxy) { DirectoryProxyHolder h = directoryCache.get(AbstractProxyHolder.toID(proxy)); if (h == null) { return true; } h.count--; if (h.count <= 0) { directoryCache.remove(h.id); if (directoryCache.get(proxy.getUniqueName())==h) { directoryCache.remove(proxy.getUniqueName()); } return true; } return false; } /** * Reduces use count for PropertyProxy for one and removes it form * cache if use count is 0. * * @param proxy * * @return <code>true</code> if cache does not hold any reference to proxy. * This can happend if proxy was not present in cache or proxy * use count was reduced to 0. */ protected synchronized boolean releasePropertyProxyFromCache( PropertyProxy<?,?> proxy) { PropertyProxyHolder h = propertyCache.get(AbstractProxyHolder.toID(proxy)); if (h == null) { return true; } h.count--; if (h.count <= 0) { propertyCache.remove(h.id); if (propertyCache.get(proxy.getUniqueName())==h) { propertyCache.remove(proxy.getUniqueName()); } return true; } return false; } /** * Returns proxy object for unique name. Object is obtained by * procedure in following order: * <ol> * <li>Internal cache of used objects is checked if proxy * with this name already exists.</li> * <li>Internal cache of object no longer used but not yet * deleted is checked if proxy with this name already exists.</li> * <li>Only if upper methods fails, new proxy is created * and cached in used queue.</li> * </ol> * <p>For returned proxy use count in increased for one.</p> * * @param uniqueName an unique name of remote object * * @return reused or new proxy object representing unique name * * @throws ConnectionException if instantiation of object fails */ public DeviceProxy<?> getDeviceProxy(String uniqueName) throws ConnectionException { return getDeviceProxy(uniqueName, DeviceProxy.class); } /** * Returns proxy object for unique name. Object is obtained by * procedure in following order: * <ol> * <li>Internal cache of used objects is checked if proxy * with this name already exists.</li> * <li>Internal cache of object no longer used but not yet * deleted is checked if proxy with this name already exists.</li> * <li>Only if upper methods fails, new proxy is created * and cached in used queue.</li> * </ol> * <p>For returned proxy use count in increased for one.</p> * * @param <TT> exact devyce proxy type * @param uniqueName an unique name of remote object * @param type class of property proxy, if <code>PropertyProxy.class</code> * than plug implementation decide which proxy will be returned. * * @return reused or new proxy object representing unique name * * @throws ConnectionException if instantiation of object fails * @throws NullPointerException if unique name parameter is null */ public <TT extends DeviceProxy<?>> TT getDeviceProxy(String uniqueName, Class<TT> type) throws ConnectionException { if (uniqueName == null) { throw new NullPointerException("uniqueName"); } DeviceProxy<?> p; if (DeviceProxy.class.equals(type)) { p= getDeviceProxyFromCache(uniqueName); } else { p= getDeviceProxyFromCache(uniqueName,type); } if (p != null) { if (!type.isInstance(p)) { throw new ConnectionException(this, "DeviceProxy in cache for '" + uniqueName + "' is of type '" + p.getClass().getName() + "' and not of expected type '" + type.getName() + "'."); } return type.cast(p); } TT pp = createNewDeviceProxy(uniqueName, type); putDeviceProxyToCache(pp); return pp; } /** * Returns DeviceProxy from cache. Warning: this operation * increases use count by 1. * * @param uniqueName * * @return Device proxy from cache or <code>null</code> is not cached */ protected synchronized DeviceProxy<?> getDeviceProxyFromCache( String uniqueName) { DeviceProxyHolder h = deviceCache.get(uniqueName); if (h == null) { return null; } h.count++; return h.proxy; } /** * Returns DeviceProxy from cache. Warning: this operation * increases use count by 1. * * @param uniqueName * @param type * * @return Device proxy from cache or <code>null</code> is not cached */ protected synchronized DeviceProxy<?> getDeviceProxyFromCache( String uniqueName, Class<?> type) { DeviceProxyHolder h = deviceCache.get(AbstractProxyHolder.toID(uniqueName,type)); if (h == null) { return null; } h.count++; return h.proxy; } /** * Stores DeviceProxy in cache with use count set to 1. * * @param proxy */ protected synchronized void putDeviceProxyToCache(DeviceProxy<?> proxy) { DeviceProxyHolder h = new DeviceProxyHolder(proxy); deviceCache.put(h.id, h); if (!deviceCache.containsKey(proxy.getUniqueName())) { deviceCache.put(proxy.getUniqueName(), h); } } /** * Reduces use count for DeviceProxy for one and removes it form * cache if use count is 0. * * @param proxy * * @return <code>true</code> if cache does not hold any reference to proxy. * This can happend if proxy was not present in cache or proxy * use count was reduced to 0. */ protected synchronized boolean releaseDeviceProxyFromCache( DeviceProxy<?> proxy) { DeviceProxyHolder h = deviceCache.get(AbstractProxyHolder.toID(proxy)); if (h == null) { return true; } h.count--; if (h.count <= 0) { deviceCache.remove(h.id); if (deviceCache.get(proxy.getUniqueName())==h) { deviceCache.remove(proxy.getUniqueName()); } return true; } return false; } /** * Returns <code>true</code> if proxy caching is enabled for this * plug. * * @return <code>true</code> if proxy caching is enabled for this plug */ public boolean isCachingEnabled() { return cachingEnabled; } /** * Returns the class that implements property interface given as parameter. * * @param type property type (can be null) * @param propertyName property name * * @return implementor of given interface * @throws RemoteException if remote request was issued and did not succeed */ public Class<?extends SimpleProperty<?>> getPropertyImplementationClass( Class<?extends SimpleProperty<?>> type, String propertyName) throws RemoteException { Class<?extends SimpleProperty<?>> propImplClass = null; if (type !=null) { /* * Try first with internal override for implementations */ propImplClass = propertyImplementationClasses.get(type); if (propImplClass != null && propImplClass.isAssignableFrom(type)) { throw new RemoteException(this,"Property implementation type '" + propImplClass.getName() + "' loaded from the internal cache does not implement the requested type '" + type.getName() +"'."); } /* * Try default llookup, which will return DAL default implementation classes for defautl DAL Property interfaces. */ if (propImplClass == null) { propImplClass = PropertyUtilities.getImplementationClass(type); } } if (propImplClass == null) { /* * Resolve implementation class according to remote name. This is strongly plug implementation dependant. */ propImplClass = getPropertyImplementationClass(propertyName); if (propImplClass != null && propImplClass.isAssignableFrom(type)) { throw new RemoteException(this,"Property implementation type '" + propImplClass.getName() + "' returned by a remote call for the property '" + propertyName +"' does not implement the requested type"+ (type != null ? "'" + type.getName() + "'.": ".")); } } return propImplClass; } /** * Returns the class that implements device interface given as parameter. * * @param type * @return implementor of given interface * @throws RemoteException if remote request was issued and did not succeed */ public Class<?extends AbstractDevice> getDeviceImplementationClass( Class<?extends AbstractDevice> type, String deviceName) throws RemoteException { Class<?extends AbstractDevice> deviceImplClass = null; if (type !=null) { deviceImplClass = deviceImplementationClasses.get(type); if (deviceImplClass != null && deviceImplClass.isAssignableFrom(type)) { throw new RemoteException(this,"Device implementation type '" + deviceImplClass.getName() + "' loaded from the internal cache does not implement the requested type '" + type.getName() +"'."); } } if (deviceImplClass == null) { deviceImplClass = getDeviceImplementationClass(deviceName); if (deviceImplClass != null && deviceImplClass.isAssignableFrom(type)) { throw new RemoteException(this,"Device implementation type '" + deviceImplClass.getName() + "' returned by a remote call for device '" + deviceName + "' does not implement the requested type" + (type != null ? "'" + type.getName() + "'.": ".")); } } return deviceImplClass; } /** * Returns device implementation class * * @param uniqueDeviceName device name * @return device implementation class */ protected abstract Class<?extends AbstractDevice> getDeviceImplementationClass (String uniqueDeviceName); /** * Returns the class that implements property proxy that can be connected to the given * interface of a property. * * By default is lookup done first by type if it's not <code>null</code> then by property name. * * * @param type property type (can be <code>null</code>) * @param implementationType property implementation type (can be <code>null</code>) * @param uniquePropertyName name * * @return property proxy for the given property * @throws RemoteException if remote request was issued and was not successfull */ public Class<?extends PropertyProxy<?,?>> getPropertyProxyImplementationClass( Class<?extends SimpleProperty<?>> type, Class<?extends SimpleProperty<?>> implementationType, String uniquePropertyName) throws RemoteException { Class<? extends PropertyProxy<?,?>> proxyImplType = null; if (type !=null) { proxyImplType = propertyProxiesImplementationClasses.get(type); } if (proxyImplType == null && implementationType != null) { proxyImplType = propertyProxiesImplementationClasses.get(implementationType); } if (proxyImplType == null) { proxyImplType = getPropertyProxyImplementationClass(uniquePropertyName); } return proxyImplType; } /** * Returns the class that implements device proxy that can be connected to the given * interface of an abstrac device. * * @param type device type (can be <code>null</code>) * @param implementationType device implementation type (can be <code>null</code>) * @param uniqueDeviceName name * @throws RemoteException if remote request was issued and was not successfull * @return implementor of given interface */ public Class<?extends DeviceProxy<?>> getDeviceProxyImplementationClass( Class<?extends AbstractDevice> type, Class<?extends AbstractDevice> implementationType, String uniqueDeviceName) throws RemoteException { Class<? extends DeviceProxy<?>> proxyImplType = null; if (type !=null) { proxyImplType = deviceProxiesImplementationClasses.get(type); } if (proxyImplType == null && implementationType != null) { proxyImplType = deviceProxiesImplementationClasses.get(implementationType); } if (proxyImplType == null) { proxyImplType = getDeviceProxyImplementationClass(uniqueDeviceName); } return proxyImplType; } /** * Returns device proxy implementation class * * @param uniqueDeviceName device name * @return device proxy implementation class * @throws RemoteException if remote request was issued and was not successfull */ protected abstract Class<?extends DeviceProxy<?>> getDeviceProxyImplementationClass(String uniqueDeviceName) throws RemoteException; /** * Puts a device implementation class to a map. When calling getDeviceImplementationClass(Class T) * method, it will return the class that was put in as an <code>impl</code> parameter * to this method, where T is the type parameter of this method. * * @param type interface that should be associated with a class impl * @param impl implementation of the type interface */ public void registerDeviceImplementationClass( Class<?extends AbstractDevice> type, Class<?extends AbstractDevice> impl) { deviceImplementationClasses.put(type, impl); } /** * Puts a property implementation class to a map. When calling getPropertyImplementationClass(Class T) * method, it will return the class that was put in as an <code>impl</code> parameter * to this method, where T is the type parameter of this method. * * @param type interface that should be associated with a class impl * @param impl implementation of the type interface */ public <T> void registerPropertyImplementationClass( Class<? extends SimpleProperty<T>> type, Class<? extends SimpleProperty<T>> impl) { propertyImplementationClasses.put(type, impl); } /** * Puts a device proxy implementation class to a map. When calling * getDeviceProxyImplementationClass(Class T) method, it will return * the class that was put in as an <code>impl</code> parameter * to this method, where T is the type parameter of this method. * * @param type interface that should be associated with a class impl * @param impl device proxy implementor associated with an abstract device class */ public void registerDeviceProxyImplementationClass( Class<?extends AbstractDevice> type, Class<?extends DeviceProxy<?>> impl) { deviceProxiesImplementationClasses.put(type, impl); } /** * Puts a property proxy implementation class to a map. When calling * getPropertyProxyImplementationClass(Class T) method, it will return * the class that was put in as an <code>impl</code> parameter * to this method, where T is the type parameter of this method. * * @param type interface that should be associated with a class impl * @param impl property proxy implementor associated with a simple property class */ public <T> void registerPropertyProxyImplementationClass( Class<? extends SimpleProperty<T>> type, Class<? extends PropertyProxy<T,?>> impl) { propertyProxiesImplementationClasses.put(type, impl); } /** * Returns DeviceProxy from cache. This does not increase use count. * * @param uniqueName * * @return Device proxy from cache or <code>null</code> is not cached */ protected synchronized DeviceProxy<?> _getDeviceProxyFromCache( String uniqueName) { DeviceProxyHolder h = deviceCache.get(uniqueName); if (h == null) { return null; } return h.proxy; } /** * Returns DeviceProxy from cache. This does not increase use count. * * @param uniqueName * @param type * * @return Device proxy from cache or <code>null</code> is not cached */ protected synchronized DeviceProxy<?> _getDeviceProxyFromCache( String uniqueName, Class<?> type) { DeviceProxyHolder h = deviceCache.get(AbstractProxyHolder.toID(uniqueName, type)); if (h == null) { return null; } return h.proxy; } /** * Returns PropertyProxy from cache. This does not increase use count. * * @param uniqueName * * @return Property proxy from cache or <code>null</code> is not cached */ protected synchronized PropertyProxy<?,?> _getPropertyProxyFromCache( String uniqueName) { PropertyProxyHolder h = propertyCache.get(uniqueName); if (h == null) { return null; } return h.proxy; } /** * Returns PropertyProxy from cache. This does not increase use count. * * @param uniqueName * @param type * * @return Property proxy from cache or <code>null</code> is not cached */ protected synchronized PropertyProxy<?,?> _getPropertyProxyFromCache( String uniqueName, Class<?> type) { PropertyProxyHolder h = propertyCache.get(AbstractProxyHolder.toID(uniqueName,type)); if (h == null) { return null; } return h.proxy; } /** * Returns DirectoryProxy from cache. This does not increase use count. * * @param uniqueName * * @return Directory proxy from cache or <code>null</code> is not cached */ protected synchronized DirectoryProxy<?> _getDirectoryProxyFromCache( String uniqueName) { DirectoryProxyHolder h = directoryCache.get(uniqueName); if (h == null) { return null; } return h.proxy; } /** * Returns DirectoryProxy from cache. This does not increase use count. * * @param uniqueName * @param type * * @return Directory proxy from cache or <code>null</code> is not cached */ protected synchronized DirectoryProxy<?> _getDirectoryProxyFromCache( String uniqueName, Class<?> type) { DirectoryProxyHolder h = directoryCache.get(AbstractProxyHolder.toID(uniqueName,type)); if (h == null) { return null; } return h.proxy; } /** * <p>This method <b>MUST</b> be implemented by plug implementation if plug want to support * default behavior with shared plug instance</p>. * * <p>This method is used by default DAL factories to access plug instances. Implementation may * choose one of the following strategies how this method is implemented: * </p> * * <ul> * <li><b>Singleton plug:</b> This method always returns the same instance, thus singleton. * This means that this particular DAL implementation will always use same plug.</li> * <li><b>Multiple plugs:</b> Implementation may decide to return different plug instance. * In this case it may be wiser to declare in AbstractFactorySupport this plug to be * non-shared.</li> * </ul> * * <p> Which strategy will be used could be hard-coded in AbstractFactorySupport or dynamically * decided from application context configuration with AbstractFactory.SHARE_PLUG property.</p> * * * @param configuration Properties with configuration, whcih is provided by application context, * which initiated plug construction. * @return new or reused plug instance, depends on plug implementation strategy * @throws Exception if construction fails. * * @see AbstractFactorySupport * @see AbstractFactory#SHARE_PLUG * @see AbstractFactory#isPlugShared() * */ public static AbstractPlug getInstance(Properties configuration) throws Exception { throw new Exception( "This method MUST be implemented by this plug class, if plug instance is shared."); } /** * <p>This method <b>MUST</b> be implemented <b>ONLY</b> by plug implementation, which * does not want to share plug instance. See for more detail AbstractFactorySupport class.</p> * * <p>Purpose of this method is to provide own plug instance for each application context. * This is required if for example plug need application context to access user or application * specific resources in order to start remote protocol and this resources can not be shared * among several applications.</p> * * <p>Default DAL factory implementation (see AbstractFactorySupport) will try to create * plug instance trough this method <b>only</p> if factory has declared in * AbstractFactorySupport constructor that plug should be created only in non-shared mode or * dynamically decided from application context configuration with AbstractFactory.SHARE_PLUG * property set to <code>true</code>.</p> * * <p>Plug, which is created with this method, will not be used any more after application * context signals end-of-life-cycle with destroy event. For this reason plug must listen to * context life-cycle events and perform cleanup of any allocated resources once application * context has been destroyed.</p> * * @param context Application context which initiates plug construction * @return new plug instance created specially for provided application context * @throws Exception if construction fails. * * @see AbstractPlug#getInstance(Properties) * @see AbstractFactorySupport * @see AbstractFactory#SHARE_PLUG * @see AbstractFactory#isPlugShared() */ public static AbstractPlug getInstance(AbstractApplicationContext context) throws Exception { throw new Exception( "This method MUST be implemented by this plug class, if plug instance is not shared among applications."); } /** * This method <b>MUST</b> be implemented by plug implementation. * This method is used by default factories to destroy plug instances. Implementation may * destroy plug instance or not (if singleton stratrgy is used): * <ul> * <li> if this plug instance is nto shared (or singelton) is should destroy the plug isntance.</li> * <li> if this plug isntancce is shared (singelton) then this method must not destroy the plug instance.</li> * </ul> * @throws Exception if construction fails. */ public abstract void releaseInstance() throws Exception; /** * Return active configuration of this plug. * @return Returns the configuration. */ @Override public Properties getConfiguration() { return configuration; } /* (non-Javadoc) * @see org.csstudio.dal.EventSystemContext#addEventSystemListener(org.csstudio.dal.EventSystemListener) */ @Override public void addEventSystemListener(EventSystemListener<PlugEvent<?>> l) throws RemoteException { if (plugListeners == null) { plugListeners = new ListenerList(EventSystemListener.class); } if (!plugListeners.contains(l)) { plugListeners.add(l); } } /* (non-Javadoc) * @see org.csstudio.dal.EventSystemContext#addEventSystemListener(java.util.Map, org.csstudio.dal.EventSystemListener) */ @Override public void addEventSystemListener(EventSystemListener<PlugEvent<?>> l, Map<String, Object> parameters) throws RemoteException { if (plugListeners == null) { plugListeners = new ListenerList(EventSystemListener.class); } if (!plugListeners.contains(l)) { plugListeners.add(l); } } /* (non-Javadoc) * @see org.csstudio.dal.EventSystemContext#getEventSystemListeners() */ @Override @SuppressWarnings("unchecked") public EventSystemListener<PlugEvent<?>>[] getEventSystemListeners() { return (EventSystemListener<PlugEvent<?>>[])plugListeners.toArray(); } /* (non-Javadoc) * @see org.csstudio.dal.context.Identifiable#getIdentifier() */ @Override public Identifier getIdentifier() { if (identifier == null) { identifier = IdentifierUtilities.createIdentifier(this); } return identifier; } /* (non-Javadoc) * @see org.csstudio.dal.EventSystemContext#getSupportedParameters() */ @Override public Map<String, Object> getSupportedEventSystemParameters() { return null; } /* (non-Javadoc) * @see org.csstudio.dal.context.Identifiable#isDebug() */ @Override public boolean isDebug() { return debug; } /* (non-Javadoc) * @see org.csstudio.dal.EventSystemContext#removeEventSystemListener(org.csstudio.dal.EventSystemListener) */ @Override public void removeEventSystemListener(EventSystemListener<PlugEvent<?>> l) { if (plugListeners != null) { plugListeners.remove(l); } } /* (non-Javadoc) * @see org.csstudio.dal.EventSystemContext#removeEventSystemListener(java.util.Map, org.csstudio.dal.EventSystemListener) */ @Override public void removeEventSystemListener(EventSystemListener<PlugEvent<?>> l, Map<String, Object> parameters) { if (plugListeners != null) { plugListeners.remove(l); } } /** * Returns the Logger used by this plug. This logger logs all exceptions and * printouts within the DAL implementation. * @return */ public Logger getLogger() { if (logger == null) { logger = Plugs.getPlugLogger(getPlugType()); } return logger; } /** * Returns an array of all PropertyProxies, which are held in the cache of * this plug. * @return */ public synchronized PropertyProxy<?,?>[] getCachedPropertyProxies() { PropertyProxyHolder[] holders = propertyCache.values().toArray(new PropertyProxyHolder[propertyCache.size()]); PropertyProxy<?,?>[] proxies = new PropertyProxy[propertyCache.size()]; for (int i = 0; i < holders.length; i++) { proxies[i] = holders[i].proxy; } return proxies; } /** * Returns an array of all DeviceProxies, which are held in the cache of * this plug. * @return */ public synchronized DeviceProxy<?>[] getCachedDeviceProxies() { DeviceProxyHolder[] holders = deviceCache.values().toArray(new DeviceProxyHolder[deviceCache.size()]); DeviceProxy<?>[] proxies = new DeviceProxy[deviceCache.size()]; for (int i = 0; i < holders.length; i++) { proxies[i] = holders[i].proxy; } return proxies; } /** * Returns an array of all DirectoryProxies, which are held in the cache of * this plug. * @return */ public synchronized DirectoryProxy<?>[] getCachedDirectoryProxies() { DirectoryProxyHolder[] holders = directoryCache.values().toArray(new DirectoryProxyHolder[directoryCache.size()]); DirectoryProxy<?>[] proxies = new DirectoryProxy[directoryCache.size()]; for (int i = 0; i < holders.length; i++) { proxies[i] = holders[i].proxy; } return proxies; } /** * Returns an iterator over the cached PropertyProxies. This iterator does * not support remove of elements. * @return */ public synchronized Iterator<PropertyProxy<?,?>> cachedPropertyProxiesIterator() { final Iterator<PropertyProxyHolder> holder = propertyCache.values().iterator(); return new Iterator<PropertyProxy<?,?>>(){ @Override public boolean hasNext() { return holder.hasNext(); } @Override public PropertyProxy<?,?> next() { return holder.next().proxy; } @Override public void remove() { throw new UnsupportedOperationException("Removing PropertyProxy from cache is not allowed."); } }; } /** * Returns an iterator over the cached DeviceProxies. This iterator does * not support remove of elements. * @return */ public synchronized Iterator<DeviceProxy<?>> cachedDeviceProxiesIterator() { final Iterator<DeviceProxyHolder> holder = deviceCache.values().iterator(); return new Iterator<DeviceProxy<?>>(){ @Override public boolean hasNext() { return holder.hasNext(); } @Override public DeviceProxy<?> next() { return holder.next().proxy; } @Override public void remove() { throw new UnsupportedOperationException("Removing DeviceProxy from cache is not allowed."); } }; } /** * Returns an iterator over the cached DirectoryProxies. This iterator does * not support remove of elements. * @return */ public synchronized Iterator<DirectoryProxy<?>> cachedDirectoryProxiesIterator() { final Iterator<DirectoryProxyHolder> holder = directoryCache.values().iterator(); return new Iterator<DirectoryProxy<?>>(){ @Override public boolean hasNext() { return holder.hasNext(); } @Override public DirectoryProxy<?> next() { return holder.next().proxy; } @Override public void remove() { throw new UnsupportedOperationException("Removing DirectoryProxy from cache is not allowed."); } }; } } /* __oOo__ */