001 /** 002 * 003 * Copyright 2004 Protique Ltd 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 **/ 018 package org.activemq.transport.zeroconf; 019 020 import org.apache.commons.logging.Log; 021 import org.apache.commons.logging.LogFactory; 022 import org.activemq.ConfigurationException; 023 import org.activemq.NotStartedException; 024 import org.activemq.transport.DiscoveryAgent; 025 import org.activemq.transport.DiscoveryAgentSupport; 026 import org.activemq.transport.DiscoveryEvent; 027 import org.activemq.util.JMSExceptionHelper; 028 import org.activemq.util.MapHelper; 029 030 import javax.jmdns.JmDNS; 031 import javax.jmdns.ServiceEvent; 032 import javax.jmdns.ServiceInfo; 033 import javax.jmdns.ServiceListener; 034 import javax.jms.JMSException; 035 import java.io.IOException; 036 import java.net.InetAddress; 037 import java.net.URI; 038 import java.net.UnknownHostException; 039 import java.util.Enumeration; 040 import java.util.HashMap; 041 import java.util.Hashtable; 042 import java.util.Map; 043 044 /** 045 * A {@link DiscoveryAgent} using <a href="http://www.zeroconf.org/">Zeroconf</a> 046 * via the <a href="http://jmdns.sf.net/">jmDNS</a> library 047 * 048 * @version $Revision$ 049 */ 050 public class ZeroconfDiscoveryAgent extends DiscoveryAgentSupport implements ServiceListener { 051 private static final Log log = LogFactory.getLog(ZeroconfDiscoveryAgent.class); 052 053 private JmDNS jmdns; 054 private InetAddress localAddress; 055 private String localhost; 056 private String type; 057 private int weight = 0; 058 private int priority = 0; 059 060 // DiscoveryAgent interface 061 //------------------------------------------------------------------------- 062 public void start() throws JMSException { 063 if (type == null) { 064 throw new ConfigurationException("You must specify a type of service to discover"); 065 } 066 if (!type.endsWith(".")) { 067 log.warn("The type '" + type + "' should end with '.' to be a valid Zeroconf type"); 068 type += "."; 069 } 070 try { 071 if (jmdns == null) { 072 jmdns = createJmDNS(); 073 } 074 if (!listeners.isEmpty()) { 075 log.info("Discovering service of type: " + type); 076 jmdns.addServiceListener(type, this); 077 } 078 } 079 catch (IOException e) { 080 JMSExceptionHelper.newJMSException("Failed to start JmDNS service: " + e, e); 081 } 082 } 083 084 public void stop() throws JMSException { 085 jmdns.unregisterAllServices(); 086 jmdns.close(); 087 } 088 089 public void registerService(String name, Map details) throws JMSException { 090 if (jmdns == null) { 091 throw new NotStartedException(); 092 } 093 try { 094 jmdns.registerService(createServiceInfo(name, details)); 095 } 096 catch (IOException e) { 097 JMSExceptionHelper.newJMSException("Could not register service: " + name + ". Reason: " + e, e); 098 } 099 } 100 101 102 // ServiceListener interface 103 //------------------------------------------------------------------------- 104 public void addService(JmDNS jmDNS, String type, String name) { 105 if (log.isDebugEnabled()) { 106 log.debug("addService with type: " + type + " name: " + name); 107 } 108 jmDNS.requestServiceInfo(type, name); 109 } 110 111 public void removeService(JmDNS jmDNS, String type, String name) { 112 if (log.isDebugEnabled()) { 113 log.debug("removeService with type: " + type + " name: " + name); 114 } 115 DiscoveryEvent event = new DiscoveryEvent(this, name); 116 fireRemoveService(event); 117 } 118 119 public void resolveService(JmDNS jmDNS, String type, String name, ServiceInfo serviceInfo) { 120 if (log.isDebugEnabled()) { 121 log.debug("removeService with type: " + type + " name: " + name + " info: " + serviceInfo); 122 } 123 124 Map map = new HashMap(); 125 if (serviceInfo != null) { 126 Enumeration iter = serviceInfo.getPropertyNames(); 127 while (iter.hasMoreElements()) { 128 String key = (String) iter.nextElement(); 129 String value = serviceInfo.getPropertyString(key); 130 map.put(key, value); 131 } 132 } 133 String urlStr = (String)map.get("connectURL"); 134 if (urlStr != null) { 135 try { 136 boolean reliable = false; 137 if (urlStr.startsWith("reliable:")){ 138 reliable = true; 139 urlStr = urlStr.substring("reliable:".length()); 140 } 141 URI temp = new URI(urlStr); 142 143 temp = new URI(temp.getScheme(), temp.getUserInfo(), serviceInfo.getHostAddress(), temp.getPort(), 144 temp.getPath(), temp.getQuery(), temp.getFragment()); 145 String newUrl = temp.toString(); 146 if (reliable) { 147 newUrl = "reliable:"+newUrl; 148 } 149 map.put("connectURL", newUrl); 150 } 151 catch (Exception e) { 152 log.error("Failed to resolve URL " + urlStr + " to an IP address: ", e); 153 } 154 } 155 DiscoveryEvent event = new DiscoveryEvent(this, name, map); 156 fireAddService(event); 157 } 158 159 public String getType() { 160 return type; 161 } 162 163 public void setType(String type) { 164 this.type = type; 165 } 166 167 public int getPriority() { 168 return priority; 169 } 170 171 public void setPriority(int priority) { 172 this.priority = priority; 173 } 174 175 public int getWeight() { 176 return weight; 177 } 178 179 public void setWeight(int weight) { 180 this.weight = weight; 181 } 182 183 public JmDNS getJmdns() { 184 return jmdns; 185 } 186 187 public void setJmdns(JmDNS jmdns) { 188 this.jmdns = jmdns; 189 } 190 191 192 public InetAddress getLocalAddress() throws UnknownHostException { 193 if (localAddress == null) { 194 localAddress = createLocalAddress(); 195 } 196 return localAddress; 197 } 198 199 public void setLocalAddress(InetAddress localAddress) { 200 this.localAddress = localAddress; 201 } 202 203 public String getLocalhost() { 204 return localhost; 205 } 206 207 public void setLocalhost(String localhost) { 208 this.localhost = localhost; 209 } 210 211 // Implementation methods 212 //------------------------------------------------------------------------- 213 protected ServiceInfo createServiceInfo(String name, Map map) { 214 name += "." + type; 215 int port = MapHelper.getInt(map, "port", 0); 216 217 if (log.isDebugEnabled()) { 218 log.debug("Registering service type: " + type + " name: " + name + " details: " + map); 219 } 220 return new ServiceInfo(type, name, port, weight, priority, new Hashtable(map)); 221 } 222 223 protected JmDNS createJmDNS() throws IOException { 224 return new JmDNS(getLocalAddress()); 225 } 226 227 protected InetAddress createLocalAddress() throws UnknownHostException { 228 if (localhost != null) { 229 return InetAddress.getByName(localhost); 230 } 231 return InetAddress.getLocalHost(); 232 } 233 234 public void serviceAdded(ServiceEvent event) { 235 } 236 237 public void serviceRemoved(ServiceEvent event) { 238 } 239 240 public void serviceResolved(ServiceEvent event) { 241 } 242 }