View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.logging.log4j.core.net;
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  import java.util.Hashtable;
23  import java.util.Map;
24  import org.apache.logging.log4j.Logger;
25  import org.apache.logging.log4j.core.config.plugins.Plugin;
26  import org.apache.logging.log4j.status.StatusLogger;
27  
28  /**
29   * Advertise an entity via ZeroConf/MulticastDNS and the JmDNS library.
30   *
31   */
32  @Plugin(name = "multicastdns", type = "Core", elementType = "advertiser", printObject = false)
33  public class MulticastDNSAdvertiser implements Advertiser {
34      protected static final Logger LOGGER = StatusLogger.getLogger();
35      private static Object jmDNS = initializeJMDNS();
36  
37      private static Class<?> jmDNSClass;
38      private static Class<?> serviceInfoClass;
39  
40      public MulticastDNSAdvertiser()
41      {
42          //no arg constructor for reflection 
43      }
44  
45      /**
46       * Advertise the provided entity.
47       * 
48       * Properties map provided in advertise method must include a "name" entry 
49       * but may also provide "protocol" (tcp/udp) as well as a "port" entry
50       * 
51       * @param properties the properties representing the entity to advertise
52       * @return the object which can be used to unadvertise, or null if advertisement was unsuccessful
53       */
54      public Object advertise(Map<String, String> properties) {
55          //default to tcp if "protocol" was not set
56          String protocol = properties.get("protocol");
57          String zone = "._log4j._"+(protocol != null ? protocol : "tcp") + ".local.";
58          //default to 4555 if "port" was not set
59          String portString = properties.get("port");
60          int port = (portString != null ? Integer.parseInt(portString) : 4555);
61  
62          String name = properties.get("name");
63  
64          //if version 3 is available, use it to construct a serviceInfo instance, otherwise support the version1 API
65          if (jmDNS != null)
66          {
67              boolean isVersion3 = false;
68              try {
69                  //create method is in version 3, not version 1
70                  jmDNSClass.getMethod("create", (Class[])null);
71                  isVersion3 = true;
72              } catch (NoSuchMethodException e) {
73                  //no-op
74              }
75              System.out.println("building: " + zone);
76              Object serviceInfo;
77              if (isVersion3) {
78                  serviceInfo = buildServiceInfoVersion3(zone, port, name, properties);
79              } else {
80                  serviceInfo = buildServiceInfoVersion1(zone, port, name, properties);
81              }
82  
83              try {
84                  Method method = jmDNSClass.getMethod("registerService", new Class[]{serviceInfoClass});
85                  method.invoke(jmDNS, serviceInfo);
86              } catch(IllegalAccessException e) {
87                  LOGGER.warn("Unable to invoke registerService method", e);
88              } catch(NoSuchMethodException e) {
89                  LOGGER.warn("No registerService method", e);
90              } catch(InvocationTargetException e) {
91                  LOGGER.warn("Unable to invoke registerService method", e);
92              }
93              return serviceInfo;
94          }
95          else
96          {
97              LOGGER.warn("JMDNS not available - will not advertise ZeroConf support");
98              return null;
99          }
100     }
101 
102     /**
103      * Unadvertise the previously advertised entity
104      * @param serviceInfo
105      */
106     public void unadvertise(Object serviceInfo) {
107         if (jmDNS != null) {
108             try {
109                 Method method = jmDNSClass.getMethod("unregisterService", new Class[]{serviceInfoClass});
110                 method.invoke(jmDNS, serviceInfo);
111             } catch(IllegalAccessException e) {
112                 LOGGER.warn("Unable to invoke unregisterService method", e);
113             } catch(NoSuchMethodException e) {
114                 LOGGER.warn("No unregisterService method", e);
115             } catch(InvocationTargetException e) {
116                 LOGGER.warn("Unable to invoke unregisterService method", e);
117             }
118         }
119     }
120 
121     private static Object createJmDNSVersion1()
122     {
123         try {
124             return jmDNSClass.newInstance();
125         } catch (InstantiationException e) {
126             LOGGER.warn("Unable to instantiate JMDNS", e);
127         } catch (IllegalAccessException e) {
128             LOGGER.warn("Unable to instantiate JMDNS", e);
129         }
130         return null;
131     }
132 
133     private static Object createJmDNSVersion3()
134     {
135         try {
136             Method jmDNSCreateMethod = jmDNSClass.getMethod("create", (Class[])null);
137             return jmDNSCreateMethod.invoke(null, (Object[])null);
138         } catch (IllegalAccessException e) {
139             LOGGER.warn("Unable to instantiate jmdns class", e);
140         } catch (NoSuchMethodException e) {
141             LOGGER.warn("Unable to access constructor", e);
142         } catch (InvocationTargetException e) {
143             LOGGER.warn("Unable to call constructor", e);
144         }
145         return null;
146     }
147 
148     private Object buildServiceInfoVersion1(String zone, int port, String name, Map<String, String> properties) {
149         //version 1 uses a hashtable
150         Hashtable<String, String> hashtableProperties = new Hashtable<String, String>(properties);
151         try {
152             Class[] args = new Class[6];
153             args[0] = String.class;
154             args[1] = String.class;
155             args[2] = int.class;
156             args[3] = int.class; //weight (0)
157             args[4] = int.class; //priority (0)
158             args[5] = Hashtable.class;
159             Constructor<?> constructor  = serviceInfoClass.getConstructor(args);
160             Object[] values = new Object[6];
161             values[0] = zone;
162             values[1] = name;
163             values[2] = port;
164             values[3] = 0;
165             values[4] = 0;
166             values[5] = hashtableProperties;
167             return constructor.newInstance(values);
168         } catch (IllegalAccessException e) {
169             LOGGER.warn("Unable to construct ServiceInfo instance", e);
170         } catch (NoSuchMethodException e) {
171             LOGGER.warn("Unable to get ServiceInfo constructor", e);
172         } catch (InstantiationException e) {
173             LOGGER.warn("Unable to construct ServiceInfo instance", e);
174         } catch (InvocationTargetException e) {
175             LOGGER.warn("Unable to construct ServiceInfo instance", e);
176         }
177         return null;
178     }
179 
180     private Object buildServiceInfoVersion3(String zone, int port, String name, Map<String, String> properties) {
181         try {
182             Class[] args = new Class[6];
183             args[0] = String.class; //zone/type
184             args[1] = String.class; //display name
185             args[2] = int.class; //port
186             args[3] = int.class; //weight (0)
187             args[4] = int.class; //priority (0)
188             args[5] = Map.class;
189             Method serviceInfoCreateMethod = serviceInfoClass.getMethod("create", args);
190             Object[] values = new Object[6];
191             values[0] = zone;
192             values[1] = name;
193             values[2] = port;
194             values[3] = 0;
195             values[4] = 0;
196             values[5] = properties;
197             return serviceInfoCreateMethod.invoke(null, values);
198         } catch (IllegalAccessException e) {
199             LOGGER.warn("Unable to invoke create method", e);
200         } catch (NoSuchMethodException e) {
201             LOGGER.warn("Unable to find create method", e);
202         } catch (InvocationTargetException e) {
203             LOGGER.warn("Unable to invoke create method", e);
204         }
205         return null;
206     }
207 
208     private static Object initializeJMDNS() {
209         try {
210             jmDNSClass = Class.forName("javax.jmdns.JmDNS");
211             serviceInfoClass = Class.forName("javax.jmdns.ServiceInfo");
212             //if version 3 is available, use it to constuct a serviceInfo instance, otherwise support the version1 API
213             boolean isVersion3 = false;
214             try {
215                 //create method is in version 3, not version 1
216                 jmDNSClass.getMethod("create", (Class[])null);
217                 isVersion3 = true;
218             } catch (NoSuchMethodException e) {
219                 //no-op
220             }
221 
222             if (isVersion3) {
223                 return createJmDNSVersion3();
224             } else {
225                 return createJmDNSVersion1();
226             }
227         } catch (ClassNotFoundException e) {
228             LOGGER.warn("JmDNS or serviceInfo class not found", e);
229         } catch (ExceptionInInitializerError e2) {
230             LOGGER.warn("JmDNS or serviceInfo class not found", e2);
231         }
232         return null;
233     }
234 }