1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. 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,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.shiro.jndi;
20
21 import java.util.Properties;
22 import javax.naming.NamingException;
23
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28 * Convenient superclass for JNDI accessors, providing "jndiTemplate"
29 * and "jndiEnvironment" bean properties.
30 *
31 * <p>Note that this implementation is an almost exact combined copy of the Spring Framework's 'JndiAccessor' and
32 * 'JndiLocatorSupport' classes from their 2.5.4 distribution - we didn't want to re-invent the wheel, but not require
33 * a full dependency on the Spring framework, nor does Spring make available only its JNDI classes in a small jar, or
34 * we would have used that. Since Shiro is also Apache 2.0 licensed, all regular licenses and conditions and
35 * authors have remained in tact.
36 *
37 * @see #setJndiTemplate
38 * @see #setJndiEnvironment
39 * @see #setResourceRef
40 * @since 1.1
41 */
42 public class JndiLocator {
43
44 /**
45 * JNDI prefix used in a Jakarta EE container
46 */
47 public static final String CONTAINER_PREFIX = "java:comp/env/";
48
49 /**
50 * Private class log.
51 */
52 private static final Logger LOGGER = LoggerFactory.getLogger(JndiLocator.class);
53
54 private boolean resourceRef;
55
56 private JndiTemplate jndiTemplate = new JndiTemplate();
57
58
59 /**
60 * Set the JNDI template to use for JNDI lookups.
61 * <p>You can also specify JNDI environment settings via "jndiEnvironment".
62 *
63 * @see #setJndiEnvironment
64 */
65 public void setJndiTemplate(JndiTemplate jndiTemplate) {
66 this.jndiTemplate = (jndiTemplate != null ? jndiTemplate : new JndiTemplate());
67 }
68
69 /**
70 * Return the JNDI template to use for JNDI lookups.
71 */
72 public JndiTemplate getJndiTemplate() {
73 return this.jndiTemplate;
74 }
75
76 /**
77 * Set the JNDI environment to use for JNDI lookups.
78 * <p>Creates a JndiTemplate with the given environment settings.
79 *
80 * @see #setJndiTemplate
81 */
82 public void setJndiEnvironment(Properties jndiEnvironment) {
83 this.jndiTemplate = new JndiTemplate(jndiEnvironment);
84 }
85
86 /**
87 * Return the JNDI environment to use for JNDI lookups.
88 */
89 public Properties getJndiEnvironment() {
90 return this.jndiTemplate.getEnvironment();
91 }
92
93 /**
94 * Set whether the lookup occurs in a Jakarta EE container, i.e. if the prefix
95 * "java:comp/env/" needs to be added if the JNDI name doesn't already
96 * contain it. Default is "false".
97 * <p>Note: Will only get applied if no other scheme (e.g. "java:") is given.
98 */
99 public void setResourceRef(boolean resourceRef) {
100 this.resourceRef = resourceRef;
101 }
102
103 /**
104 * Return whether the lookup occurs in a Jakarta EE container.
105 */
106 public boolean isResourceRef() {
107 return this.resourceRef;
108 }
109
110
111 /**
112 * Perform an actual JNDI lookup for the given name via the JndiTemplate.
113 * <p>If the name doesn't begin with "java:comp/env/", this prefix is added
114 * if "resourceRef" is set to "true".
115 *
116 * @param jndiName the JNDI name to look up
117 * @return the obtained object
118 * @throws javax.naming.NamingException if the JNDI lookup failed
119 * @see #setResourceRef
120 */
121 protected Object lookup(String jndiName) throws NamingException {
122 return lookup(jndiName, null);
123 }
124
125 /**
126 * Perform an actual JNDI lookup for the given name via the JndiTemplate.
127 * <p>If the name doesn't begin with "java:comp/env/", this prefix is added
128 * if "resourceRef" is set to "true".
129 *
130 * @param jndiName the JNDI name to look up
131 * @param requiredType the required type of the object
132 * @return the obtained object
133 * @throws NamingException if the JNDI lookup failed
134 * @see #setResourceRef
135 */
136 protected Object lookup(String jndiName, Class requiredType) throws NamingException {
137 if (jndiName == null) {
138 throw new IllegalArgumentException("jndiName argument must not be null");
139 }
140 String convertedName = convertJndiName(jndiName);
141 Object jndiObject;
142 try {
143 jndiObject = getJndiTemplate().lookup(convertedName, requiredType);
144 } catch (NamingException ex) {
145 if (!convertedName.equals(jndiName)) {
146 // Try fallback to originally specified name...
147 if (LOGGER.isDebugEnabled()) {
148 LOGGER.debug("Converted JNDI name [" + convertedName
149 + "] not found - trying original name [" + jndiName + "]. " + ex);
150 }
151 jndiObject = getJndiTemplate().lookup(jndiName, requiredType);
152 } else {
153 throw ex;
154 }
155 }
156 LOGGER.debug("Located object with JNDI name '{}'", convertedName);
157 return jndiObject;
158 }
159
160 /**
161 * Convert the given JNDI name into the actual JNDI name to use.
162 * <p>The default implementation applies the "java:comp/env/" prefix if
163 * "resourceRef" is "true" and no other scheme (e.g. "java:") is given.
164 *
165 * @param jndiName the original JNDI name
166 * @return the JNDI name to use
167 * @see #CONTAINER_PREFIX
168 * @see #setResourceRef
169 */
170 protected String convertJndiName(String jndiName) {
171 // Prepend container prefix if not already specified and no other scheme given.
172 if (isResourceRef() && !jndiName.startsWith(CONTAINER_PREFIX) && jndiName.indexOf(':') == -1) {
173 jndiName = CONTAINER_PREFIX + jndiName;
174 }
175 return jndiName;
176 }
177
178 }