/*
* 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.lang.reflect.InvocationTargetException;
import org.apache.log4j.Logger;
import org.csstudio.dal.DynamicValueProperty;
import org.csstudio.dal.RemoteException;
import org.csstudio.dal.SimpleProperty;
import org.csstudio.dal.context.AbstractApplicationContext;
import org.csstudio.dal.context.Connectable;
import org.csstudio.dal.context.ConnectionException;
import org.csstudio.dal.context.ConnectionListener;
import org.csstudio.dal.context.ConnectionState;
import org.csstudio.dal.context.DeviceFamily;
import org.csstudio.dal.context.LinkBlocker;
import org.csstudio.dal.context.PropertyContext;
import org.csstudio.dal.proxy.AbstractPlug;
import org.csstudio.dal.proxy.DirectoryProxy;
import org.csstudio.dal.proxy.PropertyProxy;
import org.csstudio.dal.proxy.Proxy;
import org.csstudio.dal.simple.RemoteInfo;
import org.csstudio.dal.spi.DefaultDeviceFactoryService;
import org.csstudio.dal.spi.DeviceFactory;
import org.csstudio.dal.spi.Plugs;
import com.cosylab.util.CommonException;
/**
* <code>DeviceBean</code> is implementation of <code>AbstractDevice</code>
* whih implements <code>ContextBean</code> and <code>Connectable</code>
* interface. This means that in contrast to AbstractDevice this device is not
* only created by factory, but can be also created as JavaBean: with
* contructor without parameters and initialized later.
*
* @author Igor Kriznar (igor.kriznarATcosylab.com)
*/
public class DeviceBean extends AbstractDeviceImpl implements Connectable
{
protected AbstractApplicationContext ctx;
protected RemoteInfo rinfo;
protected DeviceFactory deviceFactory;
private boolean autoConnect = true;
/**
* Creates new named instance of device bean.
*/
public DeviceBean()
{
super(null,null);
}
/**
* Creates new named instance of device bean.
* @param name device name
*/
public DeviceBean(String name, DeviceFamily deviceFamily)
{
super(name,deviceFamily);
}
/* (non-Javadoc)
* @see org.csstudio.dal.context.Connectable#addConnectionListener(org.csstudio.dal.context.ConnectionListener)
*/
@Override
public void addConnectionListener(ConnectionListener l)
{
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented yet.");
}
/* (non-Javadoc)
* @see org.csstudio.dal.context.Connectable#asyncConnect()
*/
@Override
public synchronized void asyncConnect()
throws ConnectionException, IllegalStateException
{
checkInitialized();
if (!connectionStateMachine.isTransitionAllowed(ConnectionState.CONNECTING)) {
throw new IllegalStateException("Connectable '" + getUniqueName()
+ "' is not in DISCONNECTED or READY state but in "
+ getConnectionState() + ".");
}
setConnectionState(ConnectionState.CONNECTING);
deviceFactory.reconnectDevice(this);
reinitializePropertyProxies();
}
/* (non-Javadoc)
* @see org.csstudio.dal.context.Connectable#connect()
*/
@Override
public void connect() throws ConnectionException, IllegalStateException
{
asyncConnect();
LinkBlocker.blockUntillConnected(this,
Plugs.getConnectionTimeout(ctx.getConfiguration(),30000), true);
}
/* (non-Javadoc)
* @see org.csstudio.dal.context.Connectable#destroy()
*/
@Override
public void destroy()
{
if (getConnectionState() == ConnectionState.DESTROYED) return; // TODO throw an exception?
if (deviceFactory != null && deviceFactory.getDeviceFamily() != null)
deviceFactory.getDeviceFamily().destroy(this);
}
/* (non-Javadoc)
* @see org.csstudio.dal.context.Connectable#disconnect()
*/
@Override
public void disconnect()
{
if (!connectionStateMachine.isTransitionAllowed(ConnectionState.DISCONNECTING)) {
throw new IllegalStateException("Connectable '" + getUniqueName()
+ "' is not in CONNECTED or CONNECTING state but in "
+ getConnectionState() + ".");
}
setConnectionState(ConnectionState.DISCONNECTING);
Proxy[] proxy= releaseProxy(false);
if (proxy != null && proxy[0]!=null) {
((AbstractPlug)deviceFactory.getPlug()).releaseProxy(proxy[0]);
}
if (proxy != null && proxy[1]!=null && proxy[1]!=proxy[0]) {
((AbstractPlug)deviceFactory.getPlug()).releaseProxy(proxy[1]);
}
setConnectionState(ConnectionState.DISCONNECTED);
}
//TODO override refresh method to also call refresh on all properties? like Abean.refresh?
/* (non-Javadoc)
* @see org.csstudio.dal.context.Connectable#getRemoteInfo()
*/
@Override
public org.csstudio.dal.simple.RemoteInfo getRemoteInfo()
{
return rinfo;
}
/* (non-Javadoc)
* @see org.csstudio.dal.context.Connectable#removeConnectionListener(org.csstudio.dal.context.ConnectionListener)
*/
@Override
public void removeConnectionListener(ConnectionListener l)
{
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see org.csstudio.dal.context.Connectable#setRemoteInfo(org.csstudio.dal.context.RemoteInfo)
*/
@Override
public void setRemoteInfo(org.csstudio.dal.simple.RemoteInfo rinfo) throws IllegalArgumentException
{
if (getConnectionState() != ConnectionState.DISCONNECTED
&& getConnectionState() != ConnectionState.READY
&& getConnectionState() != ConnectionState.INITIAL) {
throw new IllegalStateException("Connectable '" + getUniqueName()
+ "' is not in DISCONNECTED, INITIAL or READY state but in "
+ getConnectionState() + ".");
}
if (rinfo == null) {
this.rinfo = null;
this.name = null;
setConnectionState(ConnectionState.INITIAL);
return;
}
this.rinfo = rinfo;
this.name = rinfo.getRemoteName();
if (ctx!=null && deviceFactory!=null && rinfo!=null) {
setConnectionState(ConnectionState.READY);
}
tryConnect();
}
/* (non-Javadoc)
* @see org.csstudio.dal.context.ContextBean#getApplicationContext()
*/
@Override
public AbstractApplicationContext getApplicationContext()
{
return ctx;
}
/* (non-Javadoc)
* @see org.csstudio.dal.context.ContextBean#initialize(org.csstudio.dal.context.AbstractApplicationContext)
*/
@Override
public void initialize(AbstractApplicationContext ctx)
throws CommonException
{
// TODO: at some point this initialization must be changed.
// device factory should be obtained only after RemoteInfo is set and correct plug type is known,
// now it is used default plug tipe from ctx
deviceFactory = DefaultDeviceFactoryService.getDeviceFactoryService()
.getDeviceFactory(ctx, null);
if (deviceFactory == null) {
throw new CommonException(this, "Failed to create device factory");
}
this.ctx = ctx;
if (ctx!=null && deviceFactory!=null && rinfo!=null) {
setConnectionState(ConnectionState.READY);
}
tryConnect();
}
/**
* Performs initialization the other way around.
*/
public void initialize(DeviceFactory devFact){
this.deviceFactory = devFact;
this.ctx = devFact.getApplicationContext();
if (ctx!=null && deviceFactory!=null && rinfo!=null) {
setConnectionState(ConnectionState.READY);
}
tryConnect();
}
/**
* Checks if device is properly initialized.
*
* @throws IllegalStateException if it is not initialized
*
* @see #initialize(AbstractApplicationContext)
*/
protected void checkInitialized() throws IllegalStateException
{
if (deviceFactory == null) {
throw new IllegalStateException("Device '" + getUniqueName()
+ "' is not initialized.");
}
}
/**
* Reinitializes all already existing properties. Usually only happens when device is initialized
* second time.
*/
protected void reinitializePropertyProxies(){
if (properties != null){
String[] pns = properties.keySet().toArray(new String[0]);
for (int i=0; i<pns.length; i++){
try {
PropertyProxy pp = deviceProxy.getPropertyProxy(pns[i]);
DirectoryProxy dp = deviceProxy.getDirectoryProxy(pns[i]);
DynamicValuePropertyImpl<?> prop = ((DynamicValuePropertyImpl<?>)getProperty(pns[i]));
prop.initialize(pp, dp);
prop.refresh();
} catch (Exception e) {
Logger.getLogger(DeviceBean.class).warn("Problem on re-inizializing property " + pns[i]+".",e);
}
}
}
}
@Override
protected DynamicValueProperty<?> createProperty(String name)
throws RemoteException, IllegalAccessException, InstantiationException,
InvocationTargetException, NoSuchMethodException
{
Class<? extends SimpleProperty<?>> type = getPropertyType(name);
// Creates property implementation
Class<?> impClass = PropertyUtilities.getImplementationClass(type);
DynamicValuePropertyImpl<?> property = (DynamicValuePropertyImpl<?>)impClass.getConstructor(String.class,
PropertyContext.class).newInstance(name, this);
if (deviceProxy != null){
PropertyProxy pp = deviceProxy.getPropertyProxy(name);
DirectoryProxy dp = deviceProxy.getDirectoryProxy(name);
property.initialize(pp, dp);
}
property.addPropertyChangeListener(propertyInterceptor);
// if (deviceProxy != null){
// if (property.getConnectionState() != ConnectionState.CONNECTED) {
// Logger.getLogger(DeviceBean.class.getName()).info("Property '" + name +"' is not connected. Waiting for connection to be established...");
// }
// LinkBlocker.blockUntillConnected(property,Plugs.getConnectionTimeout(null, 30000) * 2, true);
// }
return property;
}
@SuppressWarnings("unchecked")
protected Class<? extends SimpleProperty<?>> getPropertyType(String name){
return (Class<? extends SimpleProperty<?>>)DynamicValueProperty.class;
}
/**
* Sets connection state and fires event.
*
* @param s new state
*/
@Override
protected void setConnectionState(ConnectionState s)
{
super.setConnectionState(s);
// TODO: should fire event here to connection listeners
}
/**
* @see {@link #setAutoConnect(boolean)}
*/
@Override
public boolean isAutoConnect() {
return autoConnect;
}
/**
* If autoConnect is true, Device is automatically connected when all requirements
* are provided (RemoteInfo, DeviceFactory). Default value is <code>true</code>.
*/
@Override
public void setAutoConnect(boolean autoConnect) {
this.autoConnect = autoConnect;
tryConnect();
}
protected void tryConnect(){
if (autoConnect && (getConnectionState() == ConnectionState.READY || getConnectionState() == ConnectionState.DISCONNECTED ) && deviceFactory != null)
try {
asyncConnect();
} catch (Exception e) {
deviceFactory.getPlug().getLogger().info("'"+getUniqueName()+"' failed to autoconnect.",e);
}
}
}
/* __oOo__ */