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