📄 serviceprovider.java
字号:
package net.sf.dz.daemon.tcp.client;import java.io.IOException;import java.util.HashMap;import java.util.HashSet;import java.util.Iterator;import java.util.Map;import java.util.Set;import java.util.StringTokenizer;import java.util.TreeMap;import java.util.TreeSet;import org.freehold.jukebox.logger.LogAware;import org.freehold.jukebox.logger.LogChannel;import org.freehold.jukebox.service.RunnableService;import net.sf.dz.pnp.MulticastEvent;/** * Service provider. * * <p> * * Provides the {@link ConnectorFactory connector factory} with a way to get * a {@link DeviceConnector device connector} for a particular kind of * service. Knows how to locate multiple service provider instances and * create {@link ServiceConnector service connectors} for them. * * @author Copyright © <a href="mailto:vt@freehold.crocodile.org">Vadim Tkachenko</a> 2001-2004 * @version $Id: ServiceProvider.java,v 1.3 2004/06/29 03:11:24 vtt Exp $ */public class ServiceProvider extends LogAware { public static final LogChannel CH_SP = new LogChannel("ServiceProvider"); public static final LogChannel CH_DCP = new LogChannel("DeviceConnectorProxy"); /** * Contains a map of known service connectors by source (to be exact, * source address). */ private Map serviceConnectorBySource = new HashMap(); /** * Contains a device connector proxy map by address. */ private Map deviceConnectorByAddress = new TreeMap(); /** * Contains a map of known service connectors by device address. */ private Map serviceConnectorByAddress = new TreeMap(); public synchronized DeviceConnector getConnector(String deviceAddress) { // Do we have a connector already? DeviceConnector dc = (DeviceConnector)deviceConnectorByAddress.get(deviceAddress); if ( dc == null ) { // Create it, then dc = new DeviceConnectorProxy(deviceAddress); // And make sure everyone else knows about it deviceConnectorByAddress.put(deviceAddress, dc); } return dc; } /** * Receive an endpoint advertisement. */ public synchronized void multicastEventReceived(MulticastEvent e) { // VT: NOTE: It is assumed that we will only get the notifications // for the services we know how to handle // Figure out the endpoint Object source = e.getSource(); // If we don't have it, create it if ( !knowAbout(source) ) { createServiceConnector(e); } else { // If we have it, update the information about the devices that // it provides update(e); } } private boolean knowAbout(Object source) { return serviceConnectorBySource.get(source) != null; } private synchronized void createServiceConnector(MulticastEvent e) { complain(LOG_DEBUG, CH_SP, "Creating service connector for " + e.getSource()); // VT: FIXME: Currently, the only kind of connector we can handle is // the TCP one. This sucks (think same-JVM endpoint, in particular, // USB) and has to be fixed. ServiceConnector sc = new TcpServiceConnector(this, e.getSourceAddress(), e.getPort(), e.isSecure(), "password"); if ( sc instanceof LogAware ) { ((LogAware)sc).setLogger(getLogger()); } if ( sc instanceof RunnableService ) { // VT: FIXME: Make sure we know if startup fails ((RunnableService)sc).start(); } // VT: FIXME: DOS attack and source flapping possible // VT: NOTE: It is possible to at least partially prevent this from // happening using PKI to sign the source advertisements and // verifying the signatures on the receiving end. serviceConnectorBySource.put(e.getSource(), sc); update(e); } /** * Analyze the event and figure out whether the service has changed and * whether the devices that used to be provided by this service are * still provided. * * @param e Event to analyze. * * @see #serviceConnectorByAddress */ private synchronized void update(MulticastEvent e) { ServiceConnector sc = (ServiceConnector)serviceConnectorBySource.get(e.getSource()); if ( sc == null ) { throw new IllegalStateException("Can't update - don't have a connector for " + e); } // VT: FIXME: This can be made more generic Set newAddressSet = new TreeSet(); for ( StringTokenizer st = new StringTokenizer(e.getMessage(), " "); st.hasMoreTokens(); ) { newAddressSet.add(st.nextToken()); } Set oldAddressSet = getAddressSetByServiceConnector(sc); //complain(LOG_DEBUG, CH_SP, "Devices: " + newAddressSet); for ( Iterator i = newAddressSet.iterator(); i.hasNext(); ) { String address = (String)i.next(); ServiceConnector sc2 = (ServiceConnector)serviceConnectorByAddress.get(address); if ( sc2 != null ) { if ( sc != sc2 ) { // In this case, the consumer isn't even supposed to // notice anything complain(LOG_INFO, CH_SP, address + " will be served by " + sc + " (used to be " + sc2 + ")"); serviceConnectorByAddress.put(address, sc); } } else { // In this case, we don't send the arrival notification // because it is assumed that the device will make itself // known - and before that, it's as good as dead // VT: FIXME: Verify this assumption complain(LOG_INFO, CH_SP, address + " will be served by " + sc); serviceConnectorByAddress.put(address, sc); } } // Now let's figure whether the devices that used to be there are // still there for ( Iterator i = oldAddressSet.iterator(); i.hasNext(); ) { String address = i.next().toString(); if ( !newAddressSet.contains(address) ) { // We may safely assume it's gone complain(LOG_NOTICE, CH_SP, "No more " + address); // VT: FIXME: This *may* cause hanging service // connectors, but it may be a lesser evil. In any case, // will have to check whether the service connector have // any devices left at all and shut them down if // necessary. serviceConnectorByAddress.remove(address); DeviceConnectorProxy dcp = (DeviceConnectorProxy)deviceConnectorByAddress.get(address); if ( dcp != null ) { // VT: NOTE: we do *not* remove the device connector // from the mappings. As a matter of fact, we never // destroy this mapping because the connector just // created once and then reused. dcp.broadcast("Not Available"); } } } } private Set getAddressSetByServiceConnector(ServiceConnector sc) { // VT: NOTE: This can be done just once and the mapping can be held // permanently, but should we bother? The set we're about to produce // is the opposite of serviceConnectorByAddress. Set result = new TreeSet(); for ( Iterator i = serviceConnectorByAddress.keySet().iterator(); i.hasNext(); ) { String address = i.next().toString(); if ( serviceConnectorByAddress.get(address).equals(sc) ) { result.add(address); } } return result; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -