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.io.PrintWriter;
009: import java.io.StringWriter;
010: import java.util.ArrayList;
011: import java.util.Arrays;
012: import java.util.Enumeration;
013: import java.util.Vector;
014:
015: import org.apache.commons.logging.Log;
016: import org.apache.commons.logging.LogFactory;
017: import org.jasig.portal.AuthorizationException;
018: import org.jasig.portal.IUserIdentityStore;
019: import org.jasig.portal.UserIdentityStoreFactory;
020: import org.jasig.portal.UserProfile;
021: import org.jasig.portal.properties.PropertiesManager;
022: import org.jasig.portal.security.IPerson;
023: import org.jasig.portal.security.provider.PersonImpl;
024: import org.w3c.dom.Document;
025: import org.w3c.dom.Element;
026: import org.w3c.dom.Node;
027: import org.w3c.dom.NodeList;
028:
029: /**
030: * @version $Revision: 36294 $ $Date: 2005-11-10 12:36:31 -0700 (Thu, 10 Nov 2005) $
031: * @since uPortal 2.5
032: */
033: public class FragmentActivator {
034: public static final String RCS_ID = "@(#) $Header$";
035: private static Log LOG = LogFactory.getLog(FragmentActivator.class);
036:
037: private FragmentDefinition[] fragments = null;
038: private IUserIdentityStore identityStore = null;
039: private RDBMDistributedLayoutStore dls = null;
040: private IRoleUpdater mRoleUpdater = null;
041:
042: private static final int CHANNELS = 0;
043: private static final int FOLDERS = 1;
044:
045: public FragmentActivator(RDBMDistributedLayoutStore dls,
046: FragmentDefinition[] fragments) {
047: identityStore = UserIdentityStoreFactory
048: .getUserIdentityStoreImpl();
049: this .dls = dls;
050: this .fragments = fragments;
051: // TODO add a role updater after we get DLM working in uP proper.
052: /*
053: try
054: {
055: Class cls = Class.forName("com.pipeline.uportal.DLMRoleUpdater");
056: mRoleUpdater = (IRoleUpdater) cls.newInstance();
057: }
058: catch(Exception e)
059: {
060: StringWriter sw = new StringWriter();
061: PrintWriter pw = new PrintWriter( sw );
062: e.printStackTrace( pw );
063: pw.flush();
064: ls.log( ls.ERROR,
065: "\n\n------ Fragment Role Adjustment Problem ------\n" +
066: "Fragment Owner Roles will not be adjusted. \nMessage: " +
067: e.getMessage() + "\nDetails:\n" +
068: sw.toString());
069: }
070: */
071: }
072:
073: void activateFragments() {
074: if (LOG.isDebugEnabled())
075: LOG.debug("\n\n------ Distributed Layout ------\n"
076: + "properties loaded = " + dls.getPropertyCount()
077: + "\nfragment definitions loaded = "
078: + (fragments == null ? 0 : fragments.length)
079: + "\n\n------ Beginning Activation ------\n");
080: if (fragments == null) {
081: if (LOG.isDebugEnabled())
082: LOG.debug("\n\nNo Fragments to Activate.");
083: } else {
084: for (int i = 0; i < fragments.length; i++)
085: if (fragments[i].evaluators == null) {
086: if (LOG.isDebugEnabled())
087: LOG.debug("\n\n------ skipping " + i + " - "
088: + fragments[i].name
089: + ", no evaluators found");
090: } else {
091: if (LOG.isDebugEnabled())
092: LOG.debug("\n\n------ activating " + i + " - "
093: + fragments[i].name);
094:
095: try {
096: IPerson owner = bindToOwner(fragments[i]);
097: updateOwnerRoles(fragments[i]);
098: UserView view = new UserView();
099: loadLayout(view, fragments[i], owner);
100:
101: // if owner just created we need to push the layout into
102: // the db so that our fragment template user is used and
103: // not the default template user as determined by
104: // the user identity store.
105: if (owner.getAttribute("newlyCreated") != null) {
106: owner.setAttribute(Constants.PLF,
107: view.layout);
108: saveLayout(view, owner);
109: }
110: loadPreferences(view, fragments[i]);
111: fragmentizeLayout(view, fragments[i]);
112: fragmentizeTSUP(view, fragments[i]);
113: fragmentizeSSUP(view, fragments[i]);
114: fragments[i].view = view;
115: if (LOG.isDebugEnabled())
116: LOG.debug("\n\n------ done activating "
117: + fragments[i].name);
118: } catch (Exception e) {
119: // problem loading so none of it should be used
120: fragments[i].view = null;
121: StringWriter sw = new StringWriter();
122: PrintWriter pw = new PrintWriter(sw);
123: e.printStackTrace(pw);
124: pw.flush();
125: LOG
126: .error("\n\n------ Problem occurred activating "
127: + fragments[i].name
128: + "------\n"
129: + (e.getMessage() != null ? e
130: .getMessage()
131: + "\n\n" : "")
132: + sw.toString());
133: }
134: }
135:
136: // lastly sort according to precedence followed by index
137: Arrays.sort(fragments, new FragmentComparator());
138:
139: // show sort order in log file if debug is on. (Could check and
140: // only build of on but do later.)
141: StringBuffer bfr = new StringBuffer();
142: bfr.append(fragments[0].name);
143: bfr.append("[");
144: bfr.append(fragments[0].precedence);
145: bfr.append("]");
146:
147: for (int i = 1; i < fragments.length; i++) {
148: bfr.append(",\n");
149: bfr.append(fragments[i].name);
150: bfr.append("[");
151: bfr.append(fragments[i].precedence);
152: bfr.append("]");
153: }
154: if (LOG.isDebugEnabled())
155: LOG
156: .debug("\n\nFragments Sorted by Precedence and then index {\n"
157: + bfr.toString() + " }\n");
158: }
159: // now let other threads in to get their layouts.
160: dls.activationFinished();
161: if (LOG.isDebugEnabled())
162: LOG.debug("\n\n------ done with Activation ------\n");
163: }
164:
165: /**
166: * Saves the loaded layout in the database for the user and profile.
167: * @param view
168: * @param owner
169: * @throws Exception
170: */
171: private void saveLayout(UserView view, IPerson owner)
172: throws Exception {
173: UserProfile profile = new UserProfile();
174: profile.setProfileId(view.profileId);
175: dls.setUserLayout(owner, profile, view.layout, true, false);
176: }
177:
178: /**
179: * Makes sure that the fragment owner has the roles specified and no
180: * others.
181: *
182: * @param definition The frament definition
183: */
184: private void updateOwnerRoles(FragmentDefinition definition) {
185: if (mRoleUpdater != null) {
186: mRoleUpdater.setFragmentOwnerRoles(definition.ownerID,
187: new ArrayList(definition.roles));
188: }
189: }
190:
191: private IPerson bindToOwner(FragmentDefinition fragment) {
192: IPerson owner = new PersonImpl();
193: owner.setAttribute("username", fragment.ownerID);
194: int userID = -1;
195:
196: try {
197: userID = identityStore.getPortalUID(owner, false);
198: } catch (AuthorizationException ae) {
199: // current implementation of RDMBUserIdentityStore throws an
200: // auth exception if the user doesn't exist even if
201: // create data is false as we have it here. So this exception
202: // can be discarded since we check for the userID being -1
203: // meaning that the user wasn't found to trigger creating
204: // that user.
205: }
206: if (userID == -1) {
207: userID = createOwner(owner, fragment);
208: owner.setAttribute("newlyCreated", "" + (userID != -1));
209: }
210: fragment.userID = userID;
211: owner.setID(userID);
212: return owner;
213: }
214:
215: private int createOwner(IPerson owner, FragmentDefinition fragment) {
216: String defaultUser = null;
217: int userID = -1;
218:
219: if (fragment.defaultLayoutOwnerID != null)
220: defaultUser = fragment.defaultLayoutOwnerID;
221: else if (dls.getProperty("defaultLayoutOwner") != null)
222: defaultUser = dls.getProperty("defaultLayoutOwner");
223: else
224: try {
225: defaultUser = PropertiesManager
226: .getProperty(RDBMDistributedLayoutStore.TEMPLATE_USER_NAME);
227: } catch (RuntimeException re) {
228: throw new RuntimeException(
229: "\n\n WARNING: defaultLayoutOwner is not specified"
230: + " in dlm.xml and no default user is configured for "
231: + "the system. Owner '"
232: + fragment.ownerID
233: + "' for "
234: + "fragment '"
235: + fragment.name
236: + "' can not be "
237: + "created. The fragment will not be available for "
238: + "inclusion into user layouts.\n", re);
239: }
240:
241: if (LOG.isDebugEnabled())
242: LOG.debug("\n\nOwner '" + fragment.ownerID
243: + "' of fragment '" + fragment.name
244: + "' not found. Creating as copy of '"
245: + defaultUser + "'\n");
246:
247: if (defaultUser != null)
248: owner.setAttribute("uPortalTemplateUserName", defaultUser);
249:
250: try {
251: userID = identityStore.getPortalUID(owner, true);
252: } catch (AuthorizationException ae) {
253: throw new RuntimeException(
254: "\n\nWARNING: Anomaly occurred while creating owner '"
255: + fragment.ownerID
256: + "' of fragment '"
257: + fragment.name
258: + "'. The fragment will not be "
259: + "available for inclusion into user layouts.",
260: ae);
261: }
262: return userID;
263: }
264:
265: private void loadLayout(UserView view, FragmentDefinition fragment,
266: IPerson owner) {
267: // if fragment not bound to user can't return any layouts.
268: if (fragment.userID == -1)
269: return;
270:
271: // this area is hacked right now. Time won't permit how to handle
272: // matching up multiple profiles for a fragment with an appropriate
273: // one for incorporating into a user's layout based on their profile
274: // when they log in with a certain user agent. The challenge is
275: // being able to match up profiles for a user with those of a
276: // fragment. Until this is resolved only one profile will be supported
277: // and will have a hard coded id of 1 which is the default for profiles.
278: // If anyone changes this user all heck could break loose for dlm. :-(
279:
280: Document layout = null;
281:
282: try {
283: // fix hard coded 1 later for multiple profiles
284: UserProfile profile = dls.getUserProfileById(owner, 1);
285:
286: // see if we have structure & theme stylesheets for this user yet.
287: // If not then fall back on system's selected stylesheets.
288: if (profile.getStructureStylesheetId() == 0
289: || profile.getThemeStylesheetId() == 0)
290: profile = dls.getSystemProfileById(profile
291: .getProfileId());
292:
293: view.profileId = profile.getProfileId();
294: view.layoutId = profile.getLayoutId();
295: view.structureStylesheetId = profile
296: .getStructureStylesheetId();
297: view.themeStylesheetId = profile.getThemeStylesheetId();
298:
299: layout = dls.getFragmentLayout(owner, profile);
300: Element root = layout.getDocumentElement();
301: root.setAttribute(Constants.ATT_ID,
302: Constants.FRAGMENT_ID_USER_PREFIX + fragment.userID
303: + Constants.FRAGMENT_ID_LAYOUT_PREFIX
304: + view.layoutId);
305: view.layout = layout;
306: } catch (Exception e) {
307: throw new RuntimeException(
308: "Anomaly occurred while loading layout for fragment '"
309: + fragment.name
310: + "'. The fragment will not be "
311: + "available for inclusion into user layouts.",
312: e);
313: }
314: }
315:
316: private void loadPreferences(UserView view,
317: FragmentDefinition fragment) {
318: // if fragment not bound to user can't return any preferences.
319: if (fragment.userID == -1)
320: return;
321:
322: IPerson p = new PersonImpl();
323: p.setID(fragment.userID);
324: p.setAttribute("username", fragment.ownerID);
325:
326: try {
327: view.structUserPrefs = dls.getDistributedSSUP(p,
328: view.profileId, view.structureStylesheetId);
329: view.themeUserPrefs = dls.getDistributedTSUP(p,
330: view.profileId, view.themeStylesheetId);
331: } catch (Exception e) {
332: throw new RuntimeException(
333: "Anomaly occurred while loading structure or theme "
334: + "stylesheet user preferences for fragment '"
335: + fragment.name
336: + "'. The fragment will not be "
337: + "available for inclusion into user layouts.",
338: e);
339: }
340: }
341:
342: /**
343: * Changes channel and folder ids on the structure stylesheet user
344: * preference object to
345: * the globally safe version containing user id and layout id from which
346: * they came. This is done prior to these preferences being available for
347: * incorporation into a regular user's preferences from an incorporated
348: * layout.
349: */
350: void fragmentizeSSUP(UserView view, FragmentDefinition fragment) {
351: Element root = view.layout.getDocumentElement();
352: String labelBase = root.getAttribute(Constants.ATT_ID);
353: fragmentizeIds(labelBase, view.structUserPrefs, FOLDERS);
354: fragmentizeIds(labelBase, view.structUserPrefs, CHANNELS);
355: }
356:
357: /**
358: * Changes channel ids on the theme stylesheet user preference object to
359: * the globally safe version containing user id and layout id from which
360: * they came. This is done prior to these preferences being available for
361: * incorporation into a regular user's preferences from an incorporated
362: * layout.
363: */
364: void fragmentizeTSUP(UserView view, FragmentDefinition fragment) {
365: Element root = view.layout.getDocumentElement();
366: String labelBase = root.getAttribute(Constants.ATT_ID);
367: fragmentizeIds(labelBase, view.themeUserPrefs, CHANNELS);
368: }
369:
370: /**
371: * Changes user preference ids of folders or channels from the uPortal
372: * default of sXX for
373: * folders and nXX for channels to a globally safe value containing the
374: * user id and layout id from which the node came.
375: */
376: private void fragmentizeIds(String labelBase,
377: DistributedUserPreferences up, int which) {
378: Enumeration elements = null;
379: if (which == CHANNELS)
380: elements = up.getChannels();
381: else
382: elements = up.getFolders();
383:
384: // grab the list of elements that have user changed attributes
385: Vector list = new Vector();
386: while (elements.hasMoreElements())
387: list.add(elements.nextElement());
388: elements = list.elements();
389:
390: // now change their id's to the globally unique values
391: while (elements.hasMoreElements()) {
392: String id = (String) elements.nextElement();
393: if (!id.startsWith(Constants.FRAGMENT_ID_USER_PREFIX)) // already converted don't change
394: {
395: if (which == CHANNELS)
396: up.changeChannelId(id, labelBase + id);
397: else
398: up.changeFolderId(id, labelBase + id);
399: }
400: }
401: }
402:
403: /**
404: * Removes all top level folders that are hidden, header, or footer and
405: * then changes all node ids to their globally safe incorporated version.
406: */
407: void fragmentizeLayout(UserView view, FragmentDefinition fragment) {
408: // if fragment not bound to user or layout empty due to error, return
409: if (fragment.userID == -1 || view.layout == null)
410: return;
411:
412: // remove all non-regular or hidden top level folders
413: // skip root folder that is only child of top level layout element
414: Element layout = view.layout.getDocumentElement();
415: Element root = (Element) layout.getFirstChild();
416: NodeList children = root.getChildNodes();
417:
418: // process the children backwards since as we delete some the indices
419: // shift around
420: for (int i = children.getLength() - 1; i >= 0; i--) {
421: Node node = children.item(i);
422: if (node.getNodeType() == Node.ELEMENT_NODE
423: && node.getNodeName().equals("folder")) {
424: Element folder = (Element) node;
425:
426: // strip out folder types 'header', 'footer' and regular,
427: // hidden folder "User Preferences" since users have their own
428: if (!folder.getAttribute("type").equals("regular")
429: || folder.getAttribute("hidden").equals("true"))
430: try {
431: root.removeChild(folder);
432: } catch (Exception e) {
433: throw new RuntimeException(
434: "Anomaly occurred while stripping out "
435: + " portions of layout for fragment '"
436: + fragment.name
437: + "'. The fragment will not be available for "
438: + "inclusion into user layouts.",
439: e);
440: }
441: }
442: }
443: // now re-lable all remaining nodes below root to have a safe system
444: // wide id.
445:
446: setIdsAndAttribs(layout, layout.getAttribute(Constants.ATT_ID),
447: "" + fragment.index, "" + fragment.precedence);
448: }
449:
450: /**
451: * Recursive method that passes through a layout tree and changes all ids
452: * from the regular format of sXX or nXX to the globally safe incorporated
453: * id of form uXlXsXX or uXlXnXX indicating the user id and layout id from
454: * which this node came.
455: */
456: private void setIdsAndAttribs(Element parent, String labelBase,
457: String index, String precedence) {
458: NodeList children = parent.getChildNodes();
459:
460: for (int i = 0; i < children.getLength(); i++) {
461: if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
462: Element child = (Element) children.item(i);
463: String id = child.getAttribute(Constants.ATT_ID);
464: if (!id.equals("")) {
465: String newId = labelBase + id;
466: child.setAttribute(Constants.ATT_ID, newId);
467: child.setIdAttribute(Constants.ATT_ID, true);
468: child.setAttributeNS(Constants.NS_URI,
469: Constants.ATT_FRAGMENT, index);
470: child.setAttributeNS(Constants.NS_URI,
471: Constants.ATT_PRECEDENCE, precedence);
472: setIdsAndAttribs(child, labelBase, index,
473: precedence);
474: }
475: }
476: }
477: }
478: }
|