/*
* 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.impl;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.csstudio.dal.DataAccess;
import org.csstudio.dal.DataExchangeException;
import org.csstudio.dal.DynamicValueCondition;
import org.csstudio.dal.DynamicValueEvent;
import org.csstudio.dal.DynamicValueListener;
import org.csstudio.dal.DynamicValueMonitor;
import org.csstudio.dal.IllegalViewException;
import org.csstudio.dal.RemoteException;
import org.csstudio.dal.SimpleProperty;
import org.csstudio.dal.StringAccess;
import org.csstudio.dal.Timestamp;
import org.csstudio.dal.context.Identifier;
import org.csstudio.dal.context.IdentifierUtilities;
import org.csstudio.dal.proxy.DirectoryProxy;
import org.csstudio.dal.proxy.MonitorProxy;
import org.csstudio.dal.proxy.PropertyProxy;
import org.csstudio.dal.proxy.Proxy;
import org.csstudio.dal.simple.ChannelListener;
import org.csstudio.dal.simple.impl.ChannelListenerNotifier;
import com.cosylab.util.ListenerList;
/**
* Simple property implementation
*
* @author Igor Kriznar (igor.kriznarATcosylab.com)
*
*/
public abstract class SimplePropertyImpl<T> extends DataAccessImpl<T>
implements SimpleProperty<T>
{
/* protected Hashtable<Class<? extends DataAccess>,Class<? extends DataAccess> > dataAccessTypes =
new Hashtable<Class<? extends DataAccess>,Class<? extends DataAccess> >();
*/
protected Hashtable<Class<? extends DataAccess<?>>,Class<? extends DataAccess<?>>> dataAccessTypes = new Hashtable<Class<? extends DataAccess<?>>,Class<? extends DataAccess<?>>>();
protected DirectoryProxy<?> directoryProxy;
protected MonitorProxyWrapper<T, ? extends SimpleProperty<T>> defaultMonitor;
protected Set<MonitorProxyWrapper<T, ? extends SimpleProperty<T>>> monitors = new HashSet<MonitorProxyWrapper<T, ? extends SimpleProperty<T>>>();
protected ListenerList propertyListener = new ListenerList(PropertyChangeListener.class);
protected String name;
protected Identifier identifier;
protected DynamicValueCondition condition;
protected Timestamp lastValueChangeTimestamp;
protected Timestamp lastValueUpdateTimestamp;
protected boolean lastValueSuccess=true;
// AnyDataChannel methods...
private ChannelListenerNotifier chListeners;
/**
* Creates a new SimplePropertyImpl object.
*
* @param valClass value datatype class
* @param name property name
*/
public SimplePropertyImpl(Class<T> valClass, String name)
{
super(valClass);
dataAccessTypes.put(StringAccess.class, StringDataAccessWrapper.class);
this.name = name;
}
/* (non-Javadoc)
* @see org.csstudio.dal.SimpleProperty#getAccessTypes()
*/
@Override
@SuppressWarnings("unchecked")
public Class<? extends DataAccess<?>>[] getAccessTypes()
{
Class<? extends DataAccess<?>>[] daTypes = (Class<? extends DataAccess<?>>[])new Class<?>[dataAccessTypes
.size()];
dataAccessTypes.keySet().toArray(daTypes);
return daTypes;
}
public void addDataAccessType(Class<? extends DataAccess<?>> dataAccessType,
Class<? extends AbstractDataAccessWrapper<?>> implementation)
{
dataAccessTypes.put(dataAccessType, implementation);
}
public void removeDataAccessType(Class<?extends DataAccess<?>> dataAccessType)
{
dataAccessTypes.remove(dataAccessType);
}
/* (non-Javadoc)
* @see org.csstudio.dal.SimpleProperty#getCondition()
*/
@Override
public DynamicValueCondition getCondition()
{
if (condition == null && proxy == null) throw new IllegalStateException("Proxy is null");
return condition != null ? condition : proxy.getCondition();
}
/* (non-Javadoc)
* @see org.csstudio.dal.SimpleProperty#getDataAccess(java.lang.Class)
*/
@Override
public <D extends DataAccess<?>> D getDataAccess(Class<D> type)
throws IllegalViewException
{
if (type.isAssignableFrom(this.getClass())) {
return type.cast(this);
}
Class<? extends DataAccess<?>> implClass = dataAccessTypes.get(type);
if (implClass != null) {
try {
return (D)implClass.getConstructor(DataAccess.class)
.newInstance(this);
} catch (Exception e) {
throw new IllegalViewException(this,
"Unable to instantiate: " + implClass.toString());
}
}
return null;
}
/* (non-Javadoc)
* @see org.csstudio.dal.SimpleProperty#getDefaultDataAccess()
*/
@Override
public DataAccess<T> getDefaultDataAccess()
{
return this;
}
/* (non-Javadoc)
* @see org.csstudio.dal.SimpleProperty#getDescription()
*/
@Override
public String getDescription() throws DataExchangeException
{
if (directoryProxy == null || !directoryProxy.getConnectionState().isConnected())
throw new DataExchangeException(this,"Directory proxy is not connected");
return (String)directoryProxy.getCharacteristic(C_DESCRIPTION);
}
/* (non-Javadoc)
* @see org.csstudio.dal.SimpleProperty#getUniqueName()
*/
@Override
public String getUniqueName()
{
if (proxy == null) throw new IllegalStateException("Proxy is null");
return proxy.getUniqueName();
}
/* (non-Javadoc)
* @see org.csstudio.dal.SimpleProperty#isTimelag()
*/
@Override
public boolean isTimelag()
{
if (condition != null) {
return condition.isTimelag();
} else if (defaultMonitor != null) {
return defaultMonitor.isTimelag();
} else {
return false;
}
}
/* (non-Javadoc)
* @see org.csstudio.dal.SimpleProperty#isTimeout()
*/
@Override
public boolean isTimeout()
{
if (condition != null) {
return condition.isTimeout();
} else if (defaultMonitor != null) {
return defaultMonitor.isTimeout();
} else {
return false;
}
}
/* (non-Javadoc)
* @see org.csstudio.dal.CharacteristicContext#addPropertyChangeListener(java.beans.PropertyChangeListener)
*/
@Override
public void addPropertyChangeListener(PropertyChangeListener l)
{
propertyListener.add(l);
}
/* (non-Javadoc)
* @see org.csstudio.dal.CharacteristicContext#getCharacteristic(java.lang.String)
*/
@Override
public Object getCharacteristic(String name) throws DataExchangeException
{
if (directoryProxy == null || !directoryProxy.getConnectionState().isConnected())
throw new DataExchangeException(this,"Directory proxy is not connected");
return directoryProxy.getCharacteristic(name);
}
/* (non-Javadoc)
* @see org.csstudio.dal.CharacteristicContext#getCharacteristicNames()
*/
@Override
public String[] getCharacteristicNames() throws DataExchangeException
{
if (directoryProxy == null || !directoryProxy.getConnectionState().isConnected())
throw new DataExchangeException(this,"Directory proxy is not connected");
return directoryProxy.getCharacteristicNames();
}
/* (non-Javadoc)
* @see org.csstudio.dal.CharacteristicContext#getCharacteristics(java.lang.String[])
*/
@Override
public Map<String,Object> getCharacteristics(String[] names) throws DataExchangeException
{
if (directoryProxy == null || !directoryProxy.getConnectionState().isConnected())
throw new DataExchangeException(this,"Directory proxy is not connected");
// TODO: implement wrapper listener
//w= new Wrapper...(names);
//dirProxy.getCharacteristics(names, );
//return w.getCharacteristics();
// TODO : this is dummy implementation, improve it
Map<String,Object> ch = new HashMap<String,Object>(names.length + 1);
for (int i = 0; i < names.length; i++) {
Object o = directoryProxy.getCharacteristic(names[i]);
if (o != null) {
ch.put(names[i], o);
}
}
return ch;
}
/* (non-Javadoc)
* @see org.csstudio.dal.CharacteristicContext#getPropertyChangeListeners()
*/
@Override
public PropertyChangeListener[] getPropertyChangeListeners()
{
return (PropertyChangeListener[])propertyListener.toArray();
}
/* (non-Javadoc)
* @see org.csstudio.dal.CharacteristicContext#removePropertyChangeListener(java.beans.PropertyChangeListener)
*/
@Override
public void removePropertyChangeListener(PropertyChangeListener l)
{
propertyListener.remove(l);
}
/* (non-Javadoc)
* @see org.csstudio.dal.ValueUpdateable#createNewMonitor(org.csstudio.dal.DynamicValueListener)
*/
@Override
public <E extends SimpleProperty<T>> DynamicValueMonitor createNewMonitor(DynamicValueListener<T, E> listener)
throws RemoteException
{
if (proxy == null) throw new IllegalStateException("Proxy is null");
MonitorProxyWrapper<T, E> mpw = new MonitorProxyWrapper<T, E>((E) this, listener);
MonitorProxy mp = null;
mp = proxy.createMonitor(mpw,null);
mpw.initialize(mp);
synchronized (monitors) {
monitors.add(mpw);
}
return mpw;
}
/* (non-Javadoc)
* @see org.csstudio.dal.ValueUpdateable#getDefaultMonitor()
*/
@Override
public synchronized DynamicValueMonitor getDefaultMonitor()
{
if (defaultMonitor == null && proxy!=null && proxy.getConnectionState().isConnected()) {
defaultMonitor = new MonitorProxyWrapper<T, SimpleProperty<T>>(this, getDvListeners());
try {
MonitorProxy mp = proxy.createMonitor(defaultMonitor,null);
defaultMonitor.initialize(mp);
synchronized (monitors) {
monitors.add(defaultMonitor);
}
} catch (Exception e) {
Logger.getLogger(this.getClass()).fatal("Failed to create default monitor: "+e.getMessage(), e);
if (defaultMonitor!=null) {
defaultMonitor.destroy();
defaultMonitor=null;
}
}
}
return defaultMonitor;
}
/* (non-Javadoc)
* @see org.csstudio.dal.DataAccess#addDynamicValueListener(org.csstudio.dal.DynamicValueListener)
*/
@Override
public <P extends SimpleProperty<T>> void addDynamicValueListener(DynamicValueListener<T, P> l)
{
super.addDynamicValueListener(l);
if (defaultMonitor == null) {
getDefaultMonitor();
}
if (proxy != null){
DynamicValueEvent<T, P> e = new DynamicValueEvent<T, P>(
this, (P)this, lastValue, getCondition(),
lastValueUpdateTimestamp, "Initial update.");
l.conditionChange(e);
if (lastValue != null && lastValueSuccess) {
l.valueChanged(e);
}
}
}
/* (non-Javadoc)
* @see org.csstudio.dal.DataAccess#removeDynamicValueListener(org.csstudio.dal.DynamicValueListener)
*/
@Override
public <P extends SimpleProperty<T>> void removeDynamicValueListener(DynamicValueListener<T, P> l)
{
super.removeDynamicValueListener(l);
}
/* (non-Javadoc)
* @see org.csstudio.dal.ValueUpdateable#getLatestValueChangeTimestamp()
*/
@Override
public Timestamp getLatestValueChangeTimestamp()
{
return lastValueChangeTimestamp;
}
/* (non-Javadoc)
* @see org.csstudio.dal.ValueUpdateable#getLatestValueSuccess()
*/
@Override
public boolean getLatestValueSuccess()
{
return lastValueSuccess;
}
/* (non-Javadoc)
* @see org.csstudio.dal.ValueUpdateable#getLatestValueUpdateTimestamp()
*/
@Override
public Timestamp getLatestValueUpdateTimestamp()
{
return lastValueUpdateTimestamp;
}
/* (non-Javadoc)
* @see org.csstudio.dal.impl.DataAccessImpl#initialize(org.csstudio.dal.proxy.PropertyProxy)
*/
public void initialize(PropertyProxy<T,?> proxy, DirectoryProxy<?> dirProxy)
{
super.initialize(proxy);
this.directoryProxy = dirProxy;
MonitorProxyWrapper<T,?>[] mm= getMonitorWrappers();
for (int i=0;i<mm.length;i++){
try {
MonitorProxy mp = proxy.createMonitor(mm[i],null);
mm[i].initialize(mp);
} catch (Exception e) {
Logger.getLogger(SimplePropertyImpl.class).warn("Problem on re-inizializing monitor on property"+getName()+".",e);
}
}
// catch the identifier
getIdentifier();
}
/* (non-Javadoc)
* @see org.csstudio.dal.SimpleProperty#getName()
*/
@Override
public String getName()
{
if (name == null) {
if (proxy == null) throw new IllegalStateException("Proxy is null");
return proxy.getUniqueName();
}
return name;
}
/* (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.context.Identifiable#isDebug()
*/
@Override
public boolean isDebug()
{
if (proxy == null) throw new IllegalStateException("Proxy is null");
return proxy.isDebug();
}
/**
* TODO must be fired when characteristics values change or characteristics
* are added or removed
* @param e
*/
protected void firePropertyChangeEvent(PropertyChangeEvent e)
{
PropertyChangeListener[] l= (PropertyChangeListener[])propertyListener.toArray();
for (int i = 0; i < l.length; i++) {
try {
l[i].propertyChange(e);
} catch (Exception ex) {
Logger.getLogger(this.getClass()).error("Exception in event handler, continuing.", ex);
}
}
}
/**
* Returns the DirectoryProxy which describes characteristics of the remote
* connection associated with this property.
*
* @return the directory proxy
*/
public DirectoryProxy<?> getDirectoryProxy() {
return directoryProxy;
}
void updateLastValueCache(T lastValue, Timestamp lastUpdate, boolean sucess, boolean change) {
if (lastValue!=null) {
this.lastValue=lastValue;
}
if (change) {
this.lastValueChangeTimestamp=lastUpdate;
}
this.lastValueUpdateTimestamp=lastUpdate;
this.lastValueSuccess=true;
}
/* (non-Javadoc)
* @see org.csstudio.dal.impl.DataAccessImpl#releaseProxy(boolean)
*/
@Override
public Proxy<?>[] releaseProxy(boolean destroy) {
MonitorProxyWrapper<T,?>[] mm= getMonitorWrappers();
for (MonitorProxyWrapper<T,?> monitor : mm) {
monitor.releaseProxy(destroy);
}
Proxy<?>[] tmp = new Proxy[]{super.releaseProxy(destroy)[0],directoryProxy};
directoryProxy=null;
if (destroy) {
synchronized (monitors) {
monitors.clear();
}
propertyListener.clear();
}
return tmp;
}
@Override
public ChannelListener[] getListeners() {
return getChListeners().getChannelListeners();
}
private ChannelListenerNotifier getChListeners() {
if (chListeners == null) {
synchronized (this) {
if (chListeners == null) {
chListeners = new ChannelListenerNotifier(this);
}
}
}
return chListeners;
}
@Override
public void addListener(ChannelListener listener) {
getChListeners().addChannelListener(listener);
}
@Override
public void removeListener(ChannelListener listener) {
getChListeners().removeChannelListener(listener);
}
@Override
public DynamicValueMonitor[] getMonitors() {
synchronized (monitors) {
return monitors.toArray(new DynamicValueMonitor[monitors.size()]);
}
}
@SuppressWarnings("unchecked")
protected MonitorProxyWrapper<T,?>[] getMonitorWrappers() {
synchronized (monitors) {
return monitors.toArray(new MonitorProxyWrapper[monitors.size()]);
}
}
void removeMonitor(MonitorProxyWrapper<?,?> mon) {
synchronized (monitors) {
monitors.remove(mon);
}
}
}