001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.shiro.jndi;
020
021import java.util.Properties;
022import javax.naming.NamingException;
023
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027/**
028 * Convenient superclass for JNDI accessors, providing "jndiTemplate"
029 * and "jndiEnvironment" bean properties.
030 *
031 * <p>Note that this implementation is an almost exact combined copy of the Spring Framework's 'JndiAccessor' and
032 * 'JndiLocatorSupport' classes from their 2.5.4 distribution - we didn't want to re-invent the wheel, but not require
033 * a full dependency on the Spring framework, nor does Spring make available only its JNDI classes in a small jar, or
034 * we would have used that. Since Shiro is also Apache 2.0 licensed, all regular licenses and conditions and
035 * authors have remained in tact.
036 *
037 * @see #setJndiTemplate
038 * @see #setJndiEnvironment
039 * @see #setResourceRef
040 * @since 1.1
041 */
042public class JndiLocator {
043
044    /**
045     * Private class log.
046     */
047    private static final Logger log = LoggerFactory.getLogger(JndiLocator.class);
048
049    /**
050     * JNDI prefix used in a J2EE container
051     */
052    public static final String CONTAINER_PREFIX = "java:comp/env/";
053
054    private boolean resourceRef = false;
055
056    private JndiTemplate jndiTemplate = new JndiTemplate();
057
058
059    /**
060     * Set the JNDI template to use for JNDI lookups.
061     * <p>You can also specify JNDI environment settings via "jndiEnvironment".
062     *
063     * @see #setJndiEnvironment
064     */
065    public void setJndiTemplate(JndiTemplate jndiTemplate) {
066        this.jndiTemplate = (jndiTemplate != null ? jndiTemplate : new JndiTemplate());
067    }
068
069    /**
070     * Return the JNDI template to use for JNDI lookups.
071     */
072    public JndiTemplate getJndiTemplate() {
073        return this.jndiTemplate;
074    }
075
076    /**
077     * Set the JNDI environment to use for JNDI lookups.
078     * <p>Creates a JndiTemplate with the given environment settings.
079     *
080     * @see #setJndiTemplate
081     */
082    public void setJndiEnvironment(Properties jndiEnvironment) {
083        this.jndiTemplate = new JndiTemplate(jndiEnvironment);
084    }
085
086    /**
087     * Return the JNDI environment to use for JNDI lookups.
088     */
089    public Properties getJndiEnvironment() {
090        return this.jndiTemplate.getEnvironment();
091    }
092
093    /**
094     * Set whether the lookup occurs in a J2EE container, i.e. if the prefix
095     * "java:comp/env/" needs to be added if the JNDI name doesn't already
096     * contain it. Default is "false".
097     * <p>Note: Will only get applied if no other scheme (e.g. "java:") is given.
098     */
099    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}