/******************************************************************************* * 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.service; import java.io.IOException; import java.util.Date; import org.ws4d.java.DPWSFramework; import org.ws4d.java.client.DefaultClient; import org.ws4d.java.communication.CommunicationBinding; import org.ws4d.java.communication.CommunicationManager; import org.ws4d.java.communication.CommunicationManagerRegistry; import org.ws4d.java.communication.CommunicationUtil; import org.ws4d.java.communication.DefaultIncomingMessageListener; import org.ws4d.java.communication.Discovery; import org.ws4d.java.communication.DiscoveryBinding; import org.ws4d.java.communication.ProtocolData; import org.ws4d.java.communication.ProtocolDomain; import org.ws4d.java.communication.protocol.http.HTTPGroup; import org.ws4d.java.communication.protocol.http.HTTPUser; import org.ws4d.java.concurrency.LockSupport; import org.ws4d.java.concurrency.Lockable; import org.ws4d.java.configuration.DeviceProperties; import org.ws4d.java.configuration.DevicesPropertiesHandler; import org.ws4d.java.configuration.Properties; import org.ws4d.java.constants.ConstantsHelper; import org.ws4d.java.constants.DPWSMessageConstants; import org.ws4d.java.dispatch.DefaultDeviceReference; import org.ws4d.java.dispatch.DeviceServiceRegistry; import org.ws4d.java.dispatch.OutDispatcher; import org.ws4d.java.message.SOAPException; 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.ResolveMatch; 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.presentation.Presentation; import org.ws4d.java.service.reference.DeviceReference; import org.ws4d.java.service.reference.ServiceReference; import org.ws4d.java.structures.ArrayList; 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.MessageIdBuffer; import org.ws4d.java.structures.ReadOnlyIterator; import org.ws4d.java.structures.Set; import org.ws4d.java.types.AppSequence; import org.ws4d.java.types.CustomizeMData; import org.ws4d.java.types.DiscoveryData; import org.ws4d.java.types.EndpointReference; import org.ws4d.java.types.EprInfo; import org.ws4d.java.types.EprInfoSet; import org.ws4d.java.types.HostMData; import org.ws4d.java.types.HostedMData; import org.ws4d.java.types.LocalizedString; import org.ws4d.java.types.ProbeScopeSet; import org.ws4d.java.types.QName; import org.ws4d.java.types.QNameSet; import org.ws4d.java.types.RelationshipMData; import org.ws4d.java.types.ScopeSet; import org.ws4d.java.types.ThisDeviceMData; import org.ws4d.java.types.ThisModelMData; 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.IDGenerator; import org.ws4d.java.util.Log; import org.ws4d.java.util.StringUtil; import org.ws4d.java.util.WS4DIllegalStateException; /** * Implementation of a local DPWS device. A device in DPWS is a web service with * specific functions. It can be discovered via probing by clients and it * supports resolving of its endpoint. A device bears metadata information and * services. * <p> * This class implements a local device within the framework. Its metadata can * be configured and services can be added. The configuration can be done by use * of a configuration file/stream via the {@link Properties} class. In this * case, the constructor {@link #DefaultDevice(int configurationId)} must be * used with the configuration id of the matching device properties. * </p> * To receive messages, a communication binding {@link CommunicationBinding} * must be added to the device. In DPWS, this binding must be a * <code>org.ws4d.java.communication.HTTPBinding</code>, so that the device can * receive get messages. * <p> * A DefaultDevice has to be started before becoming fully functional. Starting * the device will establish the binding, i. e. a socket will be opened and the * http server will listen to the address of the binding. In DPWS, a multicast * listener will also be bound to the device. A hello message will then be sent * to all connected networks. Residing services will also be started. Stopping * the device will initiate the sending of a bye message, its services will be * stopped and the binding will be removed. * </p> * Example code: <code> * <pre> * DPWSFramework.start(args); * ... * DefaultDevice device = new DefaultDevice(); * HTTPBinding binding = new HTTPBinding( ip, port, "SimpleExampleDevice" ); * * device.addBinding( binding ); * device.addService( service ); * device.addFriendlyName( "en-US", "JMEDS Simple Device" ); * * device.start(); * </pre> * </code> * <p> * <strong>Important:</strong> Setting/Adding device data includes getting the * exclusive lock (({@link Lockable}) for the device.<BR> * If the device is running, each change will cause a hello message to be sent * with an incremented metadata version. To combine multiple device data changes * with sending only one hello message, the exclusive lock has to be first be * obtained by {@link #exclusiveLock()}. After the last device data change, * releasing the exclusive lock through {@link #releaseExclusiveLock()} will * send a single hello with an incremented metadata version. * </p> * <p> * A DefaultDevice will respond to the following request message types: * <ul> * <li>Probe Message - {@link ProbeMessage} * <li>Resolve Message - {@link ResolveMessage} * <li>Get Message - {@link GetMessage} * </ul> * with the appropriate response message types: * <ul> * <li>Probe Matches Message - {@link ProbeMatchesMessage} * <li>Resolve Matches Message - {@link ResolveMatchesMessage} * <li>Get Response Message - {@link GetResponseMessage} * </ul> * Additionally the device initiates the sending of the following message types: * <ul> * <li>Hello Message - {@link HelloMessage} * <li>Bye Message - {@link ByeMessage} * </ul> * </p> * The DefaultDevice class implements the functionality of a Target Service * described in the WSDD-Discovery specification. This version supports only the * Ad hoc operational mode. */ public class DefaultDevice extends DeviceCommons implements LocalDevice { private static final int[] DISCOVERY_MESSAGE_TYPES = { DPWSMessageConstants.PROBE_MESSAGE, DPWSMessageConstants.RESOLVE_MESSAGE }; private static final int[] DEVICE_MESSAGE_TYPES = { DPWSMessageConstants.GET_MESSAGE, DPWSMessageConstants.PROBE_MESSAGE }; /** Configuration id */ protected final int configurationId; protected final DeviceMessageListener incomingListener = new DeviceMessageListener(this); /** Lock */ private final Lockable lockSupport = new LockSupport(); /** Device reference of this device */ protected DefaultDeviceReference myDeviceRef = null; protected DiscoveryData discoveryData; /** Set of services attached to this device. */ protected final Set services = new HashSet(); protected boolean running = false; protected boolean changed = false; protected boolean discoveryDataChanged = true; protected boolean isMetadataVersionSet = false; protected final AppSequenceManager appSequencer = new AppSequenceManager(); protected DataStructure transportBindings; protected DataStructure inputDiscoveryBindings; protected final DataStructure outputDiscoveryDomains = new HashSet(); protected final DeviceProperties deviceProp; protected boolean usingDefaultDiscoveryDomains = false; /** DiscoveryProxy */ private boolean isDiscoveryProxy = false; // DEFAULT VALUES protected String defaultLanugaugeString = "en-EN"; protected LocalizedString defaultFriendlyName = new LocalizedString(StringUtil.simpleClassName(getClass()), defaultLanugaugeString); protected LocalizedString defaultModelName = defaultFriendlyName; protected LocalizedString defaultManufacturer = new LocalizedString("MATERNA GmbH", null); private String namespace = "http://ws4d.org"; private final MessageIdBuffer messageIdBuffer = new MessageIdBuffer(); public static final int MAX_QNAME_SERIALIZATION = 10; /** Authentication */ private HTTPGroup userGroup = null; public static boolean hasCustomizeMData = false; private HashMap mdata = new HashMap(); public CustomizeMData custom = new CustomizeMData(); /** * Constructor local DPWS device. No device properties of the properties * file/stream {@link Properties} are used to build up the device. * <p> * <strong>Important:</strong> It is necessary to * {@link #addBinding(CommunicationBinding binding) add a binding} to a * device before it can be started. * </p> */ public DefaultDevice() { this(-1); } /** * Constructor of local DPWS device. The given configuration id should map * to the device property entries in the configuration file/stream * {@link Properties}. The property entries of this device will be gathered * in a {@link DeviceProperties} object and used to build up the device and * its metadata. * <p> * <strong>Important:</strong> It is necessary to * {@link #addBinding(CommunicationBinding binding) add a binding} to a * device before it can be started. The binding may be specified within the * configuration file/stream. * </p> * * @param configurationId The configuration id that map to the device * properties within the configuration file/stream. */ public DefaultDevice(int configurationId) { super(); this.configurationId = configurationId; if (this.configurationId != -1) { DevicesPropertiesHandler propHandler = DevicesPropertiesHandler.getInstance(); deviceProp = propHandler.getDeviceProperties(new Integer(configurationId)); /* * Reads configuration */ discoveryData = deviceProp.getDiscoveryData(); deviceMetadata = deviceProp.getDeviceData(); modelMetadata = deviceProp.getModelData(); transportBindings = deviceProp.getBindings(); // if (transportBindings.size() > 0) { // for (Iterator it = transportBindings.iterator(); it.hasNext();) { // CommunicationBinding binding = (CommunicationBinding) it.next(); // addXAddress(binding.getTransportAddress()); // } // } inputDiscoveryBindings = deviceProp.getDiscoveryBindings(); if (getEndpointReference() == null) { // sets random UUID. EndpointReference epr = new EndpointReference(IDGenerator.getUUIDasURI()); setEndpointReference(epr); } if (getMetadataVersion() < 0) { /* * sets metadata version based on system time. */ setMetadataVersion((int) ((new Date()).getTime() / 1000)); } if (deviceProp.useSecurity()) { setSecureDevice(); } // propHandler. } else { deviceProp = null; transportBindings = new ArrayList(2); inputDiscoveryBindings = new ArrayList(2); discoveryData = new DiscoveryData(); /* * sets random UUID. */ EndpointReference epr = new EndpointReference(IDGenerator.getUUIDasURI()); setEndpointReference(epr); /* * sets metadata version based on system time. */ setMetadataVersion((int) ((new Date()).getTime() / 1000)); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#isRemote() */ public boolean isRemote() { return false; } /* * (non-Javadoc) * @see org.ws4d.java.service.LocalDevice#isRunning() */ public boolean isRunning() { sharedLock(); try { return running; } finally { releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.communication.Bindable#hasBindings() */ public boolean hasBindings() { return (transportBindings.size() > 0); } public boolean hasDiscoveryBindings() { return (inputDiscoveryBindings != null && inputDiscoveryBindings.size() > 0); } /* * (non-Javadoc) * @see org.ws4d.java.communication.Bindable#getBindings() */ public Iterator getBindings() { return new ReadOnlyIterator(transportBindings); } /* * (non-Javadoc) * @see org.ws4d.java.communication.Bindable#getBindings() */ public Iterator getDiscoveryBindings() { return new ReadOnlyIterator(inputDiscoveryBindings); } /* * (non-Javadoc) * @see org.ws4d.java.communication.Bindable#supportsBindingChanges() */ public boolean supportsBindingChanges() { lockSupport.sharedLock(); try { return !isRunning(); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see * org.ws4d.java.communication.Bindable#addBinding(org.ws4d.java.communication * .CommunicationBinding) */ public void addBinding(CommunicationBinding binding) throws WS4DIllegalStateException { lockSupport.exclusiveLock(); try { if (isRunning()) { throw new WS4DIllegalStateException("Device is already running, unable to add binding"); } transportBindings.add(binding); // addXAddress(binding.getTransportAddress()); } finally { lockSupport.releaseExclusiveLock(); } } /* * (non-Javadoc) * @see * org.ws4d.java.communication.Bindable#addBinding(org.ws4d.java.communication * .CommunicationBinding) */ public void addBinding(DiscoveryBinding binding) throws WS4DIllegalStateException { lockSupport.exclusiveLock(); try { if (isRunning()) { throw new WS4DIllegalStateException("Device is already running, unable to add binding"); } if (binding instanceof DiscoveryBinding) { inputDiscoveryBindings.add(binding); } else { throw new WS4DIllegalStateException("Unsupported binding type: " + binding); } } finally { lockSupport.releaseExclusiveLock(); } } /* * (non-Javadoc) * @seeorg.ws4d.java.communication.Bindable#removeBinding(org.ws4d.java. * communication.CommunicationBinding) */ public boolean removeBinding(CommunicationBinding binding) throws WS4DIllegalStateException { lockSupport.exclusiveLock(); try { if (isRunning()) { throw new WS4DIllegalStateException("Device is already running, unable to remove binding"); } // removeXAddress(binding.getTransportAddress()); return transportBindings.remove(binding); } finally { lockSupport.releaseExclusiveLock(); } } /* * (non-Javadoc) * @seeorg.ws4d.java.communication.Bindable#removeBinding(org.ws4d.java. * communication.CommunicationBinding) */ public boolean removeBinding(DiscoveryBinding binding) throws WS4DIllegalStateException { lockSupport.exclusiveLock(); try { if (isRunning()) { throw new WS4DIllegalStateException("Device is already running, unable to remove binding"); } if (binding instanceof DiscoveryBinding) { return inputDiscoveryBindings.remove(binding); } else { throw new WS4DIllegalStateException("Unsupported binding type: " + binding); } } finally { lockSupport.releaseExclusiveLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.communication.Bindable#clearBindings() */ public void clearBindings() throws WS4DIllegalStateException { lockSupport.exclusiveLock(); try { if (isRunning()) { throw new WS4DIllegalStateException("Device is already running, unable to clear bindings"); } // for (Iterator it = transportBindings.iterator(); it.hasNext();) { // CommunicationBinding binding = (CommunicationBinding) it.next(); // removeXAddress(binding.getTransportAddress()); // it.remove(); // } transportBindings.clear(); inputDiscoveryBindings.clear(); } finally { lockSupport.releaseExclusiveLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getDeviceReference() */ public DeviceReference getDeviceReference() { if (myDeviceRef == null) { myDeviceRef = DeviceServiceRegistry.getDeviceReference(this); myDeviceRef.setLocalDevice(this); } return myDeviceRef; } /** * Starts the device. Starting the device will: * <ul> * <li>start its services, * <li>bind each {@link CommunicationBinding} to the matching * {@link CommunicationManager}, i.e. start listening to incoming messages * for the specified address, * <li>registers the device to the {@link DeviceServiceRegistry}. * </ul> * * @throws IOException is thrown, if a binding couldn't be bound to the * communication manager or if starting a service will throw the * exception. */ public final void start() throws IOException { if (!DPWSFramework.isRunning()) { throw new RuntimeException("DPWSFramework not running, please start it in advance!"); } lockSupport.exclusiveLock(); try { if (isRunning()) { Log.warn("Cannot start device. Device already running."); return; } QNameSet collectedDeviceTypes = new QNameSet(); /* * Add default values of mandatory device metadata if necessary */ if (deviceMetadata.getFriendlyNames().size() == 0) { deviceMetadata.addFriendlyName(defaultFriendlyName); } if (modelMetadata.getManufacturerNames().size() == 0) { modelMetadata.addManufacturerName(defaultManufacturer); } if (modelMetadata.getModelNames().size() == 0) { modelMetadata.addModelName(defaultModelName); } if (Log.isDebug()) { Log.debug("Start Device: " + deviceMetadata.getFriendlyNames().iterator().next()); } boolean hasInputDiscoveryBindings = hasDiscoveryBindings(); boolean hasTransportBindings = hasBindings(); boolean hasOutputDiscoveryDomain = !isUsingDefaultDiscoveryDomains(); if (!hasTransportBindings) { String descriptor = StringUtil.simpleClassName(getClass()); if (Log.isDebug()) { Log.info("No bindings found for Device. Autobinding device " + descriptor); } DataStructure autoBindings = new HashSet(); for (Iterator it = CommunicationManagerRegistry.getLoadedManagers(); it.hasNext();) { CommunicationManager manager = (CommunicationManager) it.next(); manager.getAutobindings(descriptor, autoBindings); } for (Iterator it = autoBindings.iterator(); it.hasNext();) { CommunicationBinding binding = (CommunicationBinding) it.next(); addBinding(binding); } } if (hasInputDiscoveryBindings && hasOutputDiscoveryDomain) { // everything is ok. Nothing to do. } else if (!hasInputDiscoveryBindings && hasOutputDiscoveryDomain) { for (Iterator it = CommunicationManagerRegistry.getLoadedManagers(); it.hasNext();) { CommunicationManager manager = (CommunicationManager) it.next(); for (Iterator it2 = getOutputDiscoveryDomains().iterator(); it2.hasNext();) { ProtocolDomain domain = (ProtocolDomain) it2.next(); inputDiscoveryBindings.add(manager.getDiscoveryBindingForDomain(domain)); } } } else if (hasInputDiscoveryBindings && !hasOutputDiscoveryDomain) { for (Iterator it = CommunicationManagerRegistry.getLoadedManagers(); it.hasNext();) { CommunicationManager manager = (CommunicationManager) it.next(); for (Iterator it2 = getDiscoveryBindings(); it2.hasNext();) { DataStructure d = manager.getDiscoveryDomainForBinding((DiscoveryBinding) it2.next()); for (Iterator itpd = d.iterator(); itpd.hasNext();) { addOutputDiscoveryDomain((ProtocolDomain) itpd.next()); } } } } else { // if has no input and output Discovery information -> take // informations from HTTP-Binding! for (Iterator it = CommunicationManagerRegistry.getLoadedManagers(); it.hasNext();) { DataStructure domains = new HashSet(); CommunicationManager manager = (CommunicationManager) it.next(); manager.getProtocolDomains(getBindings(), domains); if (domains.size() > 0) { for (Iterator iti = domains.iterator(); iti.hasNext();) { ProtocolDomain domain = (ProtocolDomain) iti.next(); inputDiscoveryBindings.add(manager.getDiscoveryBindingForDomain(domain)); addOutputDiscoveryDomain(domain); } } } } for (Iterator it = getBindings(); it.hasNext();) { CommunicationBinding binding = (CommunicationBinding) it.next(); CommunicationManager manager = CommunicationManagerRegistry.getManager(binding.getCommunicationManagerId()); collectedDeviceTypes.addAll(manager.getDeviceTypes()); manager.registerDevice(DEVICE_MESSAGE_TYPES, binding, incomingListener, userGroup); addXAddressInfo(new XAddressInfo(binding.getTransportAddress(), manager.getCommunicationManagerId())); } for (Iterator it = getDiscoveryBindings(); it.hasNext();) { DiscoveryBinding binding = (DiscoveryBinding) it.next(); CommunicationManager manager = CommunicationManagerRegistry.getManager(binding.getCommunicationManagerId()); collectedDeviceTypes.addAll(manager.getDeviceTypes()); manager.registerDiscovery(DISCOVERY_MESSAGE_TYPES, binding, incomingListener); } for (Iterator it = services.iterator(); it.hasNext();) { LocalService service = (LocalService) it.next(); service.setParentDevice(this); service.start(); } if (Log.isInfo()) { StringBuffer sb = new StringBuffer(); for (Iterator it = getXAddressInfos(); it.hasNext();) { sb.append(((XAddressInfo) it.next()).getXAddress()); if (it.hasNext()) { sb.append(", "); } } Log.info("Device [ UUID=" + this.getEndpointReference().getAddress() + ", XAddresses={ " + sb.toString() + " } ] online."); } discoveryData.addTypes(collectedDeviceTypes); appSequencer.reset(); // flag must be reseted, else initial started stack won't // updates metadata version with the first change isMetadataVersionSet = false; Presentation p = DPWSFramework.getPresentation(); if (p != null) { try { URI presentationURL = p.register(this); this.setPresentationUrl(presentationURL.toString()); } catch (RuntimeException e) { Log.printStackTrace(e); } } // / Registers device reference in framework getDeviceReference(); running = true; changed = false; } finally { lockSupport.releaseExclusiveLock(); } DeviceServiceRegistry.register(this); HelloMessage hello = createHelloMessage(); OutDispatcher.getInstance().send(hello, null, getOutputDiscoveryDomains()); if (myDeviceRef != null) { if (changed) { myDeviceRef.announceDeviceChangedAndBuildUp(); } else { myDeviceRef.announceDeviceRunningAndBuildUp(); } } } /** * Stops the device. Stopping the device will: * <ul> * <li>stop its services, * <li>unbind each {@link CommunicationBinding} to the matching * {@link CommunicationManager}, * <li>unregisters the device from the {@link DeviceServiceRegistry}. * </ul> * * @throws IOException is thrown if a binding couldn't be unbound or if * stopping a service will throw the exception. */ public final void stop() throws IOException { stop(true); } /** * Stops the device. Stopping the device will: * <ul> * <li>unbind each {@link CommunicationBinding} to the matching * {@link CommunicationManager}, * <li>unregisters the device from the {@link DeviceServiceRegistry}. * </ul> * * @param stopServices If true, stops services too. * @throws IOException is thrown if a binding couldn't be unbound or if * stopping a service will throw the exception. */ public final void stop(boolean stopServices) throws IOException { lockSupport.exclusiveLock(); try { if (!isRunning()) { Log.warn("Cannot stop device. Device not running."); return; } DeviceServiceRegistry.unregister(this); for (Iterator it = getBindings(); it.hasNext();) { CommunicationBinding binding = (CommunicationBinding) it.next(); CommunicationManager manager = CommunicationManagerRegistry.getManager(binding.getCommunicationManagerId()); removeXAddressInfo(new XAddressInfo(binding.getTransportAddress(), manager.getCommunicationManagerId())); manager.unregisterDevice(DEVICE_MESSAGE_TYPES, binding, incomingListener); } for (Iterator it = getDiscoveryBindings(); it.hasNext();) { DiscoveryBinding binding = (DiscoveryBinding) it.next(); CommunicationManager manager = CommunicationManagerRegistry.getManager(binding.getCommunicationManagerId()); manager.unregisterDiscovery(DISCOVERY_MESSAGE_TYPES, binding, incomingListener); } if (stopServices) { for (Iterator it = services.iterator(); it.hasNext();) { LocalService service = (LocalService) it.next(); service.stop(); } } Log.info("Device [ UUID=" + this.getEndpointReference().getAddress() + " ] offline."); sendBye(); if (myDeviceRef != null) { myDeviceRef.announceDeviceBye(); } running = false; } finally { lockSupport.releaseExclusiveLock(); } } /** * Sends hello message. Simple method to announce the device is in the * network. * <p> * <strong>Important:</strong> This method won't start the device. But * starting this device will automatically send a hello message. * </p> */ public void sendHello() { HelloMessage hello = createHelloMessage(); OutDispatcher.getInstance().send(hello, null, getOutputDiscoveryDomains()); } /** * Sends Bye Message. Simple method to send a bye message to the network. * <p> * <strong>Important:</strong> This method won't stop the device. But * stopping this device will automatically send a bye message. * </p> */ public void sendBye() { DiscoveryData data = new DiscoveryData(); data.setEndpointReference(discoveryData.getEndpointReference()); data.setXAddresInfoSet(discoveryData.getXAddressInfoSet()); ByeMessage bye = new ByeMessage(data, CommunicationManager.ID_NULL); bye.getHeader().setAppSequence(appSequencer.getNext()); OutDispatcher.getInstance().send(bye, null, getOutputDiscoveryDomains()); } /** * Increments metadata version by one and send hello, inform local device * update listener. */ private void deviceUpdated() { lockSupport.exclusiveLock(); HelloMessage hello = null; try { // if (!isRunning()) { // // fire hellos only if currently up // return; // } if (!isMetadataVersionSet) { /* * We only increment version, if not set by user. */ copyDiscoveryDataIfRunning(); long metadataVersion = discoveryData.getMetadataVersion(); metadataVersion++; discoveryData.setMetadataVersion(metadataVersion); } else { isMetadataVersionSet = false; } if (running) { hello = createHelloMessage(); } } finally { discoveryDataChanged = false; lockSupport.releaseExclusiveLock(); if (hello != null) { OutDispatcher.getInstance().send(hello, null, getOutputDiscoveryDomains()); if (myDeviceRef != null) { myDeviceRef.announceDeviceChangedAndBuildUp(); } changed = false; } } } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#sharedLock() */ public void sharedLock() { lockSupport.sharedLock(); } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#exclusiveLock() */ public void exclusiveLock() { lockSupport.exclusiveLock(); } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#releaseSharedLock() */ public void releaseSharedLock() { lockSupport.releaseSharedLock(); } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#releaseExclusiveLock() */ public boolean releaseExclusiveLock() { boolean isLastLockReleased = lockSupport.releaseExclusiveLock(); if (isLastLockReleased && changed) { changed = false; deviceUpdated(); } return isLastLockReleased; } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#tryExclusiveLock() */ public boolean tryExclusiveLock() { return lockSupport.tryExclusiveLock(); } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#trySharedLock() */ public boolean trySharedLock() { return lockSupport.trySharedLock(); } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getEndpointReferences() */ public EndpointReference getEndpointReference() { lockSupport.sharedLock(); try { return discoveryData.getEndpointReference(); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getPortTypes() */ public Iterator getPortTypes() { lockSupport.sharedLock(); try { QNameSet types = discoveryData.getTypes(); return types == null ? EmptyStructures.EMPTY_ITERATOR : new ReadOnlyIterator(types.iterator()); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getScopes() */ public Iterator getScopes() { lockSupport.sharedLock(); try { ScopeSet scopes = discoveryData.getScopes(); URISet uriScopes = (scopes == null) ? null : scopes.getScopesAsUris(); return (uriScopes == null) ? EmptyStructures.EMPTY_ITERATOR : new ReadOnlyIterator(uriScopes.iterator()); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getManufacturer(java.lang.String) */ public String getManufacturer(String lang) { lockSupport.sharedLock(); try { return super.getManufacturer(lang); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getManufacturers() */ public Iterator getManufacturers() { lockSupport.sharedLock(); try { return super.getManufacturers(); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getManufacturerUrl() */ public String getManufacturerUrl() { lockSupport.sharedLock(); try { return super.getManufacturerUrl(); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getModelName(java.lang.String) */ public String getModelName(String lang) { lockSupport.sharedLock(); try { return super.getModelName(lang); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getModelNames() */ public Iterator getModelNames() { lockSupport.sharedLock(); try { return super.getModelNames(); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getModelNumber() */ public String getModelNumber() { lockSupport.sharedLock(); try { return super.getModelNumber(); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getModelUrl() */ public String getModelUrl() { lockSupport.sharedLock(); try { return super.getModelUrl(); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getPresentationUrl() */ public String getPresentationUrl() { lockSupport.sharedLock(); try { return super.getPresentationUrl(); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getFriendlyName(java.lang.String) */ public String getFriendlyName(String lang) { lockSupport.sharedLock(); try { return super.getFriendlyName(lang); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getFriendlyNames() */ public Iterator getFriendlyNames() { lockSupport.sharedLock(); try { return super.getFriendlyNames(); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getFirmwareVersion() */ public String getFirmwareVersion() { lockSupport.sharedLock(); try { return super.getFirmwareVersion(); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getSerialNumber() */ public String getSerialNumber() { lockSupport.sharedLock(); try { return super.getSerialNumber(); } finally { lockSupport.releaseSharedLock(); } } /** * Sets the {@link EndpointReference} of this device. The endpoint reference * bears a stable globally-unique identifier of the device. This address * part is typically not a physical address. <BR> * If not set, the framework generates it automatically. The address part of * the endpoint reference can be configured via the {@link Properties}. * * @param endpoint The endpoint reference to set. */ public void setEndpointReference(EndpointReference endpoint) { if (endpoint == null) { throw new IllegalArgumentException("endpoint reference must not be null"); } lockSupport.exclusiveLock(); try { copyDiscoveryDataIfRunning(); discoveryData.setEndpointReference(endpoint); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Sets the port types of the device. This port types should show clients in * the network which services the device may hold. Clients (see * {@link DefaultClient}) can search for the specific device port types. * <p> * The port types are communicated via the hello, probe matches, resolve * matches, get response and the get metadata response messages (the * "wsdd:Types" elements and the be "dpws:Types" elements of host metadata). * </p> * <p> * The "dpws:Device" port type is added by default. * </p> * * @param qnsPortTypes Device port types to set. */ public void setPortTypes(QNameSet qnsPortTypes) { lockSupport.exclusiveLock(); try { if (qnsPortTypes == null) { qnsPortTypes = new QNameSet(); } copyDiscoveryDataIfRunning(); discoveryData.setTypes(qnsPortTypes); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Adds {@link XAddressInfo} to device. * * @param xAdrInfo */ private void addXAddressInfo(XAddressInfo xAdrInfo) { lockSupport.exclusiveLock(); try { copyDiscoveryDataIfRunning(); XAddressInfoSet xAddresses = discoveryData.getXAddressInfoSet(); if (xAddresses == null) { xAddresses = new XAddressInfoSet(); discoveryData.setXAddresInfoSet(xAddresses); } xAddresses.add(xAdrInfo); } finally { lockSupport.releaseExclusiveLock(); } } // /** // * Sets list of xaddresses. // * // * @param xAddresses xaddresses to set // */ // public void setXAddresses(URISet xAddresses) { // lockSupport.exclusiveLock(); // try { // discoveryData.setXAddrs(xAddresses); // } finally { // lockSupport.releaseExclusiveLock(); // } // } // /** * Removes {@link XAddressInfo} from device. * * @param xAdrInfo */ private void removeXAddressInfo(XAddressInfo xAdrInfo) { lockSupport.exclusiveLock(); try { copyDiscoveryDataIfRunning(); XAddressInfoSet xAddresses = discoveryData.getXAddressInfoSet(); if (xAddresses != null && xAdrInfo != null) { xAddresses.remove(xAdrInfo); } } finally { lockSupport.releaseExclusiveLock(); } } /** * Sets a list of scopes. Scopes are used within the discovery of devices. A * client may search for devices with specific scopes. <BR> * Scopes are part of the hello, probe matches, resolve matches messages. * <p> * Setting the scopes includes getting the exclusive lock (({@link Lockable} * ) of the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change, releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param scopes List of scopes to set. */ public void setScopes(ScopeSet scopes) { lockSupport.exclusiveLock(); try { copyDiscoveryDataIfRunning(); discoveryData.setScopes(scopes); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Adds manufacturer name to the device which is used as value of the * "dpws:Manufacturer" element in the model metadata. The manufacturer name * is language specific. * <p> * Adding the manufacturer name includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change, releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param lang Language attribute, i. e. "en-US or "de-DE": * <ul> * <li>The syntax of the language tags is described in RFC 5646. * <li>All language subtags are registered to the IANA Language * Subtag Registry. * <li>All region subtags are specified in * "ISO 3166: Codes for Country Names". * </ul> * @param manufacturer The manufacturer name to set in the specified * language. */ public void addManufacturer(String lang, String manufacturer) { lockSupport.exclusiveLock(); try { modelMetadata.addManufacturerName(new LocalizedString(manufacturer, lang)); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Sets the url of the manufacturer. It used as the value of the * "dpws:ManufacturerUrl" element of the model metadata. * <p> * Setting the manufacturer url includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change, releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param manufacturerUrl The url of the manufacturer to set. */ public void setManufacturerUrl(String manufacturerUrl) { lockSupport.exclusiveLock(); try { modelMetadata.setManufacturerUrl(new URI(manufacturerUrl)); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Adds a model name to the device. The model name is used as value of the * "dpws:ModelName" element in the model metadata. The model name is * language specific. * <p> * Adding a model name includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param lang Language attribute, i. e. "en-US or "de-DE": * <ul> * <li>The syntax of the language tags is described in RFC 5646. * <li>All language subtags are registered to the IANA Language * Subtag Registry. * <li>All region subtags are specified in * "ISO 3166: Codes for Country Names". * </ul> * @param modelName The model name to set in the specified language. */ public void addModelName(String lang, String modelName) { lockSupport.exclusiveLock(); try { modelMetadata.addModelName(new LocalizedString(modelName, lang)); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Sets the model number of the device. The model number is used as value of * the "dpws:ModelNumber" element in the model metadata. * <p> * Setting the model number includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through{@link #exclusiveLock()}. After the last device * data change the release of the exclusive lock by * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param modelNumber The model number of the device to set. */ public void setModelNumber(String modelNumber) { lockSupport.exclusiveLock(); try { modelMetadata.setModelNumber(modelNumber); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Sets the model url of the device. The model url is used as value of the * "dpws:ModelUrl" element of the model metadata. * <p> * Setting the model url includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be taken by {@link #exclusiveLock()}. After the last device data * change the release of the exclusive lock by * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param modelUrl The model url of the device to set. */ public void setModelUrl(String modelUrl) { lockSupport.exclusiveLock(); try { modelMetadata.setModelUrl(new URI(modelUrl)); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Sets the presentation url of the device. It is used as value of the * "dpws:PresentationUrl" element of the model metadata. * <p> * Setting the presentation url includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param presentationUrl The presentation url to set. */ public void setPresentationUrl(String presentationUrl) { lockSupport.exclusiveLock(); try { modelMetadata.setPresentationUrl(new URI(presentationUrl)); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Adds a friendly name to the device. It is used as the value of the * "dpws:FriendlyName" element of the device metadata. The friendly name is * language specific. * <p> * Adding a friendly name includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param lang Language attribute, i. e. "en-US or "de-DE": * <ul> * <li>The syntax of the language tags is described in RFC 5646. * <li>All language subtags are registered to the IANA Language * Subtag Registry. * <li>All region subtags are specified in * "ISO 3166: Codes for Country Names". * </ul> * @param friendlyName The friendly name of the device in the specified * language to be set. */ public void addFriendlyName(String lang, String friendlyName) { lockSupport.exclusiveLock(); try { deviceMetadata.addFriendlyName(new LocalizedString(friendlyName, lang)); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Sets the firmware version to the device. It is used as the value of the * "dpws:FirmwareVersion" element of the device metadata. * <p> * Setting the firmware version includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param firmware The firmware version of the device to set. */ public void setFirmwareVersion(String firmware) { lockSupport.exclusiveLock(); try { deviceMetadata.setFirmwareVersion(firmware); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Sets the serial number of the device. It is used as the value of the * "wsdp:SerialNumber" element of the device metadata. * <p> * Setting the serial number version includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param serialNumber The serial number of the device to set. */ public void setSerialNumber(String serialNumber) { lockSupport.exclusiveLock(); try { deviceMetadata.setSerialNumber(serialNumber); changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Adds service to device. * <p> * NOTICE: If the device is already running, you must start the service with * the start() method, or use the addService(LocalService, boolean) method. * </p> * <p> * Adding a service to the device includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @see org.ws4d.java.service.LocalDevice#addService(org.ws4d.java.service.LocalService, * boolean) * @param service service to add to this device. */ public void addService(LocalService service) { try { addService(service, true); } catch (IOException e) { // THIS should NEVER happen! Because we don't start the service! Log.error("Oh shit! I got an exception while adding a service. Shit should NEVER happen here!"); } } /** * Adds a service to the device. * <p> * Adding a service to the device includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change, releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param service service to add to this device. * @param startIfRunning <code>true</code> the service is started if the * device is already running, <code>false</code> the service has * not been not started, we just add it. */ public void addService(LocalService service, boolean startIfRunning) throws IOException { lockSupport.exclusiveLock(); try { service.setParentDevice(this); services.add(service); if (isRunning() && startIfRunning) { service.start(); } changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Removes service from device. The service will be removed from the device, * but won't be stopped. * <p> * Removing a service from the device includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param service The service to remove from this device. */ public void removeService(LocalService service) { try { removeService(service, false); } catch (IOException e) { // THIS should NEVER happen! Because we don't start the service! Log.error("Oh shit! I got an exception while adding a service. Shit should NEVER happen here!"); } } /** * Removes a service from the device. If stopIfRunning is * <code>true<code> the service to remove is stopped if running, else not. * <p> * Removing a service from the device includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through{@link #exclusiveLock()}. After the last device data * change releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param service The service to remove from the device. * @param stopIfRunning <code>true</code> the service is stopped if the * service is running, <code>false</code> just remove. */ public void removeService(LocalService service, boolean stopIfRunning) throws IOException { lockSupport.exclusiveLock(); try { services.remove(service); if (service.isRunning() && stopIfRunning) { service.stop(); } } finally { if (lockSupport.releaseExclusiveLock()) { if (isRunning()) { deviceUpdated(); } else { changed = true; } } } } /** * Sets the device metadata of the device. It contains different device * metadata and is transmitted to the "dpws:ThisDevice" metadata. * <p> * Setting the device metadata includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to obtained through {@link #exclusiveLock()}. After the last device data * change, releasing the exclusive lock with {@link #releaseExclusiveLock()} * will send a single hello with an incremented metadata version. * </p> * * @param deviceMetadata */ public void setDeviceMetadata(ThisDeviceMData deviceMetadata) { lockSupport.exclusiveLock(); try { this.deviceMetadata = deviceMetadata; changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Sets the metadata version of the device. The metadata version is part of * some discovery messages of the device. If it is incremented, clients * receiving this new metadata version have to update the device's * information. * <p> * Setting the metadata version includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change, releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with the new * metadata version. * </p> * * @param metadataVersion The metadata version to set is of type unsigned * int. */ public void setMetadataVersion(long metadataVersion) { lockSupport.exclusiveLock(); try { copyDiscoveryDataIfRunning(); this.discoveryData.setMetadataVersion(metadataVersion); isMetadataVersionSet = true; changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /** * Sets the model metadata of the device. It contains different model meta * data and is transmitted via the "dpws:ThisModel" metadata. * <p> * Setting the model metadata version includes getting the exclusive lock (( * {@link Lockable}) for the device.<BR> * If the device is running, each change will initiate the sending of a * hello message with an incremented metadata version. To combine multiple * device data changes with sending only one hello, the exclusive lock has * to be obtained through {@link #exclusiveLock()}. After the last device * data change releasing the exclusive lock with * {@link #releaseExclusiveLock()} will send a single hello with an * incremented metadata version. * </p> * * @param modelMetadata The model metadata of the device to set. */ public void setModelMetadata(ThisModelMData modelMetadata) { lockSupport.exclusiveLock(); try { this.modelMetadata = modelMetadata; changed = true; } finally { if (lockSupport.releaseExclusiveLock()) { deviceUpdated(); } } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getDeviceMetadata() */ public ThisDeviceMData getDeviceMetadata() { lockSupport.sharedLock(); try { return deviceMetadata; } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getMetadataVersion() */ public long getMetadataVersion() { lockSupport.sharedLock(); try { return discoveryData.getMetadataVersion(); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getModelMetadata() */ public ThisModelMData getModelMetadata() { lockSupport.sharedLock(); try { return modelMetadata; } finally { lockSupport.releaseSharedLock(); } } /** * Gets iterator over all services. A service is of type {@link Service}. * * @return Iterator over all services of type {@link Service}. */ public Iterator getServices() { return new ReadOnlyIterator(services); } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getServiceReferences() */ public Iterator getServiceReferences() { lockSupport.sharedLock(); try { Set servRefs = new HashSet(services.size()); for (Iterator it = services.iterator(); it.hasNext();) { Service service = (Service) it.next(); servRefs.add(service.getServiceReference()); } return new ReadOnlyIterator(servRefs); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see * org.ws4d.java.service.Device#getServiceReferences(org.ws4d.java.types * .QNameSet) */ public Iterator getServiceReferences(QNameSet servicePortTypes) { Set matchingServRefs = new HashSet(); addServiceReferences(matchingServRefs, servicePortTypes); return new ReadOnlyIterator(matchingServRefs); } /* * (non-Javadoc) * @see * org.ws4d.java.service.LocalDevice#addMatchingServiceRefs(org.ws4d.java * .structures.DataStructure, org.ws4d.java.types.QNameSet) */ public void addServiceReferences(DataStructure to, QNameSet servicePortTypes) { lockSupport.sharedLock(); try { for (Iterator it = services.iterator(); it.hasNext();) { Service service = (Service) it.next(); if (servicePortTypes.isContainedBy(service.getPortTypes())) { to.add(service.getServiceReference()); } } } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see * org.ws4d.java.service.Device#getServiceReference(org.ws4d.java.types.URI) */ public ServiceReference getServiceReference(URI serviceId) { if (serviceId == null) { return null; } String searchedServiceId = serviceId.toString(); lockSupport.sharedLock(); try { for (Iterator it = services.iterator(); it.hasNext();) { Service service = (Service) it.next(); if (searchedServiceId.equals(service.getServiceId().toString())) { return service.getServiceReference(); } } } finally { lockSupport.releaseSharedLock(); } return null; } /* * (non-Javadoc) * @see * org.ws4d.java.service.Device#getServiceReference(org.ws4d.java.types. * EndpointReference) */ public ServiceReference getServiceReference(EndpointReference serviceEpr) { if (serviceEpr == null) { return null; } lockSupport.sharedLock(); try { for (Iterator it = services.iterator(); it.hasNext();) { Service service = (Service) it.next(); for (Iterator it2 = service.getEprInfos(); it2.hasNext();) { EprInfo eprInfo = (EprInfo) it2.next(); if (serviceEpr.equals(eprInfo.getEndpointReference())) { return service.getServiceReference(); } } } } finally { lockSupport.releaseSharedLock(); } return null; } /* * (non-Javadoc) * @see org.ws4d.java.service.Device#getXAddresses() */ public Iterator getXAddressInfos() { lockSupport.sharedLock(); try { XAddressInfoSet xAddrs = discoveryData.getXAddressInfoSet(); return xAddrs == null ? EmptyStructures.EMPTY_ITERATOR : new ReadOnlyIterator(xAddrs.iterator()); } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see org.ws4d.java.service.LocalDevice#getDiscoveryData() */ public DiscoveryData getDiscoveryData() { return discoveryData; } /** * Does the device use the default discovery domains for send multicast * discovery messages. If <code>true</code> => hello and bye messages will * be sent via the static output domains in {@link Discovery}. If * <code>false</code> => hello and bye will be sent to this device's * {@link #addOutputDiscoveryDomain(ProtocolDomain) explicitly configured * domains}. * * @return If <code>true</code> => hello and bye messages will be sent via * the static output domains in {@link Discovery}. If * <code>false</code> => hello and bye will be sent to this device's * configured output domains. */ public boolean isUsingDefaultDiscoveryDomains() { return outputDiscoveryDomains.size() == 0; } /** * Adds the specified protocol domain to this device. The domain will be * used for sending discovery messages (hellos and byes), in case * {@link #isUsingDefaultDiscoveryDomains()} returns <code>false</code>. * * @param domain the new protocol domain to add to this device * @see #isUsingDefaultDiscoveryDomains() * @see #setUsingDefaultDiscoveryDomains(boolean) */ public void addOutputDiscoveryDomain(ProtocolDomain domain) { if (domain == null) { return; } outputDiscoveryDomains.add(domain); } /** * Removes a previously {@link #addOutputDiscoveryDomain(ProtocolDomain) * added} output domain from this device. * * @param domain the output domain to remove * @see #isUsingDefaultDiscoveryDomains() * @see #setUsingDefaultDiscoveryDomains(boolean) */ public void removeOutputDiscoveryDomain(ProtocolDomain domain) { outputDiscoveryDomains.remove(domain); } /** * Gets device configuration properties. The device properties are built up * while reading a configuration file/stream by the {@link Properties} * class. * <p> * While constructing this device, the device properties were used to set * the device data. Changes of the device data afterwards will not be * transmitted to the properties. * </p> * * @return properties The properties of device created whilst reading the * configuration file/stream. */ public DeviceProperties getDeviceProperties() { return deviceProp; } /** * Gets the configuration id. The configuration id maps to the device * properties within the configuration file/stream. The device can be * constructed by {@link #DefaultDevice(int)} which specifies the * configuration id. The default id is -1, which doesn't map to any * configuration. * * @return The configuration id of the device. If it is -1, no configuration * id was specified. */ public int getConfigurationID() { return configurationId; } /** * Checks if this device matches the searched device port types and scopes. * To match the device both the port types and the scopes must be part of * the device. * * @param searchTypes Searched device port types to match the device. * @param searchScopes Searched scopes to match the device. * @return <code>true</code> - if both the given device port types and * scopes are part of the device. */ public boolean deviceMatches(QNameSet searchTypes, ProbeScopeSet searchScopes) { QNameSet deviceTypes = discoveryData.getTypes(); if (searchTypes == null || searchTypes.isEmpty() || (deviceTypes != null && deviceTypes.containsAll(searchTypes))) { // check scopes if (searchScopes != null && !searchScopes.isEmpty()) { ScopeSet scopes = discoveryData.getScopes(); if (scopes == null || scopes.isEmpty() || !scopes.containsAll(searchScopes)) { return false; } } return true; } return false; } /** * */ private void copyDiscoveryDataIfRunning() { if (running && !discoveryDataChanged) { discoveryData = new DiscoveryData(discoveryData); myDeviceRef.setDiscoveryData(discoveryData); discoveryDataChanged = true; } } /** * Creates a wsa:Hello message for the given device. * * @return the wsa:Hello message. */ private HelloMessage createHelloMessage() { // Copy discovery data! And filter types with priorities. DiscoveryData d = new DiscoveryData(discoveryData); QName[] qarray = QNameSet.sortPrioritiesAsArray(d.getTypes()); if (qarray != null) { int j = Math.min(qarray.length, MAX_QNAME_SERIALIZATION); QNameSet nTypes = new QNameSet(j); for (int i = 0; i < j; i++) { nTypes.add(qarray[i]); } d.setTypes(nTypes); } else { Log.warn("Sending wsd:Hello message without any types (e.g DPWS)! Maybe nobody will accept this message, set correct types!"); } HelloMessage hello = new HelloMessage(d, CommunicationManager.ID_NULL); hello.getHeader().setAppSequence(appSequencer.getNext()); if (isSecure()) { hello.setSecure(true); hello.setCertificate(this.getCertificate()); hello.setPrivateKey(this.getPrivateKey()); } return hello; } private DataStructure getOutputDiscoveryDomains() { if (isUsingDefaultDiscoveryDomains()) { return Discovery.getDefaultOutputDomains(); } else { return outputDiscoveryDomains; } } public void setDiscoveryProxy(boolean isDiscoveryProxy) { this.isDiscoveryProxy = isDiscoveryProxy; } public boolean isDiscoveryProxy() { return isDiscoveryProxy; } private final class DeviceMessageListener extends DefaultIncomingMessageListener { Device ownerDevice = null; private DeviceMessageListener(Device dd) { super(); ownerDevice = dd; } /* * (non-Javadoc) * @see * org.ws4d.java.communication.DefaultIncomingMessageListener#handle * (org.ws4d.java.message.metadata.GetMessage, * org.ws4d.java.communication.ProtocolData) */ public GetResponseMessage handle(GetMessage get, ProtocolData protocolData) throws SOAPException { lockSupport.sharedLock(); try { GetResponseMessage response = new GetResponseMessage(protocolData.getCommunicationManagerId()); response.setResponseTo(get); // set DPWSVersion from the Request to the Response response.setProtocolInfo(get.getProtocolInfo()); response.setThisModel(modelMetadata); response.setThisDevice(deviceMetadata); RelationshipMData relationship = new RelationshipMData(); // the host part HostMData host = new HostMData(); host.setEndpointReference(getEndpointReference()); host.setTypes(discoveryData.getTypes()); relationship.setHost(host); // the hosted parts Iterator it = getServices(); while (it.hasNext()) { HostedMData hosted = new HostedMData(); Service service = (Service) it.next(); /* * Filter endpoint references which are not transport * addresses. DPWS specification 2.5 R0042 */ Iterator eprsCurrent = service.getEprInfos(); EprInfoSet eprsFiltered = new EprInfoSet(); while (eprsCurrent.hasNext()) { EprInfo epr = (EprInfo) eprsCurrent.next(); if (epr.getXAddress() != null) { eprsFiltered.add(epr); } } hosted.setEprInfoSet(eprsFiltered); Iterator typesCurrent = service.getPortTypes(); QNameSet typesFilled = new QNameSet(); while (typesCurrent.hasNext()) { QName name = (QName) typesCurrent.next(); typesFilled.add(name); } hosted.setTypes(typesFilled); hosted.setServiceId(service.getServiceId()); relationship.addHosted(hosted); } CommunicationManager comMan = DPWSFramework.getCommunicationManager(protocolData.getCommunicationManagerId()); CommunicationUtil comUtil = comMan.getCommunicationUtil(); ConstantsHelper helper = comUtil.getHelper(get.getProtocolInfo().getVersion()); response.addRelationship(relationship, helper); if (hasCustomizeMData) { response.addCustomizeMetaData(CustomizeMData.getInstance()); } return response; } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see * org.ws4d.java.communication.DefaultIncomingMessageListener#handle * (org.ws4d.java.message.discovery.ProbeMessage, * org.ws4d.java.communication.ProtocolData) */ public ProbeMatchesMessage handle(ProbeMessage probe, ProtocolData protocolData) throws SOAPException { if (messageIdBuffer.containsOrEnqueue(probe.getMessageId())) { if (Log.isDebug()) { Log.debug("Discarding probe message! Already saw this one!", Log.DEBUG_LAYER_APPLICATION); } return null; } lockSupport.sharedLock(); try { if (deviceMatches(probe.getTypes(), probe.getScopes())) { ProbeMatchesMessage response = new ProbeMatchesMessage(protocolData.getCommunicationManagerId()); response.setResponseTo(probe); response.getHeader().setAppSequence(appSequencer.getNext()); // set DPWSVersion from the Request to the Response response.setProtocolInfo(probe.getProtocolInfo()); ProbeMatch probeMatch = new ProbeMatch(); probeMatch.setEndpointReference(getEndpointReference()); probeMatch.setMetadataVersion(getMetadataVersion()); QNameSet matchTypes; ScopeSet matchScopes; if (DPWSFramework.hasModule(DPWSFramework.SECURITY_MODULE) && ownerDevice.isSecure()) { response.setSecure(true); response.setPrivateKey(ownerDevice.getPrivateKey()); response.setCertificate(ownerDevice.getCertificate()); } QNameSet supportedDeviceTypes = DPWSFramework.getCommunicationManager(protocolData.getCommunicationManagerId()).getDeviceTypes(); if (probe.isDirected()) { /* * directed probe probe! Add all known types and scopes. */ matchTypes = discoveryData.getTypes(); if (matchTypes != null) { matchTypes.addAll(supportedDeviceTypes); } else { matchTypes = new QNameSet(supportedDeviceTypes); } matchScopes = discoveryData.getScopes(); } else { /* * for general UDP probes, we may reduce the number of * included types, scopes and xAddresses. At this point * its necessary to answer with types which are requests * by the search. So we need to check the matches and * priorities here. We do not answer with ALL types * anymore. */ QNameSet searchedTypes = probe.getTypes(); QName[] discoveryDataTypes = QNameSet.sortPrioritiesAsArray(discoveryData.getTypes()); // add all device types and searched types we matched matchTypes = new QNameSet(supportedDeviceTypes); if (searchedTypes != null) { for (int i = 0; i < discoveryDataTypes.length; i++) { if (searchedTypes.contains(discoveryDataTypes[i])) { matchTypes.add(discoveryDataTypes[i]); } } } // add other types by priority for (int i = 0; i < discoveryDataTypes.length && matchTypes.size() <= MAX_QNAME_SERIALIZATION; i++) { matchTypes.add(discoveryDataTypes[i]); } // add scopes that matched matchScopes = new ProbeScopeSet(); ProbeScopeSet searchedScopes = probe.getScopes(); ScopeSet discoveryDataScopeSet = discoveryData.getScopes(); if (discoveryDataScopeSet != null && !discoveryDataScopeSet.isEmpty()) { String[] discoveryDataScopes = discoveryData.getScopes().getScopesAsStringArray(); if (searchedScopes != null) { for (int k = 0; k < discoveryDataScopes.length; k++) { // TODO: scope matching rule (MatchBy) has // to be considered if (searchedScopes.contains(discoveryDataScopes[k])) { matchScopes.addScope(discoveryDataScopes[k]); } } } for (int k = 0; k < discoveryDataScopes.length && matchScopes.size() <= MAX_QNAME_SERIALIZATION; k++) { matchScopes.addScope(discoveryDataScopes[k]); } } } probeMatch.setTypes(matchTypes); // TODO: scopes with prio probeMatch.setScopes(matchScopes); probeMatch.setXAddresInfoSet(discoveryData.getXAddressInfoSet()); response.addProbeMatch(probeMatch); return response; } else if (probe.isDirected()) { // always return empty ProbeMatches message when directed ProbeMatchesMessage matches = new ProbeMatchesMessage(protocolData.getCommunicationManagerId()); matches.setResponseTo(probe); // set DPWSVersion from the Request to the Response matches.setProtocolInfo(probe.getProtocolInfo()); return matches; } return null; } finally { lockSupport.releaseSharedLock(); } } /* * (non-Javadoc) * @see * org.ws4d.java.communication.DefaultIncomingMessageListener#handle * (org.ws4d.java.message.discovery.ResolveMessage, * org.ws4d.java.communication.ProtocolData) */ public ResolveMatchesMessage handle(ResolveMessage resolve, ProtocolData protocolData) { lockSupport.sharedLock(); try { if (resolve.getEndpointReference() != null && resolve.getEndpointReference().equals(getEndpointReference())) { ResolveMatchesMessage response = new ResolveMatchesMessage(protocolData.getCommunicationManagerId()); response.setResponseTo(resolve); response.getHeader().setAppSequence(appSequencer.getNext()); // set DPWSVersion from the Request to the Response response.setProtocolInfo(resolve.getProtocolInfo()); ResolveMatch match = new ResolveMatch(); match.setEndpointReference(getEndpointReference()); match.setMetadataVersion(getMetadataVersion()); match.setTypes(discoveryData.getTypes()); match.setScopes(discoveryData.getScopes()); match.setXAddresInfoSet(discoveryData.getXAddressInfoSet()); response.setResolveMatch(match); return response; } return null; } finally { lockSupport.releaseSharedLock(); } } } /** * Manages the application sequence of device. */ public class AppSequenceManager { /** Seconds till era when device started */ private long instanceId = 0; // private URI sequenceId; // optional /** last send message number */ private long messageNumber = 0; /** * Resets application sequence */ public void reset() { instanceId = System.currentTimeMillis() / 1000; messageNumber = 0; } /** * Increments message number by one and returns AppSequence with this; * * @return */ public AppSequence getNext() { messageNumber++; return new AppSequence(instanceId, messageNumber); } } public void setDefaultNamespace(String ns) { namespace = ns; } public String getDefaultNamespace() { return namespace; } public boolean isValid() { return true; } public void invalidate() { // void } public HTTPGroup getGroup() { return userGroup; } public void addUser(HTTPUser user) { if (userGroup == null) { userGroup = new HTTPGroup(); } userGroup.addUser(user); } public void addGroup(HTTPGroup group) { if (userGroup == null) { userGroup = group; } else { // TODO mehrere Gruppen hinzuf�gen k�nnen } } /** * @see org.ws4d.java.service.Device#readCustomizeMData() */ public String readCustomizeMData() { return myDeviceRef.getCustomMData(); } /** * @see org.ws4d.java.service.LocalDevice#writeCustomizeMData(HashMap) */ public void writeCustomizeMData(HashMap metaData) { hasCustomizeMData = true; this.mdata = metaData; Iterator keys = mdata.keySet().iterator(); while (keys.hasNext()) { QName element = (QName) keys.next(); Object value = mdata.get(element); CustomizeMData.getInstance().addNewElement(element, value); } } /** * @see org.ws4d.java.service.Device#hasCustomizeMData() */ public boolean hasCustomizeMData() { if (readCustomizeMData() != null) return true; else return false; } }