001: /* Copyright 2002 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.alm.tool;
007:
008: import java.sql.Connection;
009: import java.sql.PreparedStatement;
010: import java.sql.ResultSet;
011: import java.util.HashMap;
012: import java.util.Map;
013: import java.util.Set;
014: import java.util.Iterator;
015: import java.util.StringTokenizer;
016:
017: import javax.xml.transform.TransformerFactory;
018: import javax.xml.transform.sax.SAXTransformerFactory;
019: import javax.xml.transform.sax.TransformerHandler;
020: import javax.xml.transform.stream.StreamResult;
021: import javax.xml.transform.stream.StreamSource;
022:
023: import org.jasig.portal.ChannelDefinition;
024: import org.jasig.portal.IChannelRegistryStore;
025: import org.jasig.portal.ChannelRegistryStoreFactory;
026: import org.jasig.portal.layout.IUserLayoutStore;
027: import org.jasig.portal.layout.UserLayoutStoreFactory;
028: import org.jasig.portal.layout.alm.IAggregatedUserLayoutStore;
029: import org.jasig.portal.layout.restrictions.UserLayoutRestrictionFactory;
030: import org.jasig.portal.EntityIdentifier;
031: import org.jasig.portal.PortalException;
032: import org.jasig.portal.RDBMServices;
033: import org.jasig.portal.groups.IEntityGroup;
034: import org.jasig.portal.groups.IGroupConstants;
035: import org.jasig.portal.services.GroupService;
036: import org.jasig.portal.utils.SAX2FilterImpl;
037: import org.xml.sax.Attributes;
038: import org.xml.sax.ContentHandler;
039: import org.xml.sax.InputSource;
040: import org.xml.sax.SAXException;
041: import org.xml.sax.XMLReader;
042: import org.xml.sax.helpers.AttributesImpl;
043: import org.xml.sax.helpers.XMLReaderFactory;
044:
045: /**
046: * A utility class to load pushed fragment configuration into the database
047: * used by the pushfragment ant target.
048: *
049: * Prior to uPortal 2.5, this class existed in the package org.jasig.portal.layout.utils.
050: * It was moved to its present package to reflect that it is primarily a tool with
051: * a main() method intended to be used outside the context of a running uPortal instance
052: * and to reflect that this functionality is in support of Aggregated Layouts.
053: *
054: *
055: * @author Peter Kharchenko {@link <a href="mailto:pkharchenko@interactivebusiness.com"">pkharchenko@interactivebusiness.com"</a>}
056: * @author <a href="mailto:mvi@immagic.com">Michael Ivanov</a>
057: * @version 1.0 $Revision: 42363 $ $Date: 2007-08-20 13:13:23 -0700 (Mon, 20 Aug 2007) $
058: * @since uPortal 2.5
059: */
060: public class FragmentLoader {
061:
062: static final String configXSL = "/properties/al/FragmentLoader.xsl";
063:
064: public static void main(String[] args) throws Exception {
065: RDBMServices.setGetDatasourceFromJndi(false); /*don't try jndi when not in web app */
066: IUserLayoutStore layoutStoreImpl = UserLayoutStoreFactory
067: .getUserLayoutStoreImpl();
068: if (layoutStoreImpl != null
069: && !(layoutStoreImpl instanceof IAggregatedUserLayoutStore)) {
070: System.out.println("Configured UserLayoutStore is: ");
071: System.out.println(" "
072: + layoutStoreImpl.getClass().getName());
073: System.out.println("This step requires use of:");
074: System.out.println(" "
075: + IAggregatedUserLayoutStore.class.getName());
076: System.out.println();
077: System.out.println(">>>> Skipping this step.");
078: System.exit(0);
079: }
080: String alConfigFile = args[0];
081: String outputDataFile = args[1];
082:
083: HashMap rNames = new HashMap();
084: rNames.putAll(UserLayoutRestrictionFactory
085: .getAvailableRestrictions());
086:
087: // instantiate transfomer
088: SAXTransformerFactory saxTFactory = (SAXTransformerFactory) TransformerFactory
089: .newInstance();
090:
091: System.out.println("DEBUG: reading XSLT from url="
092: + FragmentLoader.class.getResource(configXSL));
093:
094: XMLReader reader = XMLReaderFactory.createXMLReader();
095: // for some weird weird reason, the following way of instantiating the parser causes all elements to dissapear ...
096: // nothing like a bizzare bug like that to take up your afternoon :(
097: // XMLReader reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
098: TransformerHandler thand = saxTFactory
099: .newTransformerHandler(new StreamSource(
100: FragmentLoader.class
101: .getResourceAsStream(configXSL)));
102:
103: // instantiate filter
104: ConfigFilter filter = new ConfigFilter(thand, rNames);
105:
106: reader.setContentHandler(filter);
107: thand.setResult(new StreamResult(outputDataFile));
108: boolean isException = false;
109: try {
110: reader.parse(new InputSource(FragmentLoader.class
111: .getResourceAsStream(alConfigFile)));
112: } catch (Exception e) {
113: isException = true;
114: System.out.println("The pushed fragment file \""
115: + alConfigFile + "\" caused the exception: ");
116: e.printStackTrace();
117: }
118:
119: if (!isException) {
120: // Cleaning the database before the DbLoader is called
121: DbCleaner.cleanTables(filter.getFragmentIds());
122: System.out.println("DEBUG: done");
123: System.exit(0);
124: }
125: System.exit(1);
126: }
127:
128: /**
129: * Cleans up the tables contained the old data of the fragments to be reloaded
130: * It is used before the DbLoader utility is called
131: *
132: */
133: private static class DbCleaner {
134:
135: public static void cleanTables(Map fragmentIds) {
136:
137: RDBMServices.setGetDatasourceFromJndi(false);
138: Connection con = RDBMServices.getConnection();
139:
140: if (fragmentIds != null && !fragmentIds.isEmpty()) {
141:
142: try {
143:
144: PreparedStatement fragmentIdstmt = con
145: .prepareStatement("SELECT FRAGMENT_ID FROM UP_OWNER_FRAGMENT WHERE FRAGMENT_NAME = ?");
146: Map oldFragmentIds = new HashMap();
147: for (Iterator i = fragmentIds.keySet().iterator(); i
148: .hasNext();) {
149: String name = (String) i.next();
150: fragmentIdstmt.setString(1, name);
151: ResultSet rs = fragmentIdstmt.executeQuery();
152: if (rs.next())
153: oldFragmentIds.put(rs.getString(1), name);
154: if (rs != null)
155: rs.close();
156: }
157: if (fragmentIdstmt != null)
158: fragmentIdstmt.close();
159:
160: if (oldFragmentIds.size() > 0) {
161:
162: System.out.println("DEBUG: cleaning tables...");
163:
164: con.setAutoCommit(false);
165:
166: //PreparedStatement deleteLayoutStruct = con.prepareStatement("DELETE FROM UP_LAYOUT_STRUCT_AGGR WHERE FRAGMENT_ID = ?");
167: PreparedStatement updateLayoutStruct = con
168: .prepareStatement("UPDATE UP_LAYOUT_STRUCT_AGGR SET FRAGMENT_ID = ? WHERE FRAGMENT_ID = ?");
169: PreparedStatement deleteFragments = con
170: .prepareStatement("DELETE FROM UP_FRAGMENTS WHERE FRAGMENT_ID = ?");
171: PreparedStatement deleteFragmentRestrictions = con
172: .prepareStatement("DELETE FROM UP_FRAGMENT_RESTRICTIONS WHERE FRAGMENT_ID = ?");
173: PreparedStatement deleteFragmentParams = con
174: .prepareStatement("DELETE FROM UP_FRAGMENT_PARAM WHERE FRAGMENT_ID = ?");
175: PreparedStatement deleteOwnerFragment = con
176: .prepareStatement("DELETE FROM UP_OWNER_FRAGMENT WHERE FRAGMENT_ID = ?");
177: PreparedStatement deleteGroupFragment = con
178: .prepareStatement("DELETE FROM UP_GROUP_FRAGMENT WHERE FRAGMENT_ID = ?");
179:
180: try {
181: for (Iterator i = oldFragmentIds.keySet()
182: .iterator(); i.hasNext();) {
183: String oldId = (String) i.next();
184: int fragmentId = Integer
185: .parseInt(oldId);
186: // Setting the parameter - fragment id
187: String newId = (String) fragmentIds
188: .get(oldFragmentIds.get(oldId));
189: updateLayoutStruct.setInt(1, Integer
190: .parseInt(newId));
191: updateLayoutStruct
192: .setInt(2, fragmentId);
193: deleteFragments.setInt(1, fragmentId);
194: deleteFragmentRestrictions.setInt(1,
195: fragmentId);
196: deleteFragmentParams.setInt(1,
197: fragmentId);
198: deleteOwnerFragment.setInt(1,
199: fragmentId);
200: deleteGroupFragment.setInt(1,
201: fragmentId);
202:
203: // Executing statements
204: updateLayoutStruct.executeUpdate();
205: deleteFragments.executeUpdate();
206: deleteFragmentRestrictions
207: .executeUpdate();
208: deleteFragmentParams.executeUpdate();
209: deleteOwnerFragment.executeUpdate();
210: deleteGroupFragment.executeUpdate();
211: }
212: // Commit
213: con.commit();
214: } catch (Exception sqle) {
215: con.rollback();
216: System.out.println("DEBUG: "
217: + sqle.getMessage());
218: sqle.printStackTrace();
219: }
220:
221: if (updateLayoutStruct != null)
222: updateLayoutStruct.close();
223: if (deleteFragments != null)
224: deleteFragments.close();
225: if (deleteFragmentRestrictions != null)
226: deleteFragmentRestrictions.close();
227: if (deleteFragmentParams != null)
228: deleteFragmentParams.close();
229: if (deleteOwnerFragment != null)
230: deleteOwnerFragment.close();
231: if (deleteGroupFragment != null)
232: deleteGroupFragment.close();
233:
234: if (con != null)
235: con.close();
236:
237: System.out.println("DEBUG: cleaning done...");
238: } // if end
239: } catch (Exception e) {
240: System.out.println("DEBUG: " + e.getMessage());
241: e.printStackTrace();
242: }
243: } // if end
244: }
245:
246: }
247:
248: /**
249: * Attempts to determine group key based on a group name.
250: * If the group key can not be determined in a unique way, the method will terminate!
251: *
252: * @param groupName a <code>String</code> value
253: * @return a group key
254: */
255: static String getGroupKey(String groupName) throws Exception {
256: EntityIdentifier[] mg = GroupService.searchForGroups(groupName,
257: IGroupConstants.IS, Class
258: .forName("org.jasig.portal.security.IPerson"));
259: if (mg != null && mg.length > 0) {
260: if (mg.length > 1) {
261: // multiple matches
262: System.out
263: .println("ERROR: group name \""
264: + groupName
265: + "\" matches several existing groups: [Key\tName\tDescription]");
266: for (int i = 0; i < mg.length; i++) {
267: IEntityGroup g = GroupService.findGroup(mg[i]
268: .getKey());
269: System.out
270: .print("\t\"" + g.getKey() + "\"\t"
271: + g.getName() + "\"\t"
272: + g.getDescription());
273: }
274: System.out
275: .println("Please modify config file to specify group key directly (i.e. <group key=\"keyValue\">...)");
276: System.exit(1);
277: } else {
278: System.out.println("DEBUG: group \"" + groupName
279: + "\", key=\"" + mg[0].getKey() + "\"");
280: return mg[0].getKey();
281: }
282: } else {
283: // didnt' match
284: System.out
285: .println("ERROR: can not find user group with name \""
286: + groupName + "\" in the database !");
287: // try nonexact match
288: EntityIdentifier[] mg2 = GroupService.searchForGroups(
289: groupName, IGroupConstants.CONTAINS,
290: Class.forName("org.jasig.portal.security.IPerson"));
291: if (mg2 != null && mg2.length > 0) {
292: System.out.print("Possible matches are: [");
293: for (int i = 0; i < mg2.length; i++) {
294: IEntityGroup g = GroupService.findGroup(mg2[i]
295: .getKey());
296: System.out.print("\"" + g.getName() + "\" ");
297: }
298: System.out.println("]");
299: }
300: throw new PortalException(
301: "ERROR: can not find user group with name \""
302: + groupName + "\" in the database !");
303: }
304: return null;
305: }
306:
307: /**
308: * A filter that will perform the following functions:
309: * - intercept and verify restriction names, writing out ids
310: * - intercept and verify user group names, writing out ids
311: *
312: */
313: private static class ConfigFilter extends SAX2FilterImpl {
314: private Map rMap;
315: private boolean groupMode = false;
316: private AttributesImpl groupAtts;
317: private String groupLocalName;
318: private String groupUri;
319: private String groupData = null;
320: private Map fragmentIds;
321: private static IAggregatedUserLayoutStore layoutStore = null;
322: private static IChannelRegistryStore channelStore = null;
323: private static String adminId = null;
324:
325: public ConfigFilter(ContentHandler ch, Map rMap)
326: throws PortalException {
327: super (ch);
328: this .rMap = rMap;
329: fragmentIds = new HashMap();
330: if (layoutStore == null) {
331: IUserLayoutStore layoutStoreImpl = UserLayoutStoreFactory
332: .getUserLayoutStoreImpl();
333: if (layoutStoreImpl == null
334: || !(layoutStoreImpl instanceof IAggregatedUserLayoutStore))
335: throw new PortalException(
336: "The user layout store is NULL or must implement IAggregatedUserLayoutStore!");
337: layoutStore = (IAggregatedUserLayoutStore) layoutStoreImpl;
338: }
339: if (channelStore == null)
340: channelStore = ChannelRegistryStoreFactory
341: .getChannelRegistryStoreImpl();
342: }
343:
344: private String getAdminId() throws Exception {
345: if (adminId == null) {
346: Connection con = RDBMServices.getConnection();
347: ResultSet rs = con
348: .createStatement()
349: .executeQuery(
350: "SELECT USER_ID FROM UP_USER WHERE USER_NAME='admin'");
351: if (rs.next())
352: adminId = rs.getString(1);
353: rs.close();
354: con.close();
355: }
356: return adminId;
357: }
358:
359: public Set getFragmentNames() {
360: return fragmentIds.keySet();
361: }
362:
363: public Map getFragmentIds() {
364: return fragmentIds;
365: }
366:
367: public void characters(char ch[], int start, int length)
368: throws SAXException {
369: if (groupMode) {
370: // accumulate character data
371: String ds = new String(ch, start, length);
372: if (groupData == null) {
373: groupData = ds;
374: } else {
375: groupData = groupData + ds;
376: }
377: } else {
378: super .characters(ch, start, length);
379: }
380: }
381:
382: public void startElement(String uri, String localName,
383: String qName, Attributes atts) throws SAXException {
384:
385: AttributesImpl ai = new AttributesImpl(atts);
386:
387: // Adding the fragment name to the vector
388: if (qName.equals("fragment")) {
389: String name = atts.getValue("name");
390: try {
391: String id = layoutStore.getNextFragmentId();
392: if (!fragmentIds.containsKey(name))
393: fragmentIds.put(name, id);
394: ai.addAttribute(uri, "id", "id", "CDATA", id);
395: ai.addAttribute(uri, "owner", "owner", "CDATA",
396: getAdminId());
397: } catch (Exception pe) {
398: throw new SAXException(pe);
399: }
400: } // Getting the channel ID by the fname
401: else if (qName.equals("channel")) {
402: String fname = atts.getValue("fname");
403: ChannelDefinition chanDef;
404: try {
405: chanDef = channelStore.getChannelDefinition(fname);
406: } catch (Exception e) {
407: // We have to catch Exception because getChannelDefinition() throws Exception.
408: throw new SAXException(e);
409: }
410: if (chanDef == null) {
411: throw new SAXException(
412: "Unable to find definition for channel fname: "
413: + fname);
414: }
415: ai.addAttribute(uri, "id", "id", "CDATA", chanDef
416: .getId()
417: + "");
418: }
419:
420: if (qName.equals("group")) { // this could be made more robust by adding another mode for "groups" element
421: groupMode = true;
422: groupUri = uri;
423: groupLocalName = localName;
424: groupAtts = new AttributesImpl(atts);
425:
426: } else if (qName.equals("restriction")) { // this can also be made more robust by adding another mode for "restrictions" element
427: // look up restriction name in the DB
428: if (ai.getIndex("type") != -1) {
429: // restriction type was specified
430: if (ai.getIndex("name") != -1
431: && rMap.containsKey(ai.getValue("name"))
432: && (!ai.getValue("type").equals(
433: ai.getValue("name")))) {
434: System.out
435: .println("ERROR: specified restriction type \""
436: + ai.getValue("type")
437: + "\" does not match the specified name \""
438: + ai.getValue("name")
439: + "\" in the database. name \""
440: + ai.getValue("name")
441: + "\" matches restriction type \""
442: + (String) rMap.get(ai
443: .getValue("name"))
444: + "\"");
445: System.exit(1);
446: } else {
447: super .startElement(uri, localName, qName, ai);
448: }
449: } else {
450:
451: // Check priority of fragment to see if valid
452: String priority = ai.getValue("value");
453: String restrName = ai.getValue("name");
454:
455: if (restrName.equals("priority")) {
456: if (priority.equals("")) {
457: System.out
458: .println("ERROR: Invalid priority. Priority is empty.");
459: System.exit(1);
460: }
461: StringTokenizer st = new StringTokenizer(
462: priority, "-");
463:
464: if (st.countTokens() == 2) {
465: String minPriority = st.nextToken();
466: String maxPriority = st.nextToken();
467: if (maxPriority.compareTo(minPriority) < 0) {
468: System.out
469: .println("ERROR: Invalid priority ["
470: + priority
471: + "]. Check priorities to ensure they are valid.");
472: System.exit(1);
473: }
474: } else {
475: System.out
476: .println("ERROR: Invalid priority ["
477: + priority
478: + "]. Check priorities to ensure they are valid.");
479: System.exit(1);
480: }
481: }
482:
483: if (restrName != null) {
484: ai.addAttribute(uri, "type", "type", "CDATA",
485: restrName);
486: } else {
487: System.out
488: .println("ERROR: config file specifies a restriction name \""
489: + restrName
490: + "\" which is not registered with the database!");
491: System.exit(1);
492: }
493: super .startElement(uri, localName, qName, ai);
494: }
495: } else {
496: super .startElement(uri, localName, qName, ai);
497: }
498:
499: }
500:
501: public void endElement(String uri, String localName,
502: String qName) throws SAXException {
503:
504: if (groupMode) {
505: if (qName.equals("group")) {
506: if (groupAtts.getIndex("key") == -1) {
507: if (groupData != null) {
508: String groupKey = null;
509: try {
510: groupKey = getGroupKey(groupData);
511: } catch (Exception e) {
512: System.out
513: .println("ERROR: encountered exception while trying to determine group key for a group name \""
514: + groupData + "\"");
515: e.printStackTrace();
516: System.exit(1);
517: }
518: groupAtts.addAttribute(groupUri, "key",
519: "key", "CDATA", groupKey);
520: // output group element
521: super .startElement(groupUri,
522: groupLocalName, "group", groupAtts);
523: super .characters(groupData.toCharArray(),
524: 0, groupData.length());
525: super .endElement(groupUri, groupLocalName,
526: "group");
527: } else {
528: System.out
529: .println("ERROR: one of the group elements is empty and no group key has been specified !");
530: System.exit(1);
531: }
532: } else {
533: // check specified group key
534: try {
535: IEntityGroup g = GroupService
536: .findGroup(groupAtts
537: .getValue("key"));
538: if (g != null) {
539: if (groupData != null) {
540: if (g.getName().equals(groupData)) {
541: System.out
542: .println("DEBUG: group key=\""
543: + groupAtts
544: .getValue("key")
545: + "\" checked out with the name \""
546: + groupData
547: + "\".");
548: // output group element
549: super .startElement(groupUri,
550: groupLocalName,
551: "group", groupAtts);
552: if (groupData != null) {
553: super .characters(groupData
554: .toCharArray(), 0,
555: groupData.length());
556: }
557: super
558: .endElement(groupUri,
559: groupLocalName,
560: "group");
561: } else {
562: System.out
563: .println("ERROR: group key \""
564: + groupAtts
565: .getValue("key")
566: + "\" belongs to a group with a name \""
567: + g.getName()
568: + "\", where the name specified by the config file is \""
569: + groupData
570: + "\". Please fix the config file.");
571: System.exit(1);
572: }
573: }
574: } else {
575: System.out
576: .println("ERROR: unable to find a group with a key \""
577: + groupAtts
578: .getValue("key")
579: + "\"! Either correct the key, or consider matching the group by name.");
580: System.exit(1);
581: }
582: } catch (Exception e) {
583: System.out
584: .println("ERROR: exception raised while trying to look up group by a key=\""
585: + groupAtts.getValue("key")
586: + "\"!");
587: e.printStackTrace();
588: System.exit(1);
589: }
590: }
591: } else {
592: System.out
593: .println("WARNING: <group/> contains other elements, which it shouldn't! Please check config validity.");
594: }
595: groupMode = false;
596: groupData = null;
597: groupAtts = null;
598: groupUri = null;
599: groupLocalName = null;
600: } else {
601: super.endElement(uri, localName, qName);
602: }
603: }
604: }
605: }
|