001: /* Copyright 2004 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.services.persondir.support;
007:
008: import java.util.Collections;
009: import java.util.HashMap;
010: import java.util.HashSet;
011: import java.util.Iterator;
012: import java.util.List;
013: import java.util.Map;
014: import java.util.Set;
015:
016: import org.jasig.portal.services.persondir.IPersonAttributeDao;
017: import org.jasig.portal.services.persondir.support.merger.IAttributeMerger;
018: import org.jasig.portal.services.persondir.support.merger.MultivaluedAttributeMerger;
019:
020: /**
021: * A {@link IPersonAttributeDao} implementation which iterates over children
022: * IPersonAttributeDaos queries each with the same data and merges their
023: * reported attributes in a configurable way. The default merger is
024: * {@link MultivaluedAttributeMerger}.
025: *
026: * @author andrew.petro@yale.edu
027: * @author Eric Dalquist <a href="mailto:edalquist@unicon.net">edalquist@unicon.net</a>
028: * @version $Revision: 35515 $ $Date: 2005-04-06 08:31:40 -0700 (Wed, 06 Apr 2005) $
029: * @since uPortal 2.5
030: */
031: public class MergingPersonAttributeDaoImpl extends
032: AbstractDefaultQueryPersonAttributeDao {
033:
034: /**
035: * A List of child IPersonAttributeDao instances which we will poll in order.
036: */
037: private List personAttributeDaos;
038:
039: /**
040: * Strategy for merging together the results from successive PersonAttributeDaos.
041: */
042: private IAttributeMerger attrMerger = new MultivaluedAttributeMerger();
043:
044: /**
045: * True if we should catch, log, and ignore Throwables propogated by
046: * individual DAOs.
047: */
048: private boolean recoverExceptions = true;
049:
050: /**
051: * Iterates through the configured {@link List} of {@link IPersonAttributeDao}
052: * instances. The results from each DAO are merged into the result {@link Map}
053: * by the configured {@link IAttributeMerger}.
054: *
055: * @see org.jasig.portal.services.persondir.IPersonAttributeDao#getUserAttributes(java.util.Map)
056: */
057: public Map getUserAttributes(final Map seed) {
058: //Ensure the arguements and state are valid
059: if (seed == null)
060: throw new IllegalArgumentException(
061: "The query seed Map cannot be null.");
062:
063: if (this .personAttributeDaos == null)
064: throw new IllegalStateException(
065: "No IPersonAttributeDaos have been specified.");
066:
067: //Initialize null, so that if none of the sub-DAOs find the user null is returned appropriately
068: Map resultAttributes = null;
069:
070: //Iterate through the configured IPersonAttributeDaos, querying each.
071: for (final Iterator iter = this .personAttributeDaos.iterator(); iter
072: .hasNext();) {
073: final IPersonAttributeDao currentlyConsidering = (IPersonAttributeDao) iter
074: .next();
075:
076: Map currentAttributes = new HashMap();
077: try {
078: currentAttributes = currentlyConsidering
079: .getUserAttributes(seed);
080: } catch (final RuntimeException rte) {
081: final String msg = "Exception thrown by DAO: "
082: + currentlyConsidering;
083:
084: if (this .recoverExceptions) {
085: log.warn("Recovering From " + msg, rte);
086: } else {
087: log.error(msg, rte);
088: throw rte;
089: }
090: }
091:
092: if (resultAttributes == null) {
093: //If this is the first valid result set just use it.
094: resultAttributes = currentAttributes;
095: } else if (currentAttributes != null) {
096: //Perform the appropriate attribute attrMerger
097: resultAttributes = this .attrMerger.mergeAttributes(
098: resultAttributes, currentAttributes);
099: }
100: }
101:
102: return resultAttributes;
103: }
104:
105: /**
106: * This implementation is not always correct.
107: * It handles the basic case where the Set of attributes returned by this
108: * implementation is the union of the attributes declared by all of the
109: * underlying implementations to be merged. Of course, an IAttributeMerger
110: * might provide for a merging policy such that the attributes resulting from
111: * invoking this IPersonAttributeDao implementation are not the union
112: * of the attributes declared by the underlying PersonAttributeDaos.
113: *
114: * @see org.jasig.portal.services.persondir.IPersonAttributeDao#getPossibleUserAttributeNames()
115: */
116: public Set getPossibleUserAttributeNames() {
117: final Set attrNames = new HashSet();
118:
119: for (final Iterator iter = this .personAttributeDaos.iterator(); iter
120: .hasNext();) {
121: final IPersonAttributeDao currentDao = (IPersonAttributeDao) iter
122: .next();
123:
124: Set currentDaoAttrNames = null;
125: try {
126: currentDaoAttrNames = currentDao
127: .getPossibleUserAttributeNames();
128: } catch (final RuntimeException rte) {
129: final String msg = "Exception thrown by DAO: "
130: + currentDao;
131:
132: if (this .recoverExceptions) {
133: log.warn(msg, rte);
134: } else {
135: log.error(msg, rte);
136: throw rte;
137: }
138: }
139:
140: if (currentDaoAttrNames != null)
141: attrNames.addAll(currentDaoAttrNames);
142: }
143:
144: return Collections.unmodifiableSet(attrNames);
145: }
146:
147: /**
148: * Get the strategy whereby we accumulate attributes.
149: *
150: * @return Returns the attrMerger.
151: */
152: public IAttributeMerger getMerger() {
153: return this .attrMerger;
154: }
155:
156: /**
157: * Set the strategy whereby we accumulate attributes from the results of
158: * polling our delegates.
159: *
160: * @param merger The attrMerger to set.
161: * @throws IllegalArgumentException If merger is <code>null</code>.
162: */
163: public void setMerger(final IAttributeMerger merger) {
164: if (merger == null)
165: throw new IllegalArgumentException(
166: "The merger cannot be null");
167:
168: this .attrMerger = merger;
169: }
170:
171: /**
172: * Get the {@link List} of delegates which we will poll for attributes.
173: *
174: * @return Returns the personAttributeDaos.
175: */
176: public List getPersonAttributeDaos() {
177: return this .personAttributeDaos;
178: }
179:
180: /**
181: * Set the {@link List} of delegates which we will poll for attributes.
182: *
183: * @param daos The personAttributeDaos to set.
184: * @throws IllegalArgumentException If daos is <code>null</code>.
185: */
186: public void setPersonAttributeDaos(final List daos) {
187: if (daos == null)
188: throw new IllegalArgumentException(
189: "The dao list cannot be null");
190:
191: this .personAttributeDaos = Collections.unmodifiableList(daos);
192: }
193:
194: /**
195: * True if this class will catch exceptions thrown by its delegate DAOs
196: * and fail to propogate them. False if this class will stop on failure.
197: *
198: * @return Returns the recoverExceptions.
199: */
200: public boolean isRecoverExceptions() {
201: return this .recoverExceptions;
202: }
203:
204: /**
205: * Set to true if you would like this class to swallow RuntimeExceptions
206: * thrown by its delegates. This allows it to recover if a particular attribute
207: * source fails, still considering previous and subsequent sources.
208: * Set to false if you would like this class to fail hard upon any Throwable
209: * thrown by its children. This is desirable in cases where your Portal will not
210: * function without attributes from all of its sources.
211: *
212: * @param recover The recoverExceptions to set.
213: */
214: public void setRecoverExceptions(boolean recover) {
215: this.recoverExceptions = recover;
216: }
217: }
|