/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.dispatch;
import java.util.NoSuchElementException;
import org.ws4d.java.DPWSFramework;
import org.ws4d.java.communication.CommunicationManager;
import org.ws4d.java.communication.DefaultResponseCallback;
import org.ws4d.java.communication.Discovery;
import org.ws4d.java.communication.ProtocolData;
import org.ws4d.java.communication.TimeoutException;
import org.ws4d.java.configuration.DispatchingProperties;
import org.ws4d.java.constants.DPWSMessageConstants;
import org.ws4d.java.constants.WSDConstants;
import org.ws4d.java.constants.WSSecurityConstants;
import org.ws4d.java.dispatch.DeviceListenerQueue.DeviceEvent;
import org.ws4d.java.message.FaultMessage;
import org.ws4d.java.message.Message;
import org.ws4d.java.message.discovery.ByeMessage;
import org.ws4d.java.message.discovery.HelloMessage;
import org.ws4d.java.message.discovery.ProbeMatch;
import org.ws4d.java.message.discovery.ProbeMatchesMessage;
import org.ws4d.java.message.discovery.ProbeMessage;
import org.ws4d.java.message.discovery.ResolveMatchesMessage;
import org.ws4d.java.message.discovery.ResolveMessage;
import org.ws4d.java.message.metadata.GetMessage;
import org.ws4d.java.message.metadata.GetResponseMessage;
import org.ws4d.java.service.Device;
import org.ws4d.java.service.LocalDevice;
import org.ws4d.java.service.ProxyFactory;
import org.ws4d.java.service.reference.DeviceListener;
import org.ws4d.java.service.reference.DeviceReference;
import org.ws4d.java.structures.AppSequenceTracker;
import org.ws4d.java.structures.DataStructure;
import org.ws4d.java.structures.EmptyStructures;
import org.ws4d.java.structures.HashMap;
import org.ws4d.java.structures.HashSet;
import org.ws4d.java.structures.Iterator;
import org.ws4d.java.structures.LockedMap;
import org.ws4d.java.structures.ReadOnlyIterator;
import org.ws4d.java.types.AppSequence;
import org.ws4d.java.types.AttributedURI;
import org.ws4d.java.types.DiscoveryData;
import org.ws4d.java.types.EndpointReference;
import org.ws4d.java.types.HostMData;
import org.ws4d.java.types.QName;
import org.ws4d.java.types.QNameSet;
import org.ws4d.java.types.ScopeSet;
import org.ws4d.java.types.URI;
import org.ws4d.java.types.URISet;
import org.ws4d.java.types.XAddressInfo;
import org.ws4d.java.types.XAddressInfoSet;
import org.ws4d.java.util.Log;
import org.ws4d.java.util.TimedEntry;
import org.ws4d.java.util.WatchDog;
/**
* Class holds listeners of device reference. Also manages creating and
* disposing of devices.
*/
public class DefaultDeviceReference extends TimedEntry implements DeviceReference {
/**
* This sequence number should only be used to compare the first incoming
* message to proxy devices
*/
public static final AppSequence APP_SEQUENCE_ZERO = new AppSequence(-1, 0);
public static final int EVENT_DEVICE_SEEN = 0;
public static final int EVENT_DEVICE_BYE = 1;
public static final int EVENT_DEVICE_GET_RSP = 2;
public static final int EVENT_DEVICE_CHANGED = 3;
public static final int EVENT_DEVICE_COMPLETELY_DISCOVERED = 4;
public static final int EVENT_DEVICE_FAULT_RESET = 5;
private static final GetRequestSynchronizer UP_TO_DATE_GET_SYNCHRONIZER = new GetRequestSynchronizer();
private static final RequestSynchronizer UP_TO_DATE_PROBE_SYNCHRONIZER = new RequestSynchronizer();
private static final ResolveRequestSynchronizer UP_TO_DATE_RESOLVE_SYNCHRONIZER = new ResolveRequestSynchronizer();
private static final int SYNC_WAITTIME = 5000;
private static final int SYNC_WAITRETRY = 5;
private boolean isSecure = false;
// ------------------------------------------------------------------------------------
private Device device = null;
// DeviceListener --> ListenerQueue
private LockedMap listeners = new LockedMap();
/** Changes to local device discovery data must not occur */
private DiscoveryData discoveryData = null;
private String customMData = null;
private XAddressInfo preferredXAddressInfo = null;
private int location = LOCATION_UNKNOWN;
private AppSequenceTracker appSequenceTracker = null;
private StateManager proxyReferenceState = new StateManager();
private GetRequestSynchronizer getSynchronizer = null;
private RequestSynchronizer probeSynchronizer = null;
private ResolveRequestSynchronizer resolveSynchronizer = null;
// Message --> [Get|Resolve]RequestSynchronizer
private final HashMap synchronizers = new HashMap();
private boolean autoUpdateDevice = false;
// -------------------------- CONSTRUCTOR ---------------------------
/**
* Constructor, device is not initialized. This constructor is used for a
* proxy device.
*
* @param data discovery data.
* @param protocolData
*/
DefaultDeviceReference(AppSequence appSeq, DiscoveryData data, ProtocolData protocolData) {
super();
DiscoveryData dataClone = new DiscoveryData(data);
XAddressInfoSet xAddressInfoSet = dataClone.getXAddressInfoSet();
if (xAddressInfoSet != null) {
xAddressInfoSet.mergeProtocolInfo(protocolData.getProtocolInfo());
}
setDiscoveryData(dataClone);
this.location = LOCATION_REMOTE;
appSequenceTracker = new AppSequenceTracker(appSeq);
setPreferredXAddress(dataClone, protocolData);
setPreferredVersion(protocolData.getCommunicationManagerId());
// Condition: we must not use Bye-Messages with metadata version to init
if (dataClone.getMetadataVersion() > DiscoveryData.UNKNOWN_METADATA_VERSION) {
proxyReferenceState.setState(STATE_RUNNING);
}
WatchDog.getInstance().register(this, DispatchingProperties.getInstance().getReferenceCachingTime());
}
/**
* Constructor. Location of device is unknown.
*
* @param epr
*/
DefaultDeviceReference(EndpointReference epr) {
super();
if (epr == null) {
throw new IllegalArgumentException("endpoint reference must not be null");
}
setDiscoveryData(new DiscoveryData(epr));
appSequenceTracker = new AppSequenceTracker();
WatchDog.getInstance().register(this, DispatchingProperties.getInstance().getReferenceCachingTime());
}
/**
* Constructor. Location of device is unknown.
*
* @param epr
*/
DefaultDeviceReference(EndpointReference epr, XAddressInfoSet addresses) {
super();
DiscoveryData d = new DiscoveryData(epr);
d.setXAddresInfoSet(addresses);
setDiscoveryData(d);
appSequenceTracker = new AppSequenceTracker();
preferredXAddressInfo = addresses.toArray()[0];
WatchDog.getInstance().register(this, DispatchingProperties.getInstance().getReferenceCachingTime());
}
/**
* Constructor. Only to be used by local devices.
*
* @param device Local device.
*/
DefaultDeviceReference(LocalDevice device) {
super();
setDiscoveryData(device.getDiscoveryData());
setLocalDevice(device);
}
/*
* (non-Javadoc)
* @see org.ws4d.java.management.TimedEntry#toString()
*/
public synchronized String toString() {
StringBuffer sb = new StringBuffer("DeviceReference [ discoveryData=");
sb.append(discoveryData);
String loc = (location == LOCATION_UNKNOWN ? "unknown" : (location == LOCATION_REMOTE ? "remote" : "local"));
sb.append(", location=").append(loc);
if (location != LOCATION_LOCAL) {
sb.append(", address=").append(preferredXAddressInfo);
}
sb.append(", device=").append(device);
sb.append(" ]");
return sb.toString();
}
/*
* (non-Javadoc)
* @see org.ws4d.java.service.reference.DeviceReference#getState()
*/
public int getState() {
if (location == LOCATION_LOCAL) {
LocalDevice device = (LocalDevice) this.device;
if (device != null) {
return device.isRunning() ? STATE_BUILD_UP : STATE_STOPPED;
} else {
Log.error("DefaultDeviceReference.getState: Location is local, but no device specified");
return STATE_UNKNOWN;
}
}
synchronized (this) {
return proxyReferenceState.getState();
}
}
/*
* (non-Javadoc)
* @see org.ws4d.java.service.reference.DeviceReference#getDevice()
*/
public Device getDevice() throws TimeoutException {
return getDevice(true);
}
/**
* Returns device. If doBuildUp is <code>false</code>, no proxy device will
* be created, i.e. no resolve and get messages will be sent.
*
* @param doBuildUp Specifies that a proxy device should be built up, if no
* device already exists.
* @return May be <code>null</code>, if build up was not requested.
* @throws TimeoutException
*/
protected Device getDevice(boolean doBuildUp) throws TimeoutException {
boolean stoppedRemoteDevice = false;
GetRequestSynchronizer sync = null;
boolean havePendingSync = false;
XAddressInfo xAddressInfo = null;
synchronized (this) {
if (location == LOCATION_LOCAL) {
return device;
}
if (!doBuildUp || getSynchronizer == UP_TO_DATE_GET_SYNCHRONIZER) {
return device;
}
if (getSynchronizer != null) {
sync = getSynchronizer;
havePendingSync = true;
} else {
sync = getSynchronizer = new GetRequestSynchronizer(this);
}
if (proxyReferenceState.getState() == STATE_STOPPED) {
stoppedRemoteDevice = true;
resolveSynchronizer = null;
}
xAddressInfo = preferredXAddressInfo;
}
if (havePendingSync) {
return waitForDevice(sync);
}
if (xAddressInfo == null || xAddressInfo.getXAddress() == null) {
xAddressInfo = resolveRemoteDevice();
} else if (stoppedRemoteDevice) {
fetchCompleteDiscoveryDataSync(resolveRemoteDevice());
}
// check whether there is a newer Get attempt
GetRequestSynchronizer newerSync;
synchronized (this) {
newerSync = getSynchronizer;
if (newerSync == sync) {
sync.metadataVersion = discoveryData.getMetadataVersion();
}
}
if (newerSync != sync) {
try {
sync.device = getDevice(true);
} catch (TimeoutException e) {
sync.exception = e;
}
synchronized (sync) {
sync.pending = false;
sync.notifyAll();
}
if (sync.exception != null) {
throw sync.exception;
}
return sync.device;
}
synchronized (this) {
synchronizers.put(sendGet(xAddressInfo).getMessageId(), sync);
}
return waitForDevice(sync);
}
private Device waitForDevice(GetRequestSynchronizer sync) throws TimeoutException {
while (true) {
synchronized (sync) {
int i = 0;
while (sync.pending) {
try {
sync.wait(SYNC_WAITTIME);
i++;
if (i >= SYNC_WAITRETRY) {
throw new TimeoutException("Device has not send an answer within " + (SYNC_WAITTIME * SYNC_WAITRETRY) + "ms.");
}
} catch (InterruptedException e) {
Log.printStackTrace(e);
}
}
if (sync.exception != null) {
throw sync.exception;
} else if (sync.device != null) {
return sync.device;
}
/*
* else { this means we had a concurrent update and someone was
* started to obtain a newer device }
*/
}
synchronized (this) {
if (getSynchronizer == UP_TO_DATE_GET_SYNCHRONIZER) {
return device;
} else if (getSynchronizer != null) {
sync = getSynchronizer;
} else {
throw new TimeoutException("Unknown communication error with device.");
}
}
}
}
/**
* Rebuilds device. Removes all service references from registry. Should
* only be used for remote devices.
*
* @return Rebuild device.
* @throws TimeoutException
*/
public Device rebuildDevice() throws TimeoutException {
reset(true);
return getDevice();
}
/**
* Instructs this device reference to asynchronously send a Get message to
* the device and create a new proxy, if required. The new proxy device is
* than announced asynchronously via
* {@link DeviceListener#deviceBuildUp(DeviceReference, Device)} method.
* <p>
* Note that in oder to reduce network traffic a Get message will actually
* be sent only if it is detected that the device within this device
* reference instance is not up to date anymore.
*/
public void buildUpDevice() {
GetRequestSynchronizer sync;
synchronized (this) {
if (getSynchronizer != null) {
return;
}
sync = getSynchronizer = new GetRequestSynchronizer(this);
}
buildUpDevice(sync);
}
private void buildUpDevice(final GetRequestSynchronizer newSynchronizer) {
XAddressInfo xAddressInfo = null;
synchronized (this) {
if (getSynchronizer != newSynchronizer) {
return;
}
xAddressInfo = preferredXAddressInfo;
if (xAddressInfo != null) {
newSynchronizer.metadataVersion = discoveryData.getMetadataVersion();
synchronizers.put(sendGet(xAddressInfo).getMessageId(), newSynchronizer);
return;
}
}
// start new thread for resolving
DPWSFramework.getThreadPool().execute(new Runnable() {
public void run() {
try {
XAddressInfo xAddressInfo = resolveRemoteDevice();
boolean callNotify = true;
synchronized (DefaultDeviceReference.this) {
if (newSynchronizer == getSynchronizer) {
newSynchronizer.metadataVersion = discoveryData.getMetadataVersion();
synchronizers.put(sendGet(xAddressInfo).getMessageId(), newSynchronizer);
callNotify = false;
}
}
if (callNotify) {
synchronized (newSynchronizer) {
newSynchronizer.pending = false;
newSynchronizer.notifyAll();
}
}
} catch (TimeoutException e) {
Log.warn("Unablte to resolve remote device: " + e.getMessage());
}
}
});
}
/**
* Sets device, replaces present device. Only to be used for local devices.
*
* @param device Replacement device (local).
* @return replaced device.
*/
public Device setLocalDevice(LocalDevice device) {
if (this.device == device) {
return device;
}
if (device == null) {
/*
* CASE: somebody want to move local device to remote location or
* remove device from device ref.
*/
location = LOCATION_UNKNOWN;
Device oldDevice = this.device;
this.device = null;
discoveryData = new DiscoveryData(discoveryData.getEndpointReference(), discoveryData.getMetadataVersion());
getSynchronizer = null;
probeSynchronizer = null;
resolveSynchronizer = null;
return oldDevice;
}
if (location == LOCATION_REMOTE) {
// XXX think about moving remote device to local location
Log.error("DefaultDeviceReference.setDevice: Setting local device to remote reference: Two devices using the same endpoint reference!");
throw new RuntimeException("Setting local device to a remote reference!");
}
location = LOCATION_LOCAL;
getSynchronizer = UP_TO_DATE_GET_SYNCHRONIZER;
resolveSynchronizer = UP_TO_DATE_RESOLVE_SYNCHRONIZER;
probeSynchronizer = UP_TO_DATE_PROBE_SYNCHRONIZER;
preferredXAddressInfo = null;
LocalDevice oldDevice = (LocalDevice) this.device;
this.device = device;
WatchDog.getInstance().unregister(this);
// copy device metadata from device
setDiscoveryData(device.getDiscoveryData());
if (oldDevice == null || !device.equals(oldDevice)) {
if (device.isRunning()) {
announceDeviceChangedAndBuildUp();
}
}
return oldDevice;
}
public void setDiscoveryData(DiscoveryData newDiscoveryData) {
if (newDiscoveryData == null) {
throw new IllegalArgumentException("discoverData must not be null");
}
if (newDiscoveryData.getEndpointReference() == null) {
throw new IllegalArgumentException("endpoint reference within discoverData must not be null");
}
this.discoveryData = newDiscoveryData;
}
/**
* Resets all state information of the device reference except the endpoint
* reference. Removes the association between the device and services. This
* method has the same effect as calling {@link #reset(boolean)} with an
* argument of <code>false</code>.
*/
public synchronized void reset() {
reset(false);
}
/**
* Resets all state information of the device reference except the endpoint
* reference. Removes the association between the device and services. If
* parameter <code>recurse</code> is set to <code>true</code>, than all
* service references currently associated with this device reference will
* be reset prior to removing them, too.
*
* @param recurse if service references associated with this device
* reference shell be reset, too
*/
public synchronized void reset(boolean recurse) {
if (location == LOCATION_LOCAL) {
Log.warn("DefaultDeviceReference.reset: Not allowed to reset references to local devices!");
return;
}
if (Log.isInfo()) {
Log.info("DefaultDeviceReference.reset: Resetting device reference with endpoint reference " + discoveryData.getEndpointReference());
}
disconnectAllServiceReferences(recurse);
device = null;
discoveryData = new DiscoveryData(discoveryData.getEndpointReference(), DiscoveryData.UNKNOWN_METADATA_VERSION);
changeProxyReferenceState(EVENT_DEVICE_FAULT_RESET);
location = LOCATION_UNKNOWN;
appSequenceTracker = new AppSequenceTracker();
preferredXAddressInfo = null;
getSynchronizer = null;
probeSynchronizer = null;
resolveSynchronizer = null;
}
/**
* Uses directed Probe to refresh discovery data. A previously built up
* device will be disposed of.
*
* @return
* @throws TimeoutException
*/
public void fetchCompleteDiscoveryDataSync() throws TimeoutException {
fetchCompleteDiscoveryDataSync(resolveRemoteDevice());
}
/**
* Sends directed probe to device
*/
private void fetchCompleteDiscoveryDataSync(XAddressInfo xAddressInfo) throws TimeoutException {
// we MUST NOT have locked DefaultDeviceReference.this up to now!
RequestSynchronizer sync;
synchronized (this) {
if (probeSynchronizer == UP_TO_DATE_PROBE_SYNCHRONIZER) {
return;
}
sync = probeSynchronizer;
if (sync == null) {
sync = probeSynchronizer = new RequestSynchronizer(this);
sendDirectedProbe(xAddressInfo);
ProbeMessage probe = new ProbeMessage(xAddressInfo.getComManId());
synchronizers.put(probe.getMessageId(), sync);
probe.setProtocolInfo(xAddressInfo.getProtocolInfo());
probe.getHeader().setTo(new AttributedURI(WSDConstants.WSD_TO));
probe.setTargetXAddressInfo(xAddressInfo);
OutDispatcher.getInstance().send(probe, xAddressInfo, new DefaultDeviceReferenceCallback(xAddressInfo));
}
}
while (true) {
synchronized (sync) {
while (sync.pending) {
try {
sync.wait();
} catch (InterruptedException e) {
Log.printStackTrace(e);
}
}
if (sync.exception != null) {
throw sync.exception;
}
/*
* else { this means we had a concurrent update and someone was
* started to obtain a newer device }
*/
}
synchronized (this) {
if (probeSynchronizer == UP_TO_DATE_PROBE_SYNCHRONIZER) {
return;
} else if (probeSynchronizer != null) {
sync = probeSynchronizer;
} else {
throw new TimeoutException("Unknown communication error with device.");
}
}
}
}
/*
* (non-Javadoc)
* @see org.ws4d.java.service.reference.DeviceReference#
* fetchCompleteDiscoveryDataAsync()
*/
public void fetchCompleteDiscoveryDataAsync() {
RequestSynchronizer sync;
synchronized (this) {
if (probeSynchronizer != null) {
return;
}
sync = probeSynchronizer = new RequestSynchronizer(this);
}
fetchCompleteDiscoveryDataAsync(sync);
}
private void fetchCompleteDiscoveryDataAsync(final RequestSynchronizer newSynchronizer) {
XAddressInfo xAddressInfo = null;
synchronized (this) {
if (probeSynchronizer != newSynchronizer) {
return;
}
xAddressInfo = preferredXAddressInfo;
if (xAddressInfo != null) {
synchronizers.put(sendDirectedProbe(xAddressInfo).getMessageId(), newSynchronizer);
return;
}
}
// start new thread for resolving
DPWSFramework.getThreadPool().execute(new Runnable() {
public void run() {
try {
XAddressInfo xAddressInfo = resolveRemoteDevice();
boolean callNotify = true;
synchronized (DefaultDeviceReference.this) {
if (newSynchronizer == probeSynchronizer) {
synchronizers.put(sendDirectedProbe(xAddressInfo).getMessageId(), newSynchronizer);
callNotify = false;
}
}
if (callNotify) {
synchronized (newSynchronizer) {
newSynchronizer.pending = false;
newSynchronizer.notifyAll();
}
}
} catch (TimeoutException e) {
Log.warn("Unablte to resolve remote device: " + e.getMessage());
}
}
});
}
/**
* Sends Resolve message to remote device. ResolveMatches message will be
* handled by callback handler, which updates discovery data. Only one
* Resolve message will be sent each time.
*
* @return preferred transport address of device and corresponding protocol
* @throws TimeoutException
*/
public XAddressInfo resolveRemoteDevice() throws TimeoutException {
// we MUST NOT have locked DefaultDeviceReference.this up to now!
ResolveRequestSynchronizer sync;
synchronized (this) {
sync = resolveSynchronizer;
if (sync == UP_TO_DATE_RESOLVE_SYNCHRONIZER) {
// means we have current discovery metadata from resolve
if (preferredXAddressInfo == null) {
/*
* fatal: resolve didn't provide us with actually usable
* addresses
*/
throw new TimeoutException("No usable transport addresses found!");
}
return preferredXAddressInfo;
}
if (sync == null) {
sync = resolveSynchronizer = new ResolveRequestSynchronizer(this);
synchronizers.put(sendResolve(preferredXAddressInfo).getMessageId(), sync);
}
}
while (true) {
synchronized (sync) {
while (sync.pending) {
try {
sync.wait();
} catch (InterruptedException e) {
Log.printStackTrace(e);
}
}
if (sync.exception != null) {
throw sync.exception;
} else if (sync.xAddressInfo != null) {
return sync.xAddressInfo;
}
/*
* else { this means we had a concurrent update and someone was
* started to obtain a newer device }
*/
}
synchronized (this) {
if (preferredXAddressInfo != null) {
return preferredXAddressInfo;
}
if (resolveSynchronizer == UP_TO_DATE_RESOLVE_SYNCHRONIZER) {
if (preferredXAddressInfo == null) {
/*
* fatal: resolve didn't provide us with actually usable
* addresses
*/
throw new TimeoutException("No usable transport addresses found!");
}
return preferredXAddressInfo;
}
if (resolveSynchronizer != null) {
sync = resolveSynchronizer;
} else {
throw new TimeoutException("Unknown communication error with device.");
}
}
}
}
private void resolveRemoteDeviceAsync(final ResolveRequestSynchronizer newSynchronizer) {
XAddressInfo xAddressInfo = null;
synchronized (this) {
if (resolveSynchronizer != newSynchronizer) {
return;
}
xAddressInfo = preferredXAddressInfo;
if (xAddressInfo != null) {
return;
}
}
// start new thread for resolving
DPWSFramework.getThreadPool().execute(new Runnable() {
public void run() {
boolean callNotify = true;
synchronized (DefaultDeviceReference.this) {
if (newSynchronizer != resolveSynchronizer) {
synchronizers.put(sendResolve(preferredXAddressInfo).getMessageId(), newSynchronizer);
callNotify = false;
}
}
if (callNotify) {
synchronized (newSynchronizer) {
newSynchronizer.pending = false;
newSynchronizer.notifyAll();
}
}
}
});
}
private GetMessage sendGet(XAddressInfo xAddressInfo) {
GetMessage get = new GetMessage(xAddressInfo.getComManId());
EndpointReference epr = getEndpointReference();
get.setProtocolInfo(xAddressInfo.getProtocolInfo());
// TODO Credentials in GETMessage
/*
* we set the wsa:to property to the EPR address (usually a URN) of this
* device instead of to an xAddress of that device
*/
get.getHeader().setEndpointReference(epr);
get.setTargetXAddressInfo(xAddressInfo);
OutDispatcher.getInstance().send(get, xAddressInfo, new DefaultDeviceReferenceCallback(xAddressInfo));
return get;
}
private ProbeMessage sendDirectedProbe(XAddressInfo xAddressInfo) {
ProbeMessage probe = new ProbeMessage(xAddressInfo.getComManId());
probe.setProtocolInfo(xAddressInfo.getProtocolInfo());
probe.getHeader().setTo(new AttributedURI(WSDConstants.WSD_TO));
probe.setTargetXAddressInfo(xAddressInfo);
OutDispatcher.getInstance().send(probe, xAddressInfo, new DefaultDeviceReferenceCallback(xAddressInfo));
return probe;
}
private ResolveMessage sendResolve(XAddressInfo xAddressInfo) {
// Send resolve to discover xAddress(es)
ResolveMessage resolve = new ResolveMessage(CommunicationManager.ID_NULL);
EndpointReference epr = getEndpointReference();
// we dont know the version, if we send an resolve
// resolve.setProtocolInfo(xAddressInfo.getProtocolVersionInfo());
resolve.setEndpointReference(epr);
OutDispatcher.getInstance().send(resolve, xAddressInfo, Discovery.getDefaultOutputDomains(), new DefaultDeviceReferenceCallback(xAddressInfo));
return resolve;
}
/*
* (non-Javadoc)
* @see org.ws4d.java.service.reference.Reference#getLocation()
*/
public int getLocation() {
return location;
}
// --------------------- DISCOVERY DATA -----------------------
/*
* (non-Javadoc)
* @see
* org.ws4d.java.service.reference.DeviceReference#getDevicePortTypes(boolean
* )
*/
public Iterator getDevicePortTypes(boolean doDiscovery) throws TimeoutException {
synchronized (this) {
if (doDiscovery) {
doDiscovery = (discoveryData.getTypes() == null || discoveryData.getTypes().size() == 0) && preferredXAddressInfo == null;
}
}
if (doDiscovery) {
resolveRemoteDevice();
}
QNameSet types = discoveryData.getTypes();
return types == null ? EmptyStructures.EMPTY_ITERATOR : new ReadOnlyIterator(types.iterator());
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.service.reference.DeviceReference#getDevicePortTypesAsArray
* (boolean)
*/
public QName[] getDevicePortTypesAsArray(boolean doDiscovery) throws TimeoutException {
synchronized (this) {
if (doDiscovery) {
doDiscovery = (discoveryData.getTypes() == null || discoveryData.getTypes().size() == 0) && preferredXAddressInfo == null;
}
}
if (doDiscovery) {
resolveRemoteDevice();
}
QNameSet types = discoveryData.getTypes();
return types == null ? (QName[]) EmptyStructures.EMPTY_OBJECT_ARRAY : types.toArray();
}
/*
* (non-Javadoc)
* @see org.ws4d.java.service.reference.DeviceReference#getScopes(boolean)
*/
public Iterator getScopes(boolean doDiscovery) throws TimeoutException {
synchronized (this) {
if (doDiscovery) {
doDiscovery = (discoveryData.getScopes() == null || discoveryData.getScopes().size() == 0) && preferredXAddressInfo == null;
}
}
if (doDiscovery) {
resolveRemoteDevice();
}
ScopeSet scopes = discoveryData.getScopes();
URISet uriScopes = (scopes == null) ? null : scopes.getScopesAsUris();
return (uriScopes == null) ? EmptyStructures.EMPTY_ITERATOR : new ReadOnlyIterator(uriScopes.iterator());
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.service.reference.DeviceReference#getScopesAsArray(boolean)
*/
public URI[] getScopesAsArray(boolean doDiscovery) throws TimeoutException {
synchronized (this) {
if (doDiscovery) {
doDiscovery = (discoveryData.getScopes() == null || discoveryData.getScopes().size() == 0) && preferredXAddressInfo == null;
}
}
if (doDiscovery) {
resolveRemoteDevice();
}
ScopeSet scopes = discoveryData.getScopes();
URISet uriScopes = (scopes == null) ? null : scopes.getScopesAsUris();
return (uriScopes == null) ? (URI[]) EmptyStructures.EMPTY_OBJECT_ARRAY : uriScopes.toArray();
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.service.reference.DeviceReference#getMetadataVersion(boolean
* )
*/
public long getMetadataVersion(boolean doDiscovery) throws TimeoutException {
synchronized (this) {
if (doDiscovery) {
doDiscovery = discoveryData.getMetadataVersion() == DiscoveryData.UNKNOWN_METADATA_VERSION && preferredXAddressInfo == null;
}
}
if (doDiscovery) {
resolveRemoteDevice();
}
return discoveryData.getMetadataVersion();
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.service.reference.DeviceReference#getXAddresses(boolean)
*/
public Iterator getXAddressInfos(boolean doDiscovery) throws TimeoutException {
synchronized (this) {
if (doDiscovery) {
doDiscovery = (discoveryData.getXAddressInfoSet() == null || discoveryData.getXAddressInfoSet().size() == 0) && preferredXAddressInfo == null;
}
}
if (doDiscovery) {
resolveRemoteDevice();
}
XAddressInfoSet xAddrs = discoveryData.getXAddressInfoSet();
return xAddrs == null ? EmptyStructures.EMPTY_ITERATOR : new ReadOnlyIterator(xAddrs.iterator());
}
/*
* (non-Javadoc)
* @see org.ws4d.java.service.reference.Reference#getPreferredXAddress()
*/
public synchronized URI getPreferredXAddress() {
return preferredXAddressInfo == null ? null : preferredXAddressInfo.getXAddress();
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.service.reference.Reference#getPreferredXAddressProtocol()
*/
public synchronized String getPreferredCommunicationManagerID() {
return preferredXAddressInfo == null ? CommunicationManager.ID_NULL : preferredXAddressInfo.getComManId();
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.service.reference.DeviceReference#getEndpointReference()
*/
public EndpointReference getEndpointReference() {
return discoveryData.getEndpointReference();
}
/**
* Give the customize metadata
*
* @return String which contains the user added metadata.
*/
public String getCustomMData() {
return customMData;
}
/**
* Set the customize metadata
*
* @param customMData String which contains the new customize metadata.
*/
public void setCustomMData(String customMData) {
this.customMData = customMData;
}
// -----------------------------------------------------------------
/**
* Checks if the specified application sequence is newer than the latest. If
* the specified sequence is newer, the latest sequence replaced by the
* newer and <code>true</code> will be returned. If new sequence is
* <code>null</code>, method returns <code>true</code>;
*
* @param newSequence
* @return <code>true</code>, if the specified sequence is newer than the
* latest sequence or if new sequence is null.
*/
protected synchronized boolean checkAppSequence(AppSequence appSeq) {
if (location == LOCATION_LOCAL) {
Log.error("DefaultDeviceReference.checkAppSequence is not available for local devices.");
throw new RuntimeException("checkAppSequence is not available for local devices!");
}
return appSequenceTracker.checkAndUpdate(appSeq);
}
protected synchronized int changeProxyReferenceState(int event) {
return proxyReferenceState.transit(event);
}
/*
* (non-Javadoc)
* @see org.ws4d.java.management.TimedEntry#timedOut()
*/
protected void timedOut() {
// Reference has no listeners, WatchDog timed out, delete this.
listeners.sharedLock();
try {
if (listeners.size() == 0 && location != LOCATION_LOCAL) {
// nobody needs this reference => remove it from registry
if (discoveryData.getEndpointReference() != null) {
DeviceServiceRegistry.unregisterDeviceReference(this);
}
}
// else {
// Log.error("Temporary DeviceReference timed out! " +
// "Listener counter should be 0, was " + listeners.size());
// }
} finally {
listeners.releaseSharedLock();
}
}
// ------------------ LISTENERS--------------------------
/*
* (non-Javadoc)
* @see
* org.ws4d.java.service.reference.DeviceReference#addListener(org.ws4d.
* java.service.reference.DeviceListener)
*/
public void addListener(DeviceListener listener) {
if (listener == null) {
return;
}
listeners.exclusiveLock();
try {
if (listeners.size() == 0 && location != LOCATION_LOCAL) {
// only remote devices may have been registered
WatchDog.getInstance().unregister(this);
}
if (listeners.containsKey(listener)) {
// no need to create new listener queue
return;
}
listeners.put(listener, new DeviceListenerQueue(listener, this));
} finally {
listeners.releaseExclusiveLock();
}
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.service.reference.DeviceReference#removeListener(org.ws4d
* .java.service.reference.DeviceListener)
*/
public void removeListener(DeviceListener listener) {
if (listener == null) {
return;
}
listeners.exclusiveLock();
try {
listeners.remove(listener);
if (listeners.size() == 0 && location != LOCATION_LOCAL) {
// only remote device will be removed
WatchDog.getInstance().register(this, DispatchingProperties.getInstance().getReferenceCachingTime());
}
} finally {
listeners.releaseExclusiveLock();
}
}
/**
* Returns amount of listeners for this reference.
*
* @return Amount of listeners for this reference.
*/
protected int sizeOfListeners() {
listeners.sharedLock();
try {
return listeners.size();
} finally {
listeners.releaseSharedLock();
}
}
/**
* Set the preferredProtocolVersion to the DPWSVersion of the DPWSProperties
* if just one is defined, else it will set to "null" for both.
*/
private void setPreferredVersion(String communicationManagerId) {
CommunicationManager comMan = DPWSFramework.getCommunicationManager(communicationManagerId);
HashSet supportedVersions = comMan.getSupportedVersions();
if (supportedVersions.size() == 1) {
if (preferredXAddressInfo == null) {
preferredXAddressInfo = new XAddressInfo();
}
Integer ver = (Integer) (supportedVersions.toArray()[0]);
preferredXAddressInfo.setProtocolInfo(comMan.createProtocolInfo(ver.intValue()));
}
}
/**
* @param address the address to set
* @param comManId the protocol ID the new preferred XAddress belongs to
*/
private void setPreferredXAddress(DiscoveryData data, ProtocolData protocolData) {
/*
* store address for target device within the device reference (handler)
*/
XAddressInfoSet xAddresses = data.getXAddressInfoSet();
if (xAddresses == null || xAddresses.size() == 0) {
return;
}
if (preferredXAddressInfo != null && xAddresses.contains(preferredXAddressInfo)) {
return;
}
for (Iterator it = xAddresses.iterator(); it.hasNext();) {
XAddressInfo xAddr = (XAddressInfo) it.next();
if (protocolData.sourceMatches(xAddr.getXAddress())) {
preferredXAddressInfo = xAddr;
return;
}
}
// take the first (or any other) one
preferredXAddressInfo = (XAddressInfo) xAddresses.iterator().next();
}
public boolean isDeviceObjectExisting() {
return device != null;
}
public boolean isDiscovered() {
if (location == LOCATION_LOCAL) {
return true;
}
return discoveryData.getMetadataVersion() == DiscoveryData.UNKNOWN_METADATA_VERSION ? false : true;
}
public synchronized boolean isCompleteDiscovered() {
return probeSynchronizer == UP_TO_DATE_PROBE_SYNCHRONIZER;
}
public synchronized void setAutoUpdateDevice(boolean autoUpdateDevice) {
this.autoUpdateDevice = autoUpdateDevice;
}
public synchronized boolean isAutoUpdateDevice() {
return autoUpdateDevice;
}
synchronized void disconnectAllServiceReferences(boolean resetServiceRefs) {
if (device == null) {
return;
}
Iterator servRefs = device.getServiceReferences();
while (servRefs.hasNext()) {
ServiceReferenceInternal servRef = (ServiceReferenceInternal) servRefs.next();
servRef.disconnectFromDevice();
if (resetServiceRefs) {
servRef.reset();
}
}
}
/**
* Updates discovery data of device reference (hence the discovery data of
* the proxy device).
*
* @param data new discovery data to check old data against.
* @return true if endpoint reference was set.
*/
private boolean updateDiscoveryData(HostMData host) {
if (location == LOCATION_LOCAL) {
throw new RuntimeException("Updating Discovery Data for a local device is prohibited outside of the device");
}
if (host == null) {
return false;
}
QNameSet types = discoveryData.getTypes();
if (types == null) {
types = new QNameSet(host.getTypes());
} else {
types.addAll(host.getTypes());
}
if (discoveryData.getEndpointReference() == null) {
discoveryData.setEndpointReference(host.getEndpointReference());
return true;
}
return false;
}
synchronized void updateFromHello(HelloMessage hello, ProtocolData protocolData) {
if (checkAppSequence(hello.getAppSequence())) {
if (Log.isInfo()) {
Log.info("Set DPWS Version for " + getEndpointReference().toString() + " to : " + protocolData.getProtocolInfo().getDisplayName());
}
setSecureDevice(hello.getHeader().getSignature() != null);
updateDiscoveryData(hello.getDiscoveryData(), protocolData);
} else if (Log.isDebug()) {
Log.debug("DefaultDeviceReference.updateFromHello: old AppSequence in HelloMessage (msgId = " + hello.getMessageId() + ")", Log.DEBUG_LAYER_FRAMEWORK);
}
}
synchronized void updateFromBye(ByeMessage bye, ProtocolData protocolData) {
if (checkAppSequence(bye.getAppSequence())) {
preferredXAddressInfo = null;
resolveSynchronizer = null;
changeProxyReferenceState(DefaultDeviceReference.EVENT_DEVICE_BYE);
} else if (Log.isDebug()) {
Log.debug("DefaultDeviceReference.updateFromBye: old AppSequence in ByeMessage (msgId = " + bye.getMessageId() + ")", Log.DEBUG_LAYER_FRAMEWORK);
}
}
/**
* Updates discovery data. Only used for references to remote devices.
* Returns <code>true</code>, if metadata version of the new discovery data
* is newer. Preferred xaddress will be set if unset or metadata version is
* newer. Changes proxy reference state.
*
* @param newData
* @param protocolData
* @return <code>true</code>, if metadata version of the new discovery data
* is newer.
*/
boolean updateDiscoveryData(DiscoveryData newData, ProtocolData protocolData) {
GetRequestSynchronizer newGetSynchronizer = null;
RequestSynchronizer newProbeSynchronizer = null;
ResolveRequestSynchronizer newResolveSynchronizer = null;
boolean updated = false;
synchronized (this) {
// handler.communicationState = CallbackHandler.COM_OK;
XAddressInfoSet xAddressInfoSet = discoveryData.getXAddressInfoSet();
if (xAddressInfoSet != null) {
xAddressInfoSet.mergeProtocolInfo(protocolData.getProtocolInfo());
}
updated = discoveryData.update(newData);
if (updated) {
if (getSynchronizer != null) {
if (getSynchronizer != UP_TO_DATE_GET_SYNCHRONIZER) {
getSynchronizer = newGetSynchronizer = new GetRequestSynchronizer(this);
} else {
getSynchronizer = null;
}
}
if (probeSynchronizer != null) {
if (probeSynchronizer != UP_TO_DATE_PROBE_SYNCHRONIZER) {
probeSynchronizer = newProbeSynchronizer = new RequestSynchronizer(this);
} else {
probeSynchronizer = null;
}
}
}
setPreferredXAddress(newData, protocolData);
if (preferredXAddressInfo == null) {
if (resolveSynchronizer != null) {
if (resolveSynchronizer != UP_TO_DATE_RESOLVE_SYNCHRONIZER) {
resolveSynchronizer = newResolveSynchronizer = new ResolveRequestSynchronizer(this);
} else {
resolveSynchronizer = null;
}
}
}
if (updated) {
if (autoUpdateDevice) {
buildUpDevice();
}
/*
* We do not remove the service from framework, we only remove
* association to the device.
*/
changeProxyReferenceState(EVENT_DEVICE_CHANGED);
} else {
changeProxyReferenceState(EVENT_DEVICE_SEEN);
}
}
if (newResolveSynchronizer != null) {
resolveRemoteDeviceAsync(newResolveSynchronizer);
}
if (newGetSynchronizer != null) {
buildUpDevice(newGetSynchronizer);
}
if (newProbeSynchronizer != null) {
fetchCompleteDiscoveryDataAsync(newProbeSynchronizer);
}
return updated;
}
private void announceDeviceListenerEvent(byte eventType, Device device) {
listeners.sharedLock();
try {
DeviceEvent event = new DeviceEvent(eventType, device);
for (Iterator it = listeners.values().iterator(); it.hasNext();) {
DeviceListenerQueue queue = (DeviceListenerQueue) it.next();
queue.announce(event);
}
} finally {
listeners.releaseSharedLock();
}
}
/**
* Informs listeners on this device reference in separate thread about
* change.
*/
private void announceDeviceRunning() {
announceDeviceListenerEvent(DeviceListenerQueue.DEVICE_RUNNING_EVENT, null);
}
/**
* Informs listeners on this device reference in separate thread about
* change.
*/
private void announceDeviceCompletelyDiscovered() {
announceDeviceListenerEvent(DeviceListenerQueue.DEVICE_COMPLETELY_DISCOVERED_EVENT, null);
}
/**
* Informs listeners on this device reference in separate thread about
* change.
*/
private void announceDeviceBuildUp() {
announceDeviceListenerEvent(DeviceListenerQueue.DEVICE_BUILT_UP_EVENT, device);
}
/**
* Informs listeners on this device reference in separate thread about stop.
*/
public void announceDeviceBye() {
announceDeviceListenerEvent(DeviceListenerQueue.DEVICE_BYE_EVENT, null);
}
/**
* Informs listeners on this device reference in separate thread about
* change.
*/
private void announceDeviceChanged() {
announceDeviceListenerEvent(DeviceListenerQueue.DEVICE_CHANGED_EVENT, null);
}
/**
* Informs listeners on this device reference in separate thread about
* change.
*/
private void announceDeviceCommunicationErrorOrReset() {
announceDeviceListenerEvent(DeviceListenerQueue.DEVICE_COMMUNICATION_ERROR_OR_RESET_EVENT, null);
}
/**
* Clones device listeners and informs everyone in separate thread about
* change.
*/
public void announceDeviceChangedAndBuildUp() {
announceDeviceListenerEvent(DeviceListenerQueue.DEVICE_CHANGED_AND_BUILT_UP_EVENT, device);
}
/**
* Clones device listeners and informs everyone in separate thread about
* change.
*/
public void announceDeviceRunningAndBuildUp() {
announceDeviceListenerEvent(DeviceListenerQueue.DEVICE_RUNNING_AND_BUILT_UP_EVENT, device);
}
private class DefaultDeviceReferenceCallback extends DefaultResponseCallback {
DefaultDeviceReferenceCallback(XAddressInfo targetXAddressInfo) {
super(targetXAddressInfo);
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.communication.DefaultResponseCallback#handle(org.ws4d
* .java.communication.message.Message,
* org.ws4d.java.message.discovery.ProbeMatchesMessage,
* org.ws4d.java.communication.ProtocolData)
*/
public void handle(Message request, ProbeMatchesMessage response, ProtocolData protocolData) {
/*
* handles directed Probe
*/
RequestSynchronizer sync = null;
try {
synchronized (DefaultDeviceReference.this) {
location = LOCATION_REMOTE;
// set the preferred
protocolData.setProtocolInfo(response.getProtocolInfo());
checkAppSequence(response.getAppSequence());
sync = (RequestSynchronizer) synchronizers.remove(request.getMessageId());
if (sync == null) {
/*
* this shouldn't ever happen, as it would mean we
* receive a response to a request we never sent...
*/
Log.warn("DefaultDeviceReference: ignoring unexpected ProbeMatches message " + response);
return;
}
long currentMetadataVersion = discoveryData.getMetadataVersion();
if (sync.metadataVersion == currentMetadataVersion) {
setSecureDevice(response.getHeader().getSignature() != null);
DataStructure data = response.getProbeMatches();
boolean matched = false;
for (Iterator it = data.iterator(); it.hasNext();) {
ProbeMatch match = (ProbeMatch) it.next();
if (discoveryData.getEndpointReference().equals(match.getEndpointReference())) {
matched = true;
updateDiscoveryData(match, protocolData);
break;
}
}
if (matched) {
if (sync == probeSynchronizer) {
probeSynchronizer = UP_TO_DATE_PROBE_SYNCHRONIZER;
changeProxyReferenceState(EVENT_DEVICE_COMPLETELY_DISCOVERED);
}
} else {
sync.exception = new TimeoutException("No matching endpoint reference in directed probe result found!");
if (sync == probeSynchronizer) {
// make next call create a new directed probe
probeSynchronizer = null;
}
}
} else {
sync.exception = new TimeoutException("Device update detected while probing device directly");
}
/*
* don't make any changes on this devRef if the response is
* outdated!
*/
}
} catch (Throwable e) {
sync.exception = new TimeoutException("Unexpected exception during probe matches processing: " + e);
}
synchronized (sync) {
sync.pending = false;
sync.notifyAll();
}
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.communication.DefaultResponseCallback#handle(org.ws4d
* .java.communication.message.Message,
* org.ws4d.java.message.discovery.ResolveMatchesMessage,
* org.ws4d.java.communication.ProtocolData)
*/
public void handle(Message request, ResolveMatchesMessage response, ProtocolData protocolData) {
// XXX Discovery Proxy handling not implemented
ResolveRequestSynchronizer sync = null;
try {
synchronized (DefaultDeviceReference.this) {
location = LOCATION_REMOTE;
protocolData.setProtocolInfo(response.getProtocolInfo());
boolean appSequenceCheckPassed = checkAppSequence(response.getAppSequence());
sync = (ResolveRequestSynchronizer) synchronizers.remove(request.getMessageId());
if (sync == null) {
/*
* this shouldn't ever happen, as it would mean we
* receive a response to a request we never sent...
*/
Log.warn("DefaultDeviceReference: ignoring unexpected ResolveMatches message " + response);
return;
}
if (appSequenceCheckPassed) {
long currentMetadataVersion = discoveryData.getMetadataVersion();
if (sync.metadataVersion == currentMetadataVersion) {
setSecureDevice(response.getHeader().getSignature() != null);
DiscoveryData newDiscoData = response.getResolveMatch();
updateDiscoveryData(newDiscoData, protocolData);
sync.xAddressInfo = preferredXAddressInfo;
}
if (sync == resolveSynchronizer) {
resolveSynchronizer = UP_TO_DATE_RESOLVE_SYNCHRONIZER;
}
} else if (Log.isDebug()) {
Log.debug("DefaultDeviceReference.handle: old AppSequence in ResolveMatches message (msgId = " + response.getMessageId() + ")", Log.DEBUG_LAYER_FRAMEWORK);
}
}
} catch (Throwable e) {
sync.exception = new TimeoutException("Unexpected exception during resolve matches processing: " + e);
}
synchronized (sync) {
sync.pending = false;
sync.notifyAll();
}
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.communication.DefaultResponseCallback#handle(org.ws4d
* .java.communication.message.Message, org.ws4d.java.message
* .metadataexchange.GetResponseMessage,
* org.ws4d.java.communication.ProtocolData)
*/
public void handle(Message request, GetResponseMessage response, ProtocolData protocolData) {
GetRequestSynchronizer sync = null;
try {
synchronized (DefaultDeviceReference.this) {
// Only message from remote devices are handled
if (location == LOCATION_LOCAL) {
// may occur if location of dev ref was unknown before
return;
}
location = LOCATION_REMOTE;
// update devRef data and schedule listener notifications
protocolData.setProtocolInfo(response.getProtocolInfo());
sync = (GetRequestSynchronizer) synchronizers.remove(request.getMessageId());
if (sync == null) {
/*
* this shouldn't ever happen, as it would mean we
* receive a response to a request we never sent...
*/
Log.warn("DefaultDeviceReference: ignoring unexpected GetResponse message " + response);
return;
}
long currentMetadataVersion = discoveryData.getMetadataVersion();
if (sync.metadataVersion == currentMetadataVersion) {
setCustomMData(response.getCustomMdata());
updateDiscoveryData(response.getHost());
ProxyFactory pFac = DPWSFramework.getProxyFactory();
boolean doChangeState = false;
if (device == null) {
// device was not build up until now
device = pFac.createProxyDevice(response, DefaultDeviceReference.this, null, protocolData);
doChangeState = true;
} else if (!device.isValid()) {
// device has been changed, create new device
device = pFac.createProxyDevice(response, DefaultDeviceReference.this, device, protocolData);
doChangeState = true;
}
if (doChangeState) {
changeProxyReferenceState(EVENT_DEVICE_GET_RSP);
}
sync.device = device;
if (sync == getSynchronizer) {
// this was the only currently pending get request
getSynchronizer = UP_TO_DATE_GET_SYNCHRONIZER;
}
} else {
if (Log.isDebug()) {
Log.debug("Concurrent device update detected, rebuilding device proxy", Log.DEBUG_LAYER_FRAMEWORK);
}
// sync.exception = new
// TimeoutException("Device update detected while trying to build up device");
}
}
} catch (Throwable e) {
sync.exception = new TimeoutException("Unexpected exception during get response processing: " + e);
}
synchronized (sync) {
sync.pending = false;
sync.notifyAll();
}
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.communication.DefaultResponseCallback#handle(org.ws4d
* .java.communication.message.Message,
* org.ws4d.java.message.FaultMessage,
* org.ws4d.java.communication.ProtocolData)
*/
public void handle(Message request, FaultMessage fault, ProtocolData protocolData) {
boolean noMoreXAddrs = false;
boolean unauthorized = false;
RequestSynchronizer sync = null;
try {
synchronized (DefaultDeviceReference.this) {
protocolData.setProtocolInfo(fault.getProtocolInfo());
sync = (RequestSynchronizer) synchronizers.remove(request.getMessageId());
if (sync == null) {
/*
* this shouldn't ever happen, as it would mean we
* receive a response to a request we never sent...
*/
Log.warn("DefaultDeviceReference.handle(FaultMessage): no synchronizer found for request message " + request);
return;
}
Log.warn("Fault returned for xaddress " + request.getTargetAddress() + ": " + fault);
if (sync == resolveSynchronizer) {
resolveSynchronizer = null;
} else if (sync == probeSynchronizer) {
try {
// try to retransmit the message to another
// XAddress!
if (retransmitRequest(request)) {
return;
}
} catch (NoSuchElementException e) {
noMoreXAddrs = true;
}
probeSynchronizer = null;
} else if (sync == getSynchronizer) {
// check fault code
if (fault.getSubcode().equals(WSSecurityConstants.WSSE_FAULT_AUTHENTICATION)) {
unauthorized = true;
} else {
try {
// try to retransmit the message to another
// XAddress!
if (retransmitRequest(request)) {
return;
}
} catch (NoSuchElementException e) {
noMoreXAddrs = true;
}
}
getSynchronizer = null;
}
synchronizers.remove(request.getMessageId());
changeProxyReferenceState(EVENT_DEVICE_FAULT_RESET);
}
} catch (Throwable e) {
Log.warn("Unexpected exception during fault processing: " + e);
}
synchronized (sync) {
if (noMoreXAddrs) {
sync.exception = new TimeoutException("No further xaddress to communicate with.");
} else if (unauthorized) {
sync.exception = new TimeoutException("Authorization Required.");
} else {
switch (request.getType()) {
case (DPWSMessageConstants.PROBE_MESSAGE): {
sync.exception = new TimeoutException("Device send fault, probably doesn't support directed probing: " + fault);
break;
}
default: {
sync.exception = new TimeoutException("Device send fault, probably WSDAPI Device: " + fault);
}
}
}
sync.pending = false;
sync.notifyAll();
}
if (Log.isDebug()) {
URI msgId = fault.getRelatesTo();
Log.debug("DefaultDeviceReference.CallbackHandler.receivedFault: get, msgId = " + msgId, Log.DEBUG_LAYER_FRAMEWORK);
}
}
/*
* (non-Javadoc)
* @see org.ws4d.java.communication.DefaultResponseCallback#
* handleMalformedResponseException(org.ws4d.java.message.Message,
* java.lang.Exception, org.ws4d.java.communication.ProtocolData)
*/
public void handleMalformedResponseException(Message request, Exception exception, ProtocolData protocolData) {
// same as for timeouts, but we additionally log the exception
Log.warn("DefaultDeviceReference.handleMalformedResponseException: malformed response exception: " + exception + ". Request was: " + request);
handleMalformedResponseOrTimeout(request, "handleMalformedResponseException");
}
/*
* (non-Javadoc)
* @see org.ws4d.java.communication.DefaultResponseCallback#
* handleTransmissionException(org.ws4d.java.message.Message,
* java.lang.Exception, org.ws4d.java.communication.ProtocolData)
*/
public void handleTransmissionException(Message request, Exception exception, ProtocolData protocolData) {
boolean noMoreXAddrs = false;
RequestSynchronizer sync = null;
try {
synchronized (DefaultDeviceReference.this) {
sync = (RequestSynchronizer) synchronizers.get(request.getMessageId());
if (sync == null) {
/*
* this shouldn't ever happen, as it would mean we
* receive a response to a request we never sent...
*/
Log.warn("DefaultDeviceReference.handleTransmissionException: no synchronizer found for request message " + request);
return;
}
Log.warn("Transmission error with xaddress " + request.getTargetAddress() + ": " + exception);
if (sync == resolveSynchronizer) {
resolveSynchronizer = null;
} else if (sync == getSynchronizer || sync == probeSynchronizer) {
try {
// try to retransmit the message to another
// XAddress!
if (retransmitRequest(request)) {
return;
}
} catch (NoSuchElementException e) {
noMoreXAddrs = true;
}
if (sync == getSynchronizer) {
getSynchronizer = null;
} else {
probeSynchronizer = null;
}
}
synchronizers.remove(request.getMessageId());
changeProxyReferenceState(EVENT_DEVICE_FAULT_RESET);
}
} catch (Throwable e) {
Log.warn("Unexpected exception: " + e);
}
synchronized (sync) {
if (noMoreXAddrs) {
sync.exception = new TimeoutException("No further xaddress to communicate with.");
} else {
sync.exception = new TimeoutException("Unable to send request message " + request);
}
sync.pending = false;
sync.notifyAll();
}
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.dispatch.ResponseCallback#receivedTimeout(org.ws4d.java
* .data.uri.URI)
*/
public void handleTimeout(Message request) {
// timeout of messages send
handleMalformedResponseOrTimeout(request, "handleTimeout");
}
private void handleMalformedResponseOrTimeout(Message request, String methodName) {
boolean noMoreXAddrs = false;
RequestSynchronizer sync = null;
try {
synchronized (DefaultDeviceReference.this) {
sync = (RequestSynchronizer) synchronizers.remove(request.getMessageId());
if (sync == null) {
if (request.getType() == DPWSMessageConstants.RESOLVE_MESSAGE) {
/*
* it is normal to receive timeouts from some
* resolve messages as we usually send resolve
* request over multiple network cards and IP
* addresses and some of them may be left unanswered
*/
if (Log.isDebug()) {
Log.debug("DefaultDeviceReference." + methodName + ": no synchronizer found for request message " + request, Log.DEBUG_LAYER_FRAMEWORK);
}
} else {
Log.warn("DefaultDeviceReference." + methodName + ": no synchronizer found for request message " + request);
}
return;
}
if (sync == resolveSynchronizer) {
resolveSynchronizer = null;
} else if (sync == getSynchronizer || sync == probeSynchronizer) {
try {
// try to retransmit the message to another
// XAddress!
if (retransmitRequest(request)) {
return;
}
} catch (NoSuchElementException e) {
noMoreXAddrs = true;
}
if (sync == getSynchronizer) {
getSynchronizer = null;
} else {
probeSynchronizer = null;
}
}
synchronizers.remove(request.getMessageId());
changeProxyReferenceState(EVENT_DEVICE_FAULT_RESET);
}
} catch (Throwable e) {
Log.warn("Unexpected exception: " + e);
}
synchronized (sync) {
if (noMoreXAddrs) {
sync.exception = new TimeoutException("No further xaddress to communicate with.");
} else {
sync.exception = new TimeoutException("Device state unknown, probably offline");
}
sync.pending = false;
sync.notifyAll();
}
}
/**
* @param request
* @param noMoreXAddrs
* @return
*/
private boolean retransmitRequest(Message request) {
XAddressInfo failedXAddress = request.getTargetXAddressInfo();
XAddressInfoSet xaddresses = discoveryData.getXAddressInfoSet();
xaddresses.remove(failedXAddress);
if (xaddresses.size() == 0) {
preferredXAddressInfo = null;
resolveSynchronizer = null;
} else {
if (preferredXAddressInfo != null && failedXAddress.equals(preferredXAddressInfo)) {
preferredXAddressInfo = (XAddressInfo) xaddresses.iterator().next();
// retransmit original request message!
request.setTargetXAddressInfo(preferredXAddressInfo);
if (request.getType() == DPWSMessageConstants.PROBE_MESSAGE) {
OutDispatcher.getInstance().send((ProbeMessage) request, preferredXAddressInfo, this);
return true;
} else if (request.getType() == DPWSMessageConstants.GET_MESSAGE) {
OutDispatcher.getInstance().send((GetMessage) request, preferredXAddressInfo, this);
return true;
} else {
// shouldn't ever happen
throw new IllegalArgumentException("Unable to retransmit unrecognized message type: " + request);
}
}
}
return false;
}
}
private class StateManager {
private int state = STATE_UNKNOWN;
private void setState(int state) {
this.state = state;
}
private int getState() {
return state;
}
private int transit(int event) {
if (location == LOCATION_LOCAL) {
throw new RuntimeException("Use of StateManager is dedicated to proxy devices!");
}
/*
* change state and announce state change
*/
switch (getState()) {
case STATE_UNKNOWN:
changeUnknownState(event);
break;
case STATE_RUNNING:
changeRunningState(event);
break;
case STATE_BUILD_UP:
changeBuildUpState(event);
break;
case STATE_STOPPED:
changeStoppedState(event);
break;
}
switch (event) {
case EVENT_DEVICE_CHANGED:
probeSynchronizer = null;
if (device != null) {
device.invalidate();
}
// removeDeviceServiceAssociation();
// device = null;
break;
case EVENT_DEVICE_COMPLETELY_DISCOVERED:
announceDeviceCompletelyDiscovered();
break;
}
return getState();
}
private void changeUnknownState(int event) {
switch (event) {
case EVENT_DEVICE_BYE:
setState(STATE_STOPPED);
announceDeviceBye();
break;
case EVENT_DEVICE_CHANGED:
setState(STATE_RUNNING);
announceDeviceRunning();
break;
case EVENT_DEVICE_GET_RSP:
setState(STATE_BUILD_UP);
announceDeviceBuildUp();
break;
case EVENT_DEVICE_SEEN:
setState(STATE_RUNNING);
announceDeviceRunning();
break;
}
}
private void changeRunningState(int event) {
switch (event) {
case EVENT_DEVICE_BYE:
setState(STATE_STOPPED);
announceDeviceBye();
break;
case EVENT_DEVICE_CHANGED:
// state: running => running
announceDeviceChanged();
break;
case EVENT_DEVICE_GET_RSP:
setState(STATE_BUILD_UP);
announceDeviceBuildUp();
break;
case EVENT_DEVICE_FAULT_RESET:
setState(STATE_UNKNOWN);
announceDeviceCommunicationErrorOrReset();
break;
}
}
private void changeBuildUpState(int event) {
switch (event) {
case EVENT_DEVICE_BYE:
setState(STATE_STOPPED);
announceDeviceBye();
break;
case EVENT_DEVICE_CHANGED:
setState(STATE_RUNNING);
announceDeviceChanged();
break;
case EVENT_DEVICE_FAULT_RESET:
setState(STATE_UNKNOWN);
announceDeviceCommunicationErrorOrReset();
break;
}
}
private void changeStoppedState(int event) {
switch (event) {
case EVENT_DEVICE_CHANGED:
setState(STATE_RUNNING);
announceDeviceChanged();
break;
case EVENT_DEVICE_GET_RSP:
setState(STATE_BUILD_UP);
announceDeviceBuildUp();
break;
case EVENT_DEVICE_SEEN:
/*
* case: device has send bye, framework didn't receive a new
* hello and somebody sends get.
*/
if (device != null) {
setState(STATE_BUILD_UP);
announceDeviceBuildUp();
} else {
setState(STATE_RUNNING);
announceDeviceRunning();
}
break;
}
}
}
/*
* (non-Javadoc)
* @see org.ws4d.java.service.reference.DeviceReference#isSecureDevice()
*/
public boolean isSecureDevice() {
return isSecure;
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.service.reference.DeviceReference#setSecureDevice(boolean)
*/
public void setSecureDevice(boolean sec) {
this.isSecure = sec;
}
private static class RequestSynchronizer {
long metadataVersion;
volatile boolean pending = true;
TimeoutException exception;
RequestSynchronizer() {
super();
this.metadataVersion = DiscoveryData.UNKNOWN_METADATA_VERSION;
}
RequestSynchronizer(DefaultDeviceReference parent) {
super();
metadataVersion = parent.discoveryData.getMetadataVersion();
}
}
private static class ResolveRequestSynchronizer extends RequestSynchronizer {
XAddressInfo xAddressInfo;
ResolveRequestSynchronizer() {
super();
}
ResolveRequestSynchronizer(DefaultDeviceReference parent) {
super(parent);
}
}
private static class GetRequestSynchronizer extends RequestSynchronizer {
Device device;
GetRequestSynchronizer() {
super();
}
GetRequestSynchronizer(DefaultDeviceReference parent) {
super(parent);
}
}
public XAddressInfo getPreferredXAddressInfo() {
return preferredXAddressInfo;
}
}