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 */
017package org.apache.log4j.net;
018
019import java.lang.reflect.Constructor;
020import java.lang.reflect.InvocationTargetException;
021import java.lang.reflect.Method;
022import java.util.HashMap;
023import java.util.Hashtable;
024import java.util.Map;
025
026import org.apache.log4j.helpers.LogLog;
027
028public class ZeroConfSupport {
029    private static Object jmDNS = initializeJMDNS();
030
031    Object serviceInfo;
032    private static Class jmDNSClass;
033    private static Class serviceInfoClass;
034
035    public ZeroConfSupport(String zone, int port, String name, Map properties) {
036        //if version 3 is available, use it to constuct a serviceInfo instance, otherwise support the version1 API
037        boolean isVersion3 = false;
038        try {
039            //create method is in version 3, not version 1
040            jmDNSClass.getMethod("create", null);
041            isVersion3 = true;
042        } catch (NoSuchMethodException e) {
043            //no-op
044        }
045
046        if (isVersion3) {
047            LogLog.debug("using JmDNS version 3 to construct serviceInfo instance");
048            serviceInfo = buildServiceInfoVersion3(zone, port, name, properties);
049        } else {
050            LogLog.debug("using JmDNS version 1.0 to construct serviceInfo instance");
051            serviceInfo = buildServiceInfoVersion1(zone, port, name, properties);
052        }
053    }
054
055    public ZeroConfSupport(String zone, int port, String name) {
056        this(zone, port, name, new HashMap());
057    }
058
059    private static Object createJmDNSVersion1()
060    {
061        try {
062            return jmDNSClass.newInstance();
063        } catch (InstantiationException e) {
064            LogLog.warn("Unable to instantiate JMDNS", e);
065        } catch (IllegalAccessException e) {
066            LogLog.warn("Unable to instantiate JMDNS", e);
067        }
068        return null;
069    }
070
071    private static Object createJmDNSVersion3()
072    {
073        try {
074            Method jmDNSCreateMethod = jmDNSClass.getMethod("create", null);
075            return jmDNSCreateMethod.invoke(null, null);
076        } catch (IllegalAccessException e) {
077            LogLog.warn("Unable to instantiate jmdns class", e);
078        } catch (NoSuchMethodException e) {
079            LogLog.warn("Unable to access constructor", e);
080        } catch (InvocationTargetException e) {
081                LogLog.warn("Unable to call constructor", e);
082        }
083        return null;
084    }
085
086    private Object buildServiceInfoVersion1(String zone, int port, String name, Map properties) {
087        //version 1 uses a hashtable
088        Hashtable hashtableProperties = new Hashtable(properties);
089        try {
090            Class[] args = new Class[6];
091            args[0] = String.class;
092            args[1] = String.class;
093            args[2] = int.class;
094            args[3] = int.class; //weight (0)
095            args[4] = int.class; //priority (0)
096            args[5] = Hashtable.class;
097            Constructor constructor  = serviceInfoClass.getConstructor(args);
098            Object[] values = new Object[6];
099            values[0] = zone;
100            values[1] = name;
101            values[2] = new Integer(port);
102            values[3] = new Integer(0);
103            values[4] = new Integer(0);
104            values[5] = hashtableProperties;
105            Object result = constructor.newInstance(values);
106            LogLog.debug("created serviceinfo: " + result);
107            return result;
108        } catch (IllegalAccessException e) {
109            LogLog.warn("Unable to construct ServiceInfo instance", e);
110        } catch (NoSuchMethodException e) {
111            LogLog.warn("Unable to get ServiceInfo constructor", e);
112        } catch (InstantiationException e) {
113            LogLog.warn("Unable to construct ServiceInfo instance", e);
114        } catch (InvocationTargetException e) {
115            LogLog.warn("Unable to construct ServiceInfo instance", e);
116        }
117        return null;
118    }
119
120    private Object buildServiceInfoVersion3(String zone, int port, String name, Map properties) {
121        try {
122            Class[] args = new Class[6];
123            args[0] = String.class; //zone/type
124            args[1] = String.class; //display name
125            args[2] = int.class; //port
126            args[3] = int.class; //weight (0)
127            args[4] = int.class; //priority (0)
128            args[5] = Map.class;
129            Method serviceInfoCreateMethod = serviceInfoClass.getMethod("create", args);
130            Object[] values = new Object[6];
131            values[0] = zone;
132            values[1] = name;
133            values[2] = new Integer(port);
134            values[3] = new Integer(0);
135            values[4] = new Integer(0);
136            values[5] = properties;
137            Object result = serviceInfoCreateMethod.invoke(null, values);
138            LogLog.debug("created serviceinfo: " + result);
139            return result;
140        } catch (IllegalAccessException e) {
141            LogLog.warn("Unable to invoke create method", e);
142        } catch (NoSuchMethodException e) {
143            LogLog.warn("Unable to find create method", e);
144        } catch (InvocationTargetException e) {
145                LogLog.warn("Unable to invoke create method", e);
146        }
147        return null;
148    }
149
150    public void advertise() {
151        try {
152            Method method = jmDNSClass.getMethod("registerService", new Class[]{serviceInfoClass});
153            method.invoke(jmDNS, new Object[]{serviceInfo});
154            LogLog.debug("registered serviceInfo: " + serviceInfo);
155        } catch(IllegalAccessException e) {
156            LogLog.warn("Unable to invoke registerService method", e);
157        } catch(NoSuchMethodException e) {
158            LogLog.warn("No registerService method", e);
159        } catch(InvocationTargetException e) {
160            LogLog.warn("Unable to invoke registerService method", e);
161        }
162    }
163
164    public void unadvertise() {
165        try {
166            Method method = jmDNSClass.getMethod("unregisterService", new Class[]{serviceInfoClass});
167            method.invoke(jmDNS, new Object[]{serviceInfo});
168            LogLog.debug("unregistered serviceInfo: " + serviceInfo);
169        } catch(IllegalAccessException e) {
170            LogLog.warn("Unable to invoke unregisterService method", e);
171        } catch(NoSuchMethodException e) {
172            LogLog.warn("No unregisterService method", e);
173        } catch(InvocationTargetException e) {
174            LogLog.warn("Unable to invoke unregisterService method", e);
175       }
176    }
177
178    private static Object initializeJMDNS() {
179        try {
180            jmDNSClass = Class.forName("javax.jmdns.JmDNS");
181            serviceInfoClass = Class.forName("javax.jmdns.ServiceInfo");
182        } catch (ClassNotFoundException e) {
183            LogLog.warn("JmDNS or serviceInfo class not found", e);
184        }
185
186        //if version 3 is available, use it to constuct a serviceInfo instance, otherwise support the version1 API
187        boolean isVersion3 = false;
188        try {
189            //create method is in version 3, not version 1
190            jmDNSClass.getMethod("create", null);
191            isVersion3 = true;
192        } catch (NoSuchMethodException e) {
193            //no-op
194        }
195
196        if (isVersion3) {
197            return createJmDNSVersion3();
198        } else {
199            return createJmDNSVersion1();
200        }
201    }
202
203    public static Object getJMDNSInstance() {
204        return jmDNS;
205    }
206}