1 /*
2  * Copyright 2008-2019 by Emeric Vernat
3  *
4  *     This file is part of Java Melody.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 package net.bull.javamelody.internal.model;
19
20 import java.io.Serializable;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.List;
24
25 import javax.naming.Binding;
26 import javax.naming.Context;
27 import javax.naming.InitialContext;
28 import javax.naming.NamingEnumeration;
29 import javax.naming.NamingException;
30 import javax.naming.Reference;
31
32 import net.bull.javamelody.internal.common.Parameters;
33
34 /**
35  * Informations sur un binding JNDI.
36  * @author Emeric Vernat
37  */

38 public class JndiBinding implements Serializable {
39     private static final long serialVersionUID = 1L;
40
41     private static final String JNDI_PREFIX = "java:";
42
43     private final String name;
44     private final String className;
45     private final String contextPath;
46     private final String value;
47
48     JndiBinding(String name, String className, String contextPath, String value) {
49         super();
50         this.name = name;
51         this.className = className;
52         this.contextPath = contextPath;
53         this.value = value;
54     }
55
56     public String getValue() {
57         return value;
58     }
59
60     public String getName() {
61         return name;
62     }
63
64     public String getClassName() {
65         return className;
66     }
67
68     public String getContextPath() {
69         return contextPath;
70     }
71
72     public static String normalizePath(String path) {
73         if (path != null) {
74             return path;
75         } else if (Parameters.getServletContext().getServerInfo().contains("GlassFish")) {
76             // dans glassfish 3.0.1, context.listBindings("java:") ne retourne aucun binding à part
77             // lui-même, donc par défaut dans glassfish on prend le path "comp" et non ""
78             return "comp";
79         }
80         return "";
81     }
82
83     public static List<JndiBinding> listBindings(String path) throws NamingException {
84         return listBindings(new InitialContext(), path);
85     }
86
87     public static List<JndiBinding> listBindings(Context context, String path)
88             throws NamingException {
89         final String normalizedPath = normalizePath(path);
90         final String jndiName;
91         if (Parameters.getServletContext().getServerInfo().contains("WebLogic")) {
92             // path + '/' nécessaire pour WebLogic 10.3.1.0 mais pas supporté dans JBoss
93             jndiName = JNDI_PREFIX + normalizedPath + '/';
94         } else {
95             jndiName = JNDI_PREFIX + normalizedPath;
96         }
97         final List<JndiBinding> result = new ArrayList<>();
98         final NamingEnumeration<Binding> enumeration = context.listBindings(jndiName);
99         try {
100             while (enumeration.hasMore()) {
101                 try {
102                     final Binding binding = enumeration.next();
103                     final JndiBinding jndiBinding = createJndiBinding(normalizedPath, binding);
104                     // si on veux corriger http://java.net/jira/browse/GLASSFISH-12831
105                     // sous glassfish 3.0.1 et non 3.1, les bindings d'un contexte contienne le contexte lui-même
106                     //        if (jndiBinding.getName().isEmpty()) {
107                     //            return;
108                     //        }
109                     result.add(jndiBinding);
110                 } catch (final Exception e) {
111                     // catch Exception et non catch NamingException car glassfish 3.1 par exemple
112                     // lance parfois des RuntimeException encapsulant des NamingException lors du next()
113                     continue;
114                 }
115             }
116         } finally {
117             // Comme indiqué dans la javadoc enumeration.close() n'est pas nécessaire après que hasMore()
118             // a retourné false. De plus, cela provoquerait une exception dans glassfish 3.0.1
119             // "javax.naming.OperationNotSupportedException: close() not implemented"
120             // enumeration.close();
121
122             context.close();
123         }
124         return result;
125     }
126
127     private static JndiBinding createJndiBinding(String path, Binding binding) {
128         final String name = getBindingName(path, binding);
129         final String className = binding.getClassName();
130         final Object object = binding.getObject();
131         final String contextPath;
132         final String value;
133         if (object instanceof Context
134                 // "javax.naming.Context".equals(className) nécessaire pour le path "comp" dans JBoss 6.0
135                 || "javax.naming.Context".equals(className)
136                 // pour jetty :
137                 || object instanceof Reference
138                         && "javax.naming.Context".equals(((Reference) object).getClassName())) {
139             if (!path.isEmpty()) {
140                 contextPath = path + '/' + name;
141             } else {
142                 // nécessaire pour jonas 5.1.0
143                 contextPath = name;
144             }
145             value = null;
146         } else {
147             contextPath = null;
148             value = formatValue(object);
149         }
150         return new JndiBinding(name, className, contextPath, value);
151     }
152
153     private static String formatValue(Object object) {
154         String value;
155         try {
156             if (object instanceof Collection) {
157                 final StringBuilder sb = new StringBuilder();
158                 sb.append('[');
159                 boolean first = true;
160                 for (final Object aItem : (Collection<?>) object) {
161                     if (first) {
162                         first = false;
163                     } else {
164                         sb.append(",\n");
165                     }
166                     sb.append(aItem);
167                 }
168                 sb.append(']');
169                 value = sb.toString();
170             } else {
171                 value = String.valueOf(object);
172             }
173         } catch (final Exception e) {
174             value = e.toString();
175         }
176         return value;
177     }
178
179     private static String getBindingName(String path, Binding binding) {
180         // nécessaire pour glassfish 3.0.1
181         String result = binding.getName();
182         if (result.startsWith(JNDI_PREFIX)) {
183             result = result.substring(JNDI_PREFIX.length());
184         }
185         if (result.startsWith(path)) {
186             result = result.substring(path.length());
187         }
188         if (!result.isEmpty() && result.charAt(0) == '/') {
189             result = result.substring(1);
190         }
191         return result;
192     }
193
194     /** {@inheritDoc} */
195     @Override
196     public String toString() {
197         return getClass().getSimpleName() + "[name=" + getName() + ", className=" + getClassName()
198                 + ", contextPath=" + getContextPath() + ']';
199     }
200 }
201