001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. 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 package org.apache.logging.log4j.core.net; 018 019 import java.lang.reflect.Constructor; 020 import java.lang.reflect.InvocationTargetException; 021 import java.lang.reflect.Method; 022 import java.util.Hashtable; 023 import java.util.Map; 024 import org.apache.logging.log4j.Logger; 025 import org.apache.logging.log4j.core.config.plugins.Plugin; 026 import org.apache.logging.log4j.status.StatusLogger; 027 028 /** 029 * Advertise an entity via ZeroConf/MulticastDNS and the JmDNS library. 030 * 031 */ 032 @Plugin(name = "multicastdns", type = "Core", elementType = "advertiser", printObject = false) 033 public class MulticastDNSAdvertiser implements Advertiser { 034 protected static final Logger LOGGER = StatusLogger.getLogger(); 035 private static Object jmDNS = initializeJMDNS(); 036 037 private static Class<?> jmDNSClass; 038 private static Class<?> serviceInfoClass; 039 040 public MulticastDNSAdvertiser() 041 { 042 //no arg constructor for reflection 043 } 044 045 /** 046 * Advertise the provided entity. 047 * 048 * Properties map provided in advertise method must include a "name" entry 049 * but may also provide "protocol" (tcp/udp) as well as a "port" entry 050 * 051 * @param properties the properties representing the entity to advertise 052 * @return the object which can be used to unadvertise, or null if advertisement was unsuccessful 053 */ 054 public Object advertise(Map<String, String> properties) { 055 //default to tcp if "protocol" was not set 056 String protocol = properties.get("protocol"); 057 String zone = "._log4j._"+(protocol != null ? protocol : "tcp") + ".local."; 058 //default to 4555 if "port" was not set 059 String portString = properties.get("port"); 060 int port = (portString != null ? Integer.parseInt(portString) : 4555); 061 062 String name = properties.get("name"); 063 064 //if version 3 is available, use it to construct a serviceInfo instance, otherwise support the version1 API 065 if (jmDNS != null) 066 { 067 boolean isVersion3 = false; 068 try { 069 //create method is in version 3, not version 1 070 jmDNSClass.getMethod("create", (Class[])null); 071 isVersion3 = true; 072 } catch (NoSuchMethodException e) { 073 //no-op 074 } 075 System.out.println("building: " + zone); 076 Object serviceInfo; 077 if (isVersion3) { 078 serviceInfo = buildServiceInfoVersion3(zone, port, name, properties); 079 } else { 080 serviceInfo = buildServiceInfoVersion1(zone, port, name, properties); 081 } 082 083 try { 084 Method method = jmDNSClass.getMethod("registerService", new Class[]{serviceInfoClass}); 085 method.invoke(jmDNS, serviceInfo); 086 } catch(IllegalAccessException e) { 087 LOGGER.warn("Unable to invoke registerService method", e); 088 } catch(NoSuchMethodException e) { 089 LOGGER.warn("No registerService method", e); 090 } catch(InvocationTargetException e) { 091 LOGGER.warn("Unable to invoke registerService method", e); 092 } 093 return serviceInfo; 094 } 095 else 096 { 097 LOGGER.warn("JMDNS not available - will not advertise ZeroConf support"); 098 return null; 099 } 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 }