View Javadoc
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       * Private class log.
46       */
47      private static final Logger log = LoggerFactory.getLogger(JndiLocator.class);
48  
49      /**
50       * JNDI prefix used in a J2EE container
51       */
52      public static final String CONTAINER_PREFIX = "java:comp/env/";
53  
54      private boolean resourceRef = false;
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 J2EE 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 J2EE 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         }
145         catch (NamingException ex) {
146             if (!convertedName.equals(jndiName)) {
147                 // Try fallback to originally specified name...
148                 if (log.isDebugEnabled()) {
149                     log.debug("Converted JNDI name [" + convertedName +
150                             "] not found - trying original name [" + jndiName + "]. " + ex);
151                 }
152                 jndiObject = getJndiTemplate().lookup(jndiName, requiredType);
153             } else {
154                 throw ex;
155             }
156         }
157         log.debug("Located object with JNDI name '{}'", convertedName);
158         return jndiObject;
159     }
160 
161     /**
162      * Convert the given JNDI name into the actual JNDI name to use.
163      * <p>The default implementation applies the "java:comp/env/" prefix if
164      * "resourceRef" is "true" and no other scheme (e.g. "java:") is given.
165      *
166      * @param jndiName the original JNDI name
167      * @return the JNDI name to use
168      * @see #CONTAINER_PREFIX
169      * @see #setResourceRef
170      */
171     protected String convertJndiName(String jndiName) {
172         // Prepend container prefix if not already specified and no other scheme given.
173         if (isResourceRef() && !jndiName.startsWith(CONTAINER_PREFIX) && jndiName.indexOf(':') == -1) {
174             jndiName = CONTAINER_PREFIX + jndiName;
175         }
176         return jndiName;
177     }
178 
179 }