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 */
019 package org.apache.shiro.realm.jndi;
020
021 import java.util.ArrayList;
022 import java.util.Arrays;
023 import java.util.Collection;
024 import java.util.List;
025
026 import org.apache.shiro.jndi.JndiLocator;
027 import org.apache.shiro.realm.Realm;
028 import org.apache.shiro.realm.RealmFactory;
029 import org.apache.shiro.util.StringUtils;
030
031
032 /**
033 * Looks up one or more Realm instances from JNDI using specified {@link #setJndiNames jndiNames}.
034 *
035 * <p>This is primarily provided to support Realm instances configured in JEE and EJB environments, but will
036 * work in any environment where {@link Realm Realm} instances are bound in JNDI instead of using
037 * programmatic or text-based configuration.
038 *
039 * @author Les Hazlewood
040 * @since 0.9
041 */
042 public class JndiRealmFactory extends JndiLocator implements RealmFactory {
043
044 Collection<String> jndiNames = null;
045
046 /**
047 * Returns the JNDI names that will be used to look up Realm(s) from JNDI.
048 *
049 * @return the JNDI names that will be used to look up Realm(s) from JNDI.
050 * @see #setJndiNames(String)
051 * @see #setJndiNames(Collection)
052 */
053 public Collection<String> getJndiNames() {
054 return jndiNames;
055 }
056
057 /**
058 * Sets the JNDI names that will be used to look up Realm(s) from JNDI.
059 * <p/>
060 * The order of the collection determines the order that the Realms will be returned to the SecurityManager.
061 * <p/>
062 * If you find it easier to specify these names as a comma-delmited string, you may use the
063 * {@link #setJndiNames(String)} method instead.
064 *
065 * @param jndiNames the JNDI names that will be used to look up Realm(s) from JNDI.
066 * @see #setJndiNames(String)
067 */
068 public void setJndiNames(Collection<String> jndiNames) {
069 this.jndiNames = jndiNames;
070 }
071
072 /**
073 * Specifies a comma-delimited list of JNDI names to lookup, each one corresponding to a jndi-bound
074 * {@link Realm Realm}. The Realms will be made available to the SecurityManager in the order
075 * they are defined.
076 *
077 * @param commaDelimited a comma-delimited list of JNDI names, each representing the JNDI name used to
078 * look up a corresponding jndi-bound Realm.
079 * @throws IllegalStateException if the specified argument is null or the empty string.
080 */
081 public void setJndiNames(String commaDelimited) throws IllegalStateException {
082 String arg = StringUtils.clean(commaDelimited);
083 if (arg == null) {
084 String msg = "One or more comma-delimited jndi names must be specified for the " +
085 getClass().getName() + " to locate Realms.";
086 throw new IllegalStateException(msg);
087 }
088 String[] names = StringUtils.tokenizeToStringArray(arg, ",");
089 setJndiNames(Arrays.asList(names));
090 }
091
092 /**
093 * Performs the JNDI lookups for each specified {@link #getJndiNames() JNDI name} and returns all
094 * discovered Realms in an ordered collection.
095 *
096 * <p>The returned Collection is in the same order as the specified
097 * {@link #setJndiNames(java.util.Collection) jndiNames}
098 *
099 * @return an ordered collection of the {@link #setJndiNames(java.util.Collection) specified Realms} found in JNDI.
100 * @throws IllegalStateException if any of the JNDI names fails to successfully look up a Realm instance.
101 */
102 public Collection<Realm> getRealms() throws IllegalStateException {
103 Collection<String> jndiNames = getJndiNames();
104 if (jndiNames == null || jndiNames.isEmpty()) {
105 String msg = "One or more jndi names must be specified for the " +
106 getClass().getName() + " to locate Realms.";
107 throw new IllegalStateException(msg);
108 }
109 List<Realm> realms = new ArrayList<Realm>(jndiNames.size());
110 for (String name : jndiNames) {
111 try {
112 Realm realm = (Realm) lookup(name, Realm.class);
113 realms.add(realm);
114 } catch (Exception e) {
115 throw new IllegalStateException("Unable to look up realm with jndi name '" + name + "'.", e);
116 }
117 }
118 return realms.isEmpty() ? null : realms;
119 }
120 }