0001: package org.drools.repository;
0002:
0003: import java.io.ByteArrayInputStream;
0004: import java.io.ByteArrayOutputStream;
0005: import java.io.IOException;
0006: import java.util.ArrayList;
0007: import java.util.Calendar;
0008: import java.util.Iterator;
0009: import java.util.List;
0010: import java.util.StringTokenizer;
0011: import java.util.zip.ZipEntry;
0012: import java.util.zip.ZipOutputStream;
0013:
0014: import javax.jcr.ImportUUIDBehavior;
0015: import javax.jcr.ItemExistsException;
0016: import javax.jcr.Node;
0017: import javax.jcr.NodeIterator;
0018: import javax.jcr.PathNotFoundException;
0019: import javax.jcr.Property;
0020: import javax.jcr.PropertyIterator;
0021: import javax.jcr.RepositoryException;
0022: import javax.jcr.Session;
0023: import javax.jcr.ValueFormatException;
0024: import javax.jcr.lock.LockException;
0025: import javax.jcr.nodetype.ConstraintViolationException;
0026: import javax.jcr.query.Query;
0027: import javax.jcr.query.QueryResult;
0028: import javax.jcr.version.Version;
0029: import javax.jcr.version.VersionException;
0030:
0031: import org.apache.log4j.Logger;
0032:
0033: /**
0034: * RulesRepository is the class that defines the bahavior for the JBoss Rules
0035: * (drools) rule repository based upon the JCR specification (JSR-170).
0036: * <p>
0037: * An instance of this class is capable of storing rules used by the JBoss Rule
0038: * engine. It also provides a versioning capability for rules. Rules can be
0039: * imported from specified files. The RulesRepository is also capable of storing
0040: * DSL content. Rules can be explicitly tied to a particular DSL node within the
0041: * repository, and this reference can either follow the head version, or a
0042: * specific version of the DSL node.
0043: * <p>
0044: * Rules can be tagged. Tags are stored in a separate area of the repository,
0045: * and can be added on demand. Rules can have 0 or more tags. Tags are intended
0046: * to help provide a means for searching for specific types of rules quickly,
0047: * even when they aren't all part of the same rulepackage.
0048: * <p>
0049: * Rules can be associated with 0 or 1 states. States are created in a seperate
0050: * area of the repository. States are intended to help track the progress of a
0051: * rule as it traverses its life- cycle. (e.g. draft, approved, deprecated,
0052: * etc.)
0053: * <p>
0054: * The RulesRepository provides versioning of rules, rule packages, and DSLs.
0055: * This versioning works in a strictly linear fashion, with one version having
0056: * at most 1 predecessor version (or none, if it is the first version), and at
0057: * most 1 successor version (or none, if it is the most recently checked-in
0058: * version). The JCR specification supports a more complicated versioning
0059: * system, and if there is sufficient demand, we can modify our versioning
0060: * scheme to be better aligned with JCR's versioning abilities.
0061: *
0062: * @author Ben Truitt
0063: * @author Fernando Meyer
0064: * @author Michael Neale
0065: */
0066: public class RulesRepository {
0067:
0068: public static final String DEFAULT_PACKAGE = "defaultPackage";
0069:
0070: public static final String DROOLS_URI = "http://www.jboss.org/drools-repository/1.0";
0071:
0072: private static final Logger log = Logger
0073: .getLogger(RulesRepository.class);
0074:
0075: /**
0076: * The name of the rulepackage area of the repository
0077: */
0078: public final static String RULE_PACKAGE_AREA = "drools:package_area";
0079:
0080: /**
0081: * The name of the rulepackage area of the repository
0082: */
0083: public final static String PACKAGE_SNAPSHOT_AREA = "drools:packagesnapshot_area";
0084:
0085: /**
0086: * The name of the tag area of the repository
0087: */
0088: public final static String TAG_AREA = "drools:tag_area";
0089:
0090: /**
0091: * The name of the state area of the repository
0092: */
0093: public final static String STATE_AREA = "drools:state_area";
0094:
0095: /**
0096: * The name of the rules repository within the JCR repository
0097: */
0098: public final static String RULES_REPOSITORY_NAME = "drools:repository";
0099:
0100: private Session session;
0101:
0102: /**
0103: * This requires a JCR session be setup, and the repository be configured.
0104: */
0105: public RulesRepository(Session session) {
0106: this .session = session;
0107: }
0108:
0109: /**
0110: * Will add a node named 'nodeName' of type 'type' to 'parent' if such a
0111: * node does not already exist.
0112: *
0113: * @param parent
0114: * the parent node to add the new node to
0115: * @param nodeName
0116: * the name of the new node
0117: * @param type
0118: * the type of the new node
0119: * @return a reference to the Node object that is created by the addition,
0120: * or, if the node already existed, a reference to the pre-existant
0121: * node.
0122: * @throws RulesRepositoryException
0123: */
0124: protected static Node addNodeIfNew(Node parent, String nodeName,
0125: String type) throws RulesRepositoryException {
0126: Node node;
0127: try {
0128: node = parent.getNode(nodeName);
0129: } catch (PathNotFoundException e) {
0130: // it doesn't exist yet, so create it
0131: try {
0132: log.debug(new StringBuilder().append(
0133: "Adding new node of type: ").append(type)
0134: .append(" named: ").append(nodeName).append(
0135: " to parent node named ").append(
0136: parent.getName()));
0137:
0138: node = parent.addNode(nodeName, type);
0139: } catch (Exception e1) {
0140: log.error("Caught Exception", e);
0141: throw new RulesRepositoryException(e1);
0142: }
0143: } catch (Exception e) {
0144: log.error("Caught Exception", e);
0145: throw new RulesRepositoryException(e);
0146: }
0147: return node;
0148: }
0149:
0150: /**
0151: * Explicitly logout of the underlying JCR repository.
0152: */
0153: public void logout() {
0154: this .session.logout();
0155: }
0156:
0157: private Node getAreaNode(String areaName)
0158: throws RulesRepositoryException {
0159: Node folderNode = null;
0160: int tries = 0;
0161: while (folderNode == null && tries < 2) {
0162: try {
0163: tries++;
0164: folderNode = this .session.getRootNode().getNode(
0165: RULES_REPOSITORY_NAME + "/" + areaName);
0166: } catch (PathNotFoundException e) {
0167: if (tries == 1) {
0168: // hmm..repository must have gotten screwed up. set it
0169: // up again
0170: log
0171: .warn("The repository appears to have become corrupted.");
0172: throw new RulesRepositoryException(
0173: "Unable to get the main rule repo node. Repository is not setup correctly.",
0174: e);
0175: } else {
0176: log
0177: .error("Unable to correct repository corruption");
0178: }
0179: } catch (Exception e) {
0180: log.error("Caught Exception", e);
0181: throw new RulesRepositoryException("Caught exception "
0182: + e.getClass().getName(), e);
0183: }
0184: }
0185: if (folderNode == null) {
0186: String message = "Could not get a reference to a node for "
0187: + RULES_REPOSITORY_NAME + "/" + areaName;
0188: log.error(message);
0189: throw new RulesRepositoryException(message);
0190: }
0191: return folderNode;
0192: }
0193:
0194: // MN: This is kept for future reference showing how to tie references
0195: // to a specific version when
0196: // sharing assets.
0197: //
0198: // /**
0199: // * Adds a Rule node in the repository using the content specified,
0200: // associating it with
0201: // * the specified DSL node
0202: // *
0203: // * @param ruleName the name of the rule
0204: // * @param lhsContent the lhs of the rule
0205: // * @param rhsContent the rhs of the rule
0206: // * @param dslItem the dslItem encapsulting the dsl node to associate
0207: // this rule node with
0208: // * @paaram followDslHead whether or not to follow the head revision of
0209: // the dsl node
0210: // * @return a RuleItem object encapsulating the node that gets added
0211: // * @throws RulesRepositoryException
0212: // */
0213: // public RuleItem addRule(String ruleName, String ruleContent, DslItem
0214: // dslItem, boolean followDslHead) throws RulesRepositoryException {
0215: // Node folderNode = this.getAreaNode(RULE_AREA);
0216: //
0217: // try {
0218: // //create the node - see section 6.7.22.6 of the spec
0219: // Node ruleNode = folderNode.addNode(ruleName,
0220: // RuleItem.RULE_NODE_TYPE_NAME);
0221: //
0222: // ruleNode.setProperty(RuleItem.TITLE_PROPERTY_NAME, ruleName);
0223: // ruleNode.setProperty(RuleItem.RULE_CONTENT_PROPERTY_NAME,
0224: // ruleContent);
0225: // ruleNode.setProperty(RuleItem.DESCRIPTION_PROPERTY_NAME, "");
0226: // ruleNode.setProperty(RuleItem.FORMAT_PROPERTY_NAME,
0227: // RuleItem.RULE_FORMAT);
0228: //
0229: //
0230: // if(followDslHead) {
0231: // ruleNode.setProperty(RuleItem.DSL_PROPERTY_NAME, dslItem.getNode());
0232: // }
0233: // else {
0234: // //tie the ruleNode to specifically the current version of the dslNode
0235: // ruleNode.setProperty(RuleItem.DSL_PROPERTY_NAME,
0236: // dslItem.getNode().getBaseVersion());
0237: // }
0238: //
0239: // Calendar lastModified = Calendar.getInstance();
0240: // ruleNode.setProperty(RuleItem.LAST_MODIFIED_PROPERTY_NAME,
0241: // lastModified);
0242: //
0243: // session.save();
0244: //
0245: // try {
0246: // ruleNode.checkin();
0247: // }
0248: // catch(UnsupportedRepositoryOperationException e) {
0249: // String message = "Error: Caught
0250: // UnsupportedRepositoryOperationException when attempting to checkin
0251: // rule: " + ruleNode.getName() + ". Are you sure your JCR repository
0252: // supports versioning? ";
0253: // log.error(message + e);
0254: // throw new RulesRepositoryException(message, e);
0255: // }
0256: //
0257: // return new RuleItem(this, ruleNode);
0258: // }
0259: // catch(Exception e) {
0260: // log.error("Caught Exception", e);
0261: // throw new RulesRepositoryException(e);
0262: // }
0263: // }
0264: //
0265: //
0266: // /**
0267: // * Adds a Rule node in the repository using the content specified
0268: // *
0269: // * @param ruleName the name of the rule
0270: // * @param lhsContent the lhs of the rule
0271: // * @param rhsContent the rhs of the rule
0272: // * @return a RuleItem object encapsulating the node that gets added
0273: // * @throws RulesRepositoryException
0274: // */
0275: // public RuleItem addRule(String ruleName, String ruleContent) throws
0276: // RulesRepositoryException {
0277: // Node folderNode = this.getAreaNode(RULE_AREA);
0278: //
0279: // try {
0280: // //create the node - see section 6.7.22.6 of the spec
0281: // Node ruleNode = folderNode.addNode(ruleName,
0282: // RuleItem.RULE_NODE_TYPE_NAME);
0283: //
0284: // ruleNode.setProperty(RuleItem.TITLE_PROPERTY_NAME, ruleName);
0285: //
0286: //
0287: // ruleNode.setProperty(RuleItem.DESCRIPTION_PROPERTY_NAME, "");
0288: // ruleNode.setProperty(RuleItem.FORMAT_PROPERTY_NAME,
0289: // RuleItem.RULE_FORMAT);
0290: // ruleNode.setProperty(RuleItem.RULE_CONTENT_PROPERTY_NAME,
0291: // ruleContent);
0292: //
0293: // ruleNode.setProperty( VersionableItem.CHECKIN_COMMENT, "Initial" );
0294: //
0295: //
0296: // Calendar lastModified = Calendar.getInstance();
0297: // ruleNode.setProperty(RuleItem.LAST_MODIFIED_PROPERTY_NAME,
0298: // lastModified);
0299: //
0300: // session.save();
0301: //
0302: // try {
0303: // ruleNode.checkin();
0304: // }
0305: // catch(UnsupportedRepositoryOperationException e) {
0306: // String message = "Error: Caught
0307: // UnsupportedRepositoryOperationException when attempting to checkin
0308: // rule: " + ruleNode.getName() + ". Are you sure your JCR repository
0309: // supports versioning? ";
0310: // log.error(message + e);
0311: // throw new RulesRepositoryException(message, e);
0312: // }
0313: //
0314: // return new RuleItem(this, ruleNode);
0315: // }
0316: // catch(Exception e) {
0317: // log.error("Caught Exception", e);
0318: // throw new RulesRepositoryException(e);
0319: // }
0320: // }
0321:
0322: /**
0323: * This will copy an assets content to the new location.
0324: * @return the UUID of the new asset.
0325: */
0326: public String copyAsset(String uuidSource,
0327: String destinationPackage, String destinationName) {
0328: try {
0329: AssetItem source = loadAssetByUUID(uuidSource);
0330: String sourcePath = source.getNode().getPath();
0331:
0332: String destPath = this .getAreaNode(RULE_PACKAGE_AREA)
0333: .getPath()
0334: + "/"
0335: + destinationPackage
0336: + "/"
0337: + PackageItem.ASSET_FOLDER_NAME
0338: + "/"
0339: + destinationName;
0340: this .session.getWorkspace().copy(sourcePath, destPath);
0341: AssetItem dest = loadPackage(destinationPackage).loadAsset(
0342: destinationName);
0343: // if (dest.getContent() != null ) {
0344: // dest.updateContent( dest.getContent().replaceAll( source.getName(), dest.getName() ) );
0345: // }
0346:
0347: dest.updateStringProperty(destinationPackage,
0348: AssetItem.PACKAGE_NAME_PROPERTY);
0349: dest.node.setProperty(
0350: AssetItem.VERSION_NUMBER_PROPERTY_NAME, 0);
0351: dest.checkin("Copied from " + source.getPackageName() + "/"
0352: + source.getName());
0353: return dest.getUUID();
0354: } catch (RepositoryException e) {
0355: log.error("Unable to copy asset.", e);
0356: throw new RulesRepositoryException(e);
0357: }
0358: }
0359:
0360: /**
0361: * Loads a RulePackage for the specified package name. Will throw an
0362: * exception if the specified rule package does not exist.
0363: *
0364: * @param name
0365: * the name of the package to load
0366: * @return a RulePackageItem object
0367: */
0368: public PackageItem loadPackage(String name)
0369: throws RulesRepositoryException {
0370: try {
0371: Node folderNode = this .getAreaNode(RULE_PACKAGE_AREA);
0372: Node rulePackageNode = folderNode.getNode(name);
0373:
0374: return new PackageItem(this , rulePackageNode);
0375: } catch (RepositoryException e) {
0376: log.error("Unable to load a rule package. ", e);
0377:
0378: throw new RulesRepositoryException(
0379: "Unable to load a rule package. ", e);
0380:
0381: }
0382: }
0383:
0384: /**
0385: * This returns true if the repository contains the specified package name.
0386: */
0387: public boolean containsPackage(String name) {
0388: Node folderNode = this .getAreaNode(RULE_PACKAGE_AREA);
0389: try {
0390: return folderNode.hasNode(name);
0391: } catch (RepositoryException e) {
0392: throw new RulesRepositoryException(e);
0393: }
0394: }
0395:
0396: public PackageItem loadPackageSnapshot(String packageName,
0397: String snapshotName) {
0398: try {
0399: Node n = this .getAreaNode(PACKAGE_SNAPSHOT_AREA).getNode(
0400: packageName).getNode(snapshotName);
0401: return new PackageItem(this , n);
0402: } catch (RepositoryException e) {
0403: log.error(e);
0404: throw new RulesRepositoryException(e);
0405: }
0406: }
0407:
0408: /**
0409: * This will copy the package to the snapshot area. Creating a copy for
0410: * deployment, etc.
0411: */
0412: public void createPackageSnapshot(String packageName,
0413: String snapshotName) {
0414: log.debug("Creating snapshot for [" + packageName
0415: + "] called [" + snapshotName + "]");
0416: try {
0417: Node snaps = this .getAreaNode(PACKAGE_SNAPSHOT_AREA);
0418:
0419: if (!snaps.hasNode(packageName)) {
0420: snaps.addNode(packageName, "nt:folder");
0421: save();
0422: }
0423:
0424: Node pkgSnaps = snaps.getNode(packageName);
0425:
0426: String newName = pkgSnaps.getPath() + "/" + snapshotName;
0427:
0428: Node folderNode = this .getAreaNode(RULE_PACKAGE_AREA);
0429: Node rulePackageNode = folderNode.getNode(packageName);
0430:
0431: String source = rulePackageNode.getPath();
0432:
0433: this .session.getWorkspace().copy(source, newName);
0434:
0435: } catch (RepositoryException e) {
0436: log.error("Unable to create snapshot", e);
0437: throw new RulesRepositoryException(e);
0438: }
0439: }
0440:
0441: /**
0442: * This will remove the specified snapshot.
0443: */
0444: public void removePackageSnapshot(String packageName,
0445: String snapshotName) {
0446: log.debug("Removing snapshot for [" + packageName
0447: + "] called [" + snapshotName + "]");
0448: try {
0449: Node snaps = this .getAreaNode(PACKAGE_SNAPSHOT_AREA);
0450:
0451: if (!snaps.hasNode(packageName)) {
0452: throw new RulesRepositoryException("The package "
0453: + packageName + " does not have any snapshots.");
0454: }
0455:
0456: Node pkgSnaps = snaps.getNode(packageName);
0457:
0458: if (pkgSnaps.hasNode(snapshotName)) {
0459: pkgSnaps.getNode(snapshotName).remove();
0460: }
0461:
0462: save();
0463: } catch (RepositoryException e) {
0464: log.error("Unable to remove snapshot", e);
0465: throw new RulesRepositoryException(e);
0466: }
0467: }
0468:
0469: /**
0470: * Copies a snapshot to the new location/label.
0471: *
0472: * @param packageName
0473: * The name of the package.
0474: * @param snapshotName
0475: * The label of the source snapshot
0476: * @param newName
0477: * The new label. The old one is left intact.
0478: */
0479: public void copyPackageSnapshot(String packageName,
0480: String snapshotName, String newName) {
0481: log.debug("Creating snapshot for [" + packageName
0482: + "] called [" + snapshotName + "]");
0483: try {
0484: Node snaps = this .getAreaNode(PACKAGE_SNAPSHOT_AREA);
0485:
0486: Node pkgSnaps = snaps.getNode(packageName);
0487:
0488: Node sourceNode = pkgSnaps.getNode(snapshotName);
0489:
0490: String destinationPath = pkgSnaps.getPath() + "/" + newName;
0491:
0492: this .session.getWorkspace().copy(sourceNode.getPath(),
0493: destinationPath);
0494: } catch (RepositoryException e) {
0495: log.error("Unable to create snapshot", e);
0496: throw new RulesRepositoryException(e);
0497: }
0498: }
0499:
0500: /**
0501: * This will return or create the default package for rules that have no
0502: * home yet.
0503: */
0504: public PackageItem loadDefaultPackage()
0505: throws RulesRepositoryException {
0506: Node folderNode = this .getAreaNode(RULE_PACKAGE_AREA);
0507: try {
0508: if (folderNode.hasNode(DEFAULT_PACKAGE)) {
0509: return loadPackage(DEFAULT_PACKAGE);
0510: } else {
0511: return createPackage(DEFAULT_PACKAGE, "");
0512: }
0513: } catch (RepositoryException e) {
0514: throw new RulesRepositoryException(e);
0515: }
0516:
0517: }
0518:
0519: /**
0520: * Similar to above. Loads a RulePackage for the specified uuid.
0521: *
0522: * @param uuid
0523: * the uuid of the package to load
0524: * @return a RulePackageItem object
0525: * @throws RulesRepositoryException
0526: */
0527: public PackageItem loadPackageByUUID(String uuid)
0528: throws RulesRepositoryException {
0529: try {
0530: Node rulePackageNode = this .session.getNodeByUUID(uuid);
0531: return new PackageItem(this , rulePackageNode);
0532: } catch (Exception e) {
0533: log.error("Unable to load a rule package by UUID. ", e);
0534: if (e instanceof RuntimeException) {
0535: throw (RuntimeException) e;
0536: } else {
0537: throw new RulesRepositoryException(
0538: "Unable to load a rule package. ", e);
0539: }
0540: }
0541: }
0542:
0543: /**
0544: * This will restore the historical version, save, and check it in as a new
0545: * version with the given comment.
0546: *
0547: * @param versionToRestore
0548: * @param headVersion
0549: * @param comment
0550: */
0551: public void restoreHistoricalAsset(AssetItem versionToRestore,
0552: AssetItem headVersion, String comment) {
0553:
0554: long oldVersionNumber = headVersion.getVersionNumber();
0555:
0556: Version v = (Version) versionToRestore.getNode();
0557: try {
0558: headVersion.getNode().restore(v, true);
0559: AssetItem newHead = loadAssetByUUID(headVersion.getUUID());
0560: newHead.checkout();
0561: newHead.getNode().setProperty(
0562: VersionableItem.VERSION_NUMBER_PROPERTY_NAME,
0563: oldVersionNumber);
0564: newHead.checkin(comment);
0565: } catch (RepositoryException e) {
0566: log.error("Unable to restore version of asset.", e);
0567: throw new RulesRepositoryException(e);
0568: }
0569: }
0570:
0571: /**
0572: * Loads a rule by its UUID (generally the fastest way to load something).
0573: */
0574: public AssetItem loadAssetByUUID(String uuid) {
0575: try {
0576: Node rulePackageNode = this .session.getNodeByUUID(uuid);
0577: return new AssetItem(this , rulePackageNode);
0578: } catch (RepositoryException e) {
0579: log.error("Unable to load a rule asset by UUID.", e);
0580: throw new RulesRepositoryException(e);
0581: }
0582:
0583: }
0584:
0585: /**
0586: * Adds a package to the repository.
0587: *
0588: * @param name
0589: * what to name the node added
0590: * @param description
0591: * what description to use for the node
0592: * @return a PackageItem, encapsulating the created node
0593: * @throws RulesRepositoryException
0594: */
0595: public PackageItem createPackage(String name, String description)
0596: throws RulesRepositoryException {
0597: Node folderNode = this .getAreaNode(RULE_PACKAGE_AREA);
0598:
0599: try {
0600: // create the node - see section 6.7.22.6 of the spec
0601: Node rulePackageNode = folderNode.addNode(name,
0602: PackageItem.RULE_PACKAGE_TYPE_NAME);
0603:
0604: rulePackageNode.addNode(PackageItem.ASSET_FOLDER_NAME,
0605: "drools:versionableAssetFolder");
0606:
0607: rulePackageNode.setProperty(
0608: PackageItem.TITLE_PROPERTY_NAME, name);
0609:
0610: rulePackageNode.setProperty(
0611: AssetItem.DESCRIPTION_PROPERTY_NAME, description);
0612: rulePackageNode.setProperty(AssetItem.FORMAT_PROPERTY_NAME,
0613: PackageItem.PACKAGE_FORMAT);
0614: rulePackageNode.setProperty(
0615: PackageItem.CREATOR_PROPERTY_NAME, this .session
0616: .getUserID());
0617:
0618: Calendar lastModified = Calendar.getInstance();
0619: rulePackageNode.setProperty(
0620: PackageItem.LAST_MODIFIED_PROPERTY_NAME,
0621: lastModified);
0622:
0623: PackageItem item = new PackageItem(this , rulePackageNode);
0624: item.checkin("Initial");
0625:
0626: return item;
0627: } catch (ItemExistsException e) {
0628: throw new RulesRepositoryException(
0629: "A package name must be unique.", e);
0630: } catch (RepositoryException e) {
0631: log.error("Error when creating a new rule package", e);
0632: throw new RulesRepositoryException(e);
0633: }
0634:
0635: }
0636:
0637: /**
0638: * Gets a StateItem for the specified state name. If a node for the
0639: * specified state does not yet exist, one is first created.
0640: *
0641: * @param name
0642: * the name of the state to get
0643: * @return a StateItem object encapsulating the retreived node
0644: * @throws RulesRepositoryException
0645: */
0646: public StateItem getState(String name)
0647: throws RulesRepositoryException {
0648: try {
0649: Node folderNode = this .getAreaNode(STATE_AREA);
0650: if (!folderNode.hasNode(name)) {
0651: throw new RulesRepositoryException("The state called ["
0652: + name + "] does not exist.");
0653: }
0654: Node stateNode = folderNode.getNode(name);// RulesRepository.addNodeIfNew(folderNode,
0655: // name,
0656: // StateItem.STATE_NODE_TYPE_NAME);
0657: return new StateItem(this , stateNode);
0658: } catch (Exception e) {
0659: log.error(e);
0660: throw new RulesRepositoryException(e);
0661: }
0662: }
0663:
0664: /**
0665: * Create a status node of the given name.
0666: */
0667: public StateItem createState(String name) {
0668: try {
0669: Node folderNode = this .getAreaNode(STATE_AREA);
0670: Node stateNode = RulesRepository.addNodeIfNew(folderNode,
0671: name, StateItem.STATE_NODE_TYPE_NAME);
0672: log.debug("Created the status [" + name + "]");
0673: return new StateItem(this , stateNode);
0674: } catch (Exception e) {
0675: log.error(e);
0676: throw new RulesRepositoryException(e);
0677: }
0678: }
0679:
0680: /**
0681: * This will return a category for the given category path.
0682: *
0683: * @param tagName
0684: * the name of the tag to get. If the tag to get is within a
0685: * heirarchy of tag nodes, specify the full path to the tag node
0686: * of interest (e.g. if you want to get back 'child-tag', use
0687: * "parent-tag/child-tag")
0688: * @return a TagItem object encapsulating the node for the tag in the
0689: * repository
0690: * @throws RulesRepositoryException
0691: */
0692: public CategoryItem loadCategory(String tagName)
0693: throws RulesRepositoryException {
0694: if (tagName == null || "".equals(tagName)) {
0695: throw new RuntimeException(
0696: "Empty category name not permitted.");
0697: }
0698:
0699: try {
0700: Node folderNode = this .getAreaNode(TAG_AREA);
0701: Node tagNode = folderNode;
0702:
0703: StringTokenizer tok = new StringTokenizer(tagName, "/");
0704: while (tok.hasMoreTokens()) {
0705: String currentTagName = tok.nextToken();
0706: tagNode = folderNode.getNode(currentTagName);
0707: // MN was this: RulesRepository.addNodeIfNew(folderNode,
0708: // currentTagName, CategoryItem.TAG_NODE_TYPE_NAME);
0709: folderNode = tagNode;
0710: }
0711:
0712: return new CategoryItem(this , tagNode);
0713: } catch (RepositoryException e) {
0714: if (e instanceof PathNotFoundException) {
0715: throw new RulesRepositoryException(
0716: "Unable to load the category : [" + tagName
0717: + "] does not exist.", e);
0718: }
0719: throw new RulesRepositoryException(e);
0720: }
0721: }
0722:
0723: /**
0724: * This will retrieve a list of RuleItem objects - that are allocated to the
0725: * provided category. Only the latest versions of each RuleItem will be
0726: * returned (you will have to delve into the rules deepest darkest history
0727: * yourself... mahahahaha).
0728: */
0729: public List findAssetsByCategory(String categoryTag,
0730: boolean seekArchivedAsset) throws RulesRepositoryException {
0731:
0732: CategoryItem item = this .loadCategory(categoryTag);
0733: List results = new ArrayList();
0734: try {
0735: PropertyIterator it = item.getNode().getReferences();
0736:
0737: while (it.hasNext()) {
0738: Property ruleLink = (Property) it.next();
0739: Node parentNode = ruleLink.getParent();
0740: if (isNotSnapshot(parentNode)
0741: && parentNode.getPrimaryNodeType().getName()
0742: .equals(AssetItem.RULE_NODE_TYPE_NAME)) {
0743: if (seekArchivedAsset
0744: || !parentNode
0745: .getProperty(
0746: AssetItem.CONTENT_PROPERTY_ARCHIVE_FLAG)
0747: .getBoolean())
0748: results.add(new AssetItem(this , parentNode));
0749: }
0750: }
0751: return results;
0752: } catch (RepositoryException e) {
0753: throw new RulesRepositoryException(e);
0754: }
0755: }
0756:
0757: /**
0758: * TODO: Comment
0759: */
0760: public List findAssetsByCategory(String categoryTag)
0761: throws RulesRepositoryException {
0762: return this .findAssetsByCategory(categoryTag, false);
0763: }
0764:
0765: /**
0766: * TODO: comment
0767: * @return
0768: * @throws IOException
0769: * @throws PathNotFoundException
0770: * @throws RepositoryException
0771: */
0772: public byte[] exportRulesRepository() throws IOException,
0773: PathNotFoundException, RepositoryException {
0774:
0775: ByteArrayOutputStream bout = new ByteArrayOutputStream();
0776: ZipOutputStream zout = new ZipOutputStream(bout);
0777:
0778: zout.putNextEntry(new ZipEntry("repository_export.xml"));
0779: zout.write(dumpRepositoryXml());
0780: zout.closeEntry();
0781: zout.finish();
0782: return bout.toByteArray();
0783: }
0784:
0785: public byte[] dumpRepositoryXml() throws PathNotFoundException,
0786: IOException, RepositoryException {
0787: ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
0788: session.refresh(false);
0789: session.exportSystemView("/" + RULES_REPOSITORY_NAME, byteOut,
0790: false, false);
0791: return byteOut.toByteArray();
0792: }
0793:
0794: /**
0795: *
0796: * @param byteArray
0797: */
0798: public void importRulesRepository(byte[] byteArray) {
0799: try {
0800: new RulesRepositoryAdministrator(this .session)
0801: .clearRulesRepository();
0802: this .session.getWorkspace().importXML("/",
0803: new ByteArrayInputStream(byteArray),
0804: ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
0805: session.save();
0806: } catch (RepositoryException e) {
0807: e.printStackTrace();
0808: throw new RulesRepositoryException();
0809: } catch (IOException e) {
0810: e.printStackTrace();
0811: }
0812: }
0813:
0814: /**
0815: *
0816: * @param parentNode
0817: * @return
0818: * @throws RepositoryException
0819: */
0820: boolean isNotSnapshot(Node parentNode) throws RepositoryException {
0821: return parentNode.getPath().indexOf(PACKAGE_SNAPSHOT_AREA) == -1;
0822: }
0823:
0824: /**
0825: * @return an Iterator which will provide RulePackageItem's. This will show
0826: * ALL the packages, only returning latest versions, by default.
0827: */
0828: public Iterator listPackages() {
0829: Node folderNode = this .getAreaNode(RULE_PACKAGE_AREA);
0830:
0831: try {
0832: if (!folderNode.hasNode(DEFAULT_PACKAGE)) {
0833: createPackage(DEFAULT_PACKAGE,
0834: "The default rule package");
0835: folderNode = this .getAreaNode(RULE_PACKAGE_AREA);
0836: }
0837: return new PackageIterator(this , folderNode.getNodes());
0838: } catch (RepositoryException e) {
0839: throw new RulesRepositoryException(e);
0840: }
0841: }
0842:
0843: /**
0844: * @return The JCR session that this repository is using.
0845: */
0846: public Session getSession() {
0847: return this .session;
0848: }
0849:
0850: /**
0851: * Save any pending changes.
0852: */
0853: public void save() {
0854: try {
0855: this .session.save();
0856: } catch (Exception e) {
0857: if (e instanceof RuntimeException) {
0858: throw (RuntimeException) e;
0859: } else {
0860: throw new RulesRepositoryException(e);
0861: }
0862: }
0863:
0864: }
0865:
0866: /**
0867: * This moves a rule asset from one package to another, preserving history
0868: * etc etc.
0869: *
0870: * @param newPackage
0871: * The destination package.
0872: * @param uuid
0873: * The UUID of the rule
0874: * @param explanation
0875: * The reason (which will be added as the checkin message).
0876: */
0877: public void moveRuleItemPackage(String newPackage, String uuid,
0878: String explanation) {
0879: try {
0880: AssetItem item = loadAssetByUUID(uuid);
0881:
0882: PackageItem destPkg = loadPackage(newPackage);
0883:
0884: String sourcePath = item.node.getPath();
0885: String destPath = destPkg.node.getPath() + "/"
0886: + PackageItem.ASSET_FOLDER_NAME + "/"
0887: + item.getName();
0888:
0889: this .session.move(sourcePath, destPath);
0890:
0891: item.checkout();
0892: item.node.setProperty(AssetItem.PACKAGE_NAME_PROPERTY,
0893: newPackage);
0894:
0895: item.checkin(explanation);
0896:
0897: } catch (RepositoryException e) {
0898: throw new RulesRepositoryException(e);
0899: }
0900:
0901: }
0902:
0903: /**
0904: * This will rename an assset and apply the change immediately.
0905: * @return the UUID of the new asset
0906: */
0907: public String renameAsset(String uuid, String newAssetName) {
0908: try {
0909: AssetItem itemOriginal = loadAssetByUUID(uuid);
0910: log.info("Renaming asset: "
0911: + itemOriginal.getNode().getPath() + " to "
0912: + newAssetName);
0913: Node node = itemOriginal.getNode();
0914: String sourcePath = node.getPath();
0915: String destPath = node.getParent().getPath() + "/"
0916: + newAssetName;
0917: this .session.move(sourcePath, destPath);
0918:
0919: itemOriginal.updateTitle(newAssetName);
0920: itemOriginal.checkin("Renamed asset "
0921: + itemOriginal.getName());
0922: return itemOriginal.getUUID();
0923: } catch (RepositoryException e) {
0924: log.error(e);
0925: throw new RulesRepositoryException(e);
0926: }
0927: }
0928:
0929: /**
0930: * This will rename a package and apply the change immediately.
0931: * @return the UUID of the package
0932: */
0933: public String renamePackage(String uuid, String newPackageName) {
0934: try {
0935: PackageItem itemOriginal = loadPackageByUUID(uuid);
0936: log.info("Renaming package: "
0937: + itemOriginal.getNode().getPath() + " to "
0938: + newPackageName);
0939: Node node = itemOriginal.getNode();
0940: String sourcePath = node.getPath();
0941: String destPath = node.getParent().getPath() + "/"
0942: + newPackageName;
0943: this .session.move(sourcePath, destPath);
0944:
0945: itemOriginal.updateTitle(newPackageName);
0946: itemOriginal.checkin("Renamed package "
0947: + itemOriginal.getName());
0948:
0949: PackageItem newPkg = loadPackage(newPackageName);
0950:
0951: for (Iterator iter = newPkg.getAssets(); iter.hasNext();) {
0952: AssetItem as = (AssetItem) iter.next();
0953: as.updateStringProperty(newPackageName,
0954: AssetItem.PACKAGE_NAME_PROPERTY);
0955: }
0956:
0957: save();
0958:
0959: return itemOriginal.getUUID();
0960: } catch (RepositoryException e) {
0961: log.error(e);
0962: throw new RulesRepositoryException(e);
0963: }
0964: }
0965:
0966: /**
0967: * Return a list of the snapshots available for the given package name.
0968: */
0969: public String[] listPackageSnapshots(String packageName) {
0970: Node snaps = this .getAreaNode(PACKAGE_SNAPSHOT_AREA);
0971: try {
0972: if (!snaps.hasNode(packageName)) {
0973: return new String[0];
0974: } else {
0975: List result = new ArrayList();
0976: NodeIterator it = snaps.getNode(packageName).getNodes();
0977: while (it.hasNext()) {
0978: Node element = (Node) it.next();
0979: result.add(element.getName());
0980: }
0981: return (String[]) result.toArray(new String[result
0982: .size()]);
0983: }
0984: } catch (RepositoryException e) {
0985: throw new RulesRepositoryException(e);
0986: }
0987: }
0988:
0989: public AssetItemIterator findArchivedAssets() {
0990: try {
0991:
0992: String sql = "SELECT " + AssetItem.TITLE_PROPERTY_NAME
0993: + ", " + AssetItem.DESCRIPTION_PROPERTY_NAME + ", "
0994: + AssetItem.CONTENT_PROPERTY_ARCHIVE_FLAG
0995: + " FROM " + AssetItem.RULE_NODE_TYPE_NAME;
0996: sql += " WHERE ";
0997: sql += " jcr:path LIKE '/" + RULES_REPOSITORY_NAME + "/"
0998: + RULE_PACKAGE_AREA + "/%'";
0999: sql += " AND " + AssetItem.CONTENT_PROPERTY_ARCHIVE_FLAG
1000: + " = 'true'";
1001:
1002: Query q = this .session.getWorkspace().getQueryManager()
1003: .createQuery(sql, Query.SQL);
1004:
1005: QueryResult res = q.execute();
1006:
1007: return new AssetItemIterator(res.getNodes(), this );
1008: } catch (RepositoryException e) {
1009: System.out.println(e.getMessage());
1010: throw new RulesRepositoryException(e);
1011: }
1012: }
1013:
1014: /**
1015: * This will search assets, looking for matches against the name.
1016: */
1017: public AssetItemIterator findAssetsByName(String name,
1018: boolean seekArchived) {
1019: try {
1020:
1021: String sql = "SELECT " + AssetItem.TITLE_PROPERTY_NAME
1022: + ", " + AssetItem.DESCRIPTION_PROPERTY_NAME + ", "
1023: + AssetItem.CONTENT_PROPERTY_ARCHIVE_FLAG
1024: + " FROM " + AssetItem.RULE_NODE_TYPE_NAME;
1025: sql += " WHERE " + AssetItem.TITLE_PROPERTY_NAME
1026: + " LIKE '" + name + "'";
1027: sql += " AND jcr:path LIKE '/" + RULES_REPOSITORY_NAME
1028: + "/" + RULE_PACKAGE_AREA + "/%'";
1029:
1030: if (seekArchived == false) {
1031: sql += " AND "
1032: + AssetItem.CONTENT_PROPERTY_ARCHIVE_FLAG
1033: + " = 'false'";
1034: }
1035:
1036: Query q = this .session.getWorkspace().getQueryManager()
1037: .createQuery(sql, Query.SQL);
1038:
1039: QueryResult res = q.execute();
1040:
1041: return new AssetItemIterator(res.getNodes(), this );
1042: } catch (RepositoryException e) {
1043: System.out.println(e.getMessage());
1044: throw new RulesRepositoryException(e);
1045: }
1046: }
1047:
1048: public AssetItemIterator findAssetsByName(String name) {
1049: return this .findAssetsByName(name, false);
1050: }
1051:
1052: /**
1053: * @return A list of statii in the system.
1054: */
1055: public StateItem[] listStates() {
1056: List states = new ArrayList();
1057: NodeIterator it;
1058: try {
1059: it = this .getAreaNode(STATE_AREA).getNodes();
1060:
1061: while (it.hasNext()) {
1062: states.add(new StateItem(this , it.nextNode()));
1063: }
1064: } catch (RepositoryException e) {
1065: log.error(e);
1066: throw new RulesRepositoryException(e);
1067: }
1068: return (StateItem[]) states
1069: .toArray(new StateItem[states.size()]);
1070: }
1071:
1072: /**
1073: * Copy a package to the target name.
1074: */
1075: public void copyPackage(String sourcePackageName,
1076: String destPackageName) {
1077: PackageItem source = loadPackage(sourcePackageName);
1078: String sourcePath;
1079:
1080: try {
1081: sourcePath = source.getNode().getPath();
1082:
1083: String destPath = source.getNode().getParent().getPath()
1084: + "/" + destPackageName;
1085: if (this .getAreaNode(RULE_PACKAGE_AREA).hasNode(
1086: destPackageName)) {
1087: throw new RulesRepositoryException(
1088: "Destination already exists.");
1089: }
1090: this .session.getWorkspace().copy(sourcePath, destPath);
1091:
1092: PackageItem newPkg = loadPackage(destPackageName);
1093:
1094: for (Iterator iter = newPkg.getAssets(); iter.hasNext();) {
1095: AssetItem as = (AssetItem) iter.next();
1096: as.updateStringProperty(destPackageName,
1097: AssetItem.PACKAGE_NAME_PROPERTY);
1098: }
1099:
1100: save();
1101:
1102: } catch (RepositoryException e) {
1103: log.error(e);
1104: throw new RulesRepositoryException(e);
1105: }
1106:
1107: }
1108: }
|