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 }