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 }