001: /* Copyright 2005 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.layout.dlm;
007:
008: import java.util.ArrayList;
009: import java.util.List;
010:
011: import org.apache.commons.logging.Log;
012: import org.apache.commons.logging.LogFactory;
013: import org.jasig.portal.security.IPerson;
014: import org.jasig.portal.utils.XML;
015: import org.w3c.dom.Element;
016: import org.w3c.dom.NamedNodeMap;
017: import org.w3c.dom.Node;
018: import org.w3c.dom.NodeList;
019:
020: /**
021: * @version $Revision: 36782 $ $Date: 2007-01-26 11:39:27 -0700 (Fri, 26 Jan 2007) $
022: * @since uPortal 2.5
023: */
024: public class FragmentDefinition implements Evaluator {
025: private static final Log LOG = LogFactory
026: .getLog(FragmentDefinition.class);
027:
028: String name = null;
029: String ownerID = null;
030: int userID = -1;
031: /**
032: * User account whose layout should be copied to create a layout for new
033: * fragments.
034: */
035: private static String cDefaultLayoutOwnerId = "fragmentTemplate";
036:
037: String defaultLayoutOwnerID = null;
038: Element configDOM = null;
039: double precedence = 0.0; // precedence of fragment
040: int index = 0; // index of definition within config file
041: boolean noAudienceIncluded = false;
042: Evaluator[] evaluators = null;
043: UserView view = null;
044: List roles = null;
045:
046: /**
047: * This constructor is passed a dlm:fragment element from which this
048: * FragmentDefinition instance gathers its configuration information.
049: *
050: * @param e An Element representing a single <dlm:fragment> in dlm.xml.
051: * @throws Exception
052: */
053: public FragmentDefinition(Element e) {
054: final boolean REQUIRED = true;
055: final boolean NOT_REQUIRED = false;
056:
057: this .configDOM = e;
058: NamedNodeMap atts = e.getAttributes();
059:
060: this .name = loadAttribute("name", atts, REQUIRED);
061: this .ownerID = loadAttribute("ownerID", atts, REQUIRED);
062: this .defaultLayoutOwnerID = loadAttribute(
063: "defaultLayoutOwnerID", atts, NOT_REQUIRED);
064:
065: String precedence = loadAttribute("precedence", atts, REQUIRED);
066: try {
067: this .precedence = Double.valueOf(precedence).doubleValue();
068: } catch (NumberFormatException nfe) {
069: throw new RuntimeException(
070: "Invalid format for precedence attribute "
071: + "of <fragment> in\n'"
072: + XML.serializeNode(e), nfe);
073: }
074: loadOwnerRoles(e.getElementsByTagName("dlm:role"));
075: loadAudienceEvaluators(e.getElementsByTagName("dlm:audience"));
076: }
077:
078: public int getUserId() {
079: return this .userID;
080: }
081:
082: public void setUserId(int id) {
083: this .userID = id;
084: }
085:
086: public static String getDefaultLayoutOwnerId() {
087: return cDefaultLayoutOwnerId;
088: }
089:
090: /**
091: * Captures the values of any included dlm:role elements so that the owner
092: * can later be granted those roles during fragment activation.
093: *
094: * @param nodes
095: */
096: private void loadOwnerRoles(NodeList nodes) {
097: roles = new ArrayList();
098:
099: if (nodes != null && nodes.getLength() != 0) {
100: /*
101: * This looks really screwy but is actually necessary. For element
102: * nodes you can't simply call the getNodeValue() method on its
103: * superclass Node. That simply returns a null value. The role
104: * element has textual content only consisting of a single
105: * access group ID. So these element nodes will have a single
106: * text node child node and its getNodeValue() must be called to
107: * acquire the group ID. To play it safe I'll grab all child nodes
108: * and for concatenate the value of all child text nodes.
109: */
110: for (int i = 0; i < nodes.getLength(); i++) {
111: Element roleElement = (Element) nodes.item(i);
112: NodeList childNodes = roleElement.getChildNodes();
113: StringBuffer groupId = new StringBuffer();
114:
115: for (int j = 0; j < childNodes.getLength(); j++) {
116: Node node = childNodes.item(j);
117: if (node.getNodeType() == Node.TEXT_NODE)
118: groupId.append(node.getNodeValue());
119: }
120:
121: roles.add(groupId.toString());
122: }
123: }
124: }
125:
126: private void loadAudienceEvaluators(NodeList nodes) {
127: final String evaluatorFactoryAtt = "evaluatorFactory";
128:
129: if (nodes == null || nodes.getLength() == 0) {
130: noAudienceIncluded = true;
131: return;
132: }
133:
134: for (int i = 0; i < nodes.getLength(); i++) {
135: Node audience = nodes.item(i);
136: NamedNodeMap atts = audience.getAttributes();
137: Node att = atts.getNamedItem(evaluatorFactoryAtt);
138: if (att == null || att.getNodeValue().equals(""))
139: throw new RuntimeException("Required attibute '"
140: + evaluatorFactoryAtt + "' "
141: + "is missing or empty on 'audience' "
142: + " element in\n'"
143: + XML.serializeNode(audience) + "'");
144: String className = att.getNodeValue();
145: EvaluatorFactory factory = loadEvaluatorFactory(className,
146: audience);
147: addEvaluator(factory, audience);
148: }
149: }
150:
151: private void addEvaluator(EvaluatorFactory factory, Node audience) {
152: Evaluator evaluator = factory.getEvaluator(audience);
153:
154: if (evaluator == null)
155: throw new RuntimeException("Evaluator factory '"
156: + factory.getClass().getName() + "' failed to "
157: + "return an evaluator for 'audience' element"
158: + " in\n'" + XML.serializeNode(audience) + "'");
159: if (evaluators == null)
160: evaluators = new Evaluator[] { evaluator };
161: else {
162: Evaluator[] newArr = new Evaluator[evaluators.length + 1];
163: System.arraycopy(evaluators, 0, newArr, 0,
164: evaluators.length);
165: newArr[evaluators.length] = evaluator;
166: evaluators = newArr;
167: }
168: }
169:
170: private EvaluatorFactory loadEvaluatorFactory(
171: String factoryClassName, Node audience) {
172: Class theClass = null;
173: try {
174: theClass = Class.forName(factoryClassName);
175: } catch (ClassNotFoundException cnfe) {
176: throw new RuntimeException(
177: "java.lang.ClassNotFoundException occurred"
178: + " while loading evaluator factory class '"
179: + factoryClassName
180: + "' (or one of its "
181: + "dependent classes) for 'audience' element "
182: + "in\n'" + XML.serializeNode(audience)
183: + "'");
184: } catch (ExceptionInInitializerError eiie) {
185: throw new RuntimeException(
186: "java.lang.ExceptionInInitializerError "
187: + "occurred while "
188: + "loading evaluator factory Class '"
189: + factoryClassName
190: + "' (or one of its "
191: + "dependent classes) for 'audience' element "
192: + "in\n'" + XML.serializeNode(audience)
193: + "'. \nThis indicates that an exception "
194: + "occurred during evaluation of a static"
195: + " initializer or the initializer for a "
196: + "static variable.", eiie);
197: } catch (LinkageError le) {
198: throw new RuntimeException(
199: "java.lang.LinkageError occurred while "
200: + "loading evaluator factory Class '"
201: + factoryClassName + "' for "
202: + "'audience' element in\n'"
203: + XML.serializeNode(audience)
204: + "'. \nThis typically means that a "
205: + "dependent class has changed "
206: + "incompatibly after compiling the "
207: + "factory class.", le);
208: }
209:
210: Object theInstance = null;
211:
212: try {
213: theInstance = theClass.newInstance();
214: } catch (IllegalAccessException iae) {
215: throw new RuntimeException(
216: "java.lang.IllegalAccessException occurred "
217: + "while loading evaluator factory Class '"
218: + factoryClassName
219: + "' (or one of its "
220: + "dependent classes) for 'audience' element "
221: + "in\n'" + XML.serializeNode(audience)
222: + "' \nVerify that this is a public class "
223: + "and that it contains a public, zero "
224: + "argument constructor.", iae);
225: } catch (InstantiationException ie) {
226: throw new RuntimeException(
227: "java.lang.InstantiationException occurred "
228: + "while loading evaluator factory Class '"
229: + factoryClassName
230: + "' (or one of its "
231: + "dependent classes) for 'audience' element "
232: + "in\n'"
233: + XML.serializeNode(audience)
234: + "' \nVerify that the specified class is a "
235: + "class and not an interface or abstract "
236: + "class.", ie);
237: }
238: try {
239: return (EvaluatorFactory) theInstance;
240: } catch (ClassCastException cce) {
241: throw new RuntimeException(
242: "java.lang.ClassCastException occurred "
243: + "while loading evaluator factory Class '"
244: + factoryClassName
245: + "' (or one of its "
246: + "dependent classes) for 'audience' element "
247: + "in\n'"
248: + XML.serializeNode(audience)
249: + "'. \nVerify that the class implements the "
250: + "EvaluatorFactory interface.", cce);
251: }
252: }
253:
254: public boolean isApplicable(IPerson p) {
255: boolean isApplicable = false;
256: if (LOG.isInfoEnabled())
257: LOG.info(">>>> calling " + name + ".isApplicable( "
258: + p.getAttribute("username") + " )");
259: if (userID == -1 || view == null || evaluators == null) {
260: isApplicable = false;
261: } else {
262: for (int i = 0; i < evaluators.length; i++)
263: if (evaluators[i].isApplicable(p)) {
264: isApplicable = true;
265: break;
266: }
267: }
268: if (LOG.isInfoEnabled())
269: LOG
270: .info("---- " + name + ".isApplicable( "
271: + p.getAttribute("username") + " )="
272: + isApplicable);
273: return isApplicable;
274: }
275:
276: private String loadAttribute(String name, NamedNodeMap atts,
277: boolean required) {
278: Node att = atts.getNamedItem(name);
279: if (required && (att == null || att.getNodeValue().equals("")))
280: throw new RuntimeException("Missing or empty attribute '"
281: + name + "' required by <fragment> in\n'"
282: + XML.serializeNode(this .configDOM) + "'");
283: if (att == null)
284: return null;
285: return att.getNodeValue();
286: }
287: }
|