001: package org.drools.repository;
002:
003: import java.io.InputStream;
004: import java.util.Calendar;
005: import java.util.Iterator;
006:
007: import javax.jcr.Node;
008: import javax.jcr.PathNotFoundException;
009: import javax.jcr.Property;
010: import javax.jcr.RepositoryException;
011:
012: import org.apache.log4j.Logger;
013:
014: /**
015: * The RuleItem class is used to abstract away the details of the underlying JCR repository.
016: * It is used to pass information about rules stored in the repository.
017: *
018: * @author btruitt
019: */
020: public class AssetItem extends CategorisableItem {
021: private Logger log = Logger.getLogger(AssetItem.class);
022: /**
023: * The name of the rule node type
024: */
025: public static final String RULE_NODE_TYPE_NAME = "drools:assetNodeType";
026:
027: public static final String CONTENT_PROPERTY_NAME = "drools:content";
028: public static final String CONTENT_PROPERTY_BINARY_NAME = "drools:binaryContent";
029: public static final String CONTENT_PROPERTY_ATTACHMENT_FILENAME = "drools:attachmentFileName";
030:
031: /**
032: * The name of the date effective property on the rule node type
033: */
034: public static final String DATE_EFFECTIVE_PROPERTY_NAME = "drools:dateEffective";
035:
036: /**
037: * The name of the date expired property on the rule node type
038: */
039: public static final String DATE_EXPIRED_PROPERTY_NAME = "drools:dateExpired";
040:
041: public static final String PACKAGE_NAME_PROPERTY = "drools:packageName";
042:
043: /**
044: * Constructs a RuleItem object, setting its node attribute to the specified node.
045: *
046: * @param rulesRepository the rulesRepository that instantiated this object
047: * @param node the node in the repository that this RuleItem corresponds to
048: * @throws RulesRepositoryException
049: */
050: public AssetItem(RulesRepository rulesRepository, Node node)
051: throws RulesRepositoryException {
052: super (rulesRepository, node);
053:
054: try {
055: //make sure this node is a rule node
056: if (!(this .node.getPrimaryNodeType().getName().equals(
057: RULE_NODE_TYPE_NAME) || isHistoricalVersion())) {
058: String message = this .node.getName()
059: + " is not a node of type "
060: + RULE_NODE_TYPE_NAME
061: + " nor nt:version. It is a node of type: "
062: + this .node.getPrimaryNodeType().getName();
063: log.error(message);
064: throw new RulesRepositoryException(message);
065: }
066: } catch (Exception e) {
067: log.error("Caught exception", e);
068: throw new RulesRepositoryException(e);
069: }
070: }
071:
072: public AssetItem() {
073: super (null, null);
074: }
075:
076: /**
077: * returns the string contents of the rule node.
078: * If this is a binary asset, this will return null (use getBinaryContent instead).
079: */
080: public String getContent() throws RulesRepositoryException {
081: try {
082: Node ruleNode = getVersionContentNode();
083: if (ruleNode.hasProperty(CONTENT_PROPERTY_NAME)) {
084: Property data = ruleNode
085: .getProperty(CONTENT_PROPERTY_NAME);
086: return data.getValue().getString();
087:
088: } else {
089: return null;
090: }
091: } catch (Exception e) {
092: log.error("Caught Exception", e);
093: throw new RulesRepositoryException(e);
094: }
095: }
096:
097: /**
098: * If this asset contains binary data, this is how you return it.
099: * Otherwise it will return null.
100: */
101: public InputStream getBinaryContentAttachment() {
102: try {
103: Node ruleNode = getVersionContentNode();
104: if (ruleNode.hasProperty(CONTENT_PROPERTY_BINARY_NAME)) {
105: Property data = ruleNode
106: .getProperty(CONTENT_PROPERTY_BINARY_NAME);
107: return data.getStream();
108: } else {
109: return null;
110: }
111: } catch (Exception e) {
112: log.error("Caught Exception", e);
113: throw new RulesRepositoryException(e);
114: }
115: }
116:
117: /** Get the name of the "file" attachment, if one is set. Null otherwise */
118: public String getBinaryContentAttachmentFileName() {
119: return getStringProperty(CONTENT_PROPERTY_ATTACHMENT_FILENAME);
120: }
121:
122: /**
123: * This is a convenience method for returning the binary data as a byte array.
124: */
125: public byte[] getBinaryContentAsBytes() {
126: try {
127: Node ruleNode = getVersionContentNode();
128: if (ruleNode.hasProperty(CONTENT_PROPERTY_BINARY_NAME)) {
129: Property data = ruleNode
130: .getProperty(CONTENT_PROPERTY_BINARY_NAME);
131: InputStream in = data.getStream();
132:
133: // Create the byte array to hold the data
134: byte[] bytes = new byte[(int) data.getLength()];
135:
136: // Read in the bytes
137: int offset = 0;
138: int numRead = 0;
139: while (offset < bytes.length
140: && (numRead = in.read(bytes, offset,
141: bytes.length - offset)) >= 0) {
142: offset += numRead;
143: }
144:
145: // Ensure all the bytes have been read in
146: if (offset < bytes.length) {
147: throw new RulesRepositoryException(
148: "Could not completely read asset "
149: + getName());
150: }
151:
152: // Close the input stream and return bytes
153: in.close();
154: return bytes;
155: } else {
156: return null;
157: }
158: } catch (Exception e) {
159: log.error(e);
160: if (e instanceof RuntimeException)
161: throw (RuntimeException) e;
162: throw new RulesRepositoryException(e);
163: }
164: }
165:
166: /**
167: * @return the date the rule becomes effective
168: * @throws RulesRepositoryException
169: */
170: public Calendar getDateEffective() throws RulesRepositoryException {
171: try {
172: Node ruleNode = getVersionContentNode();
173:
174: Property dateEffectiveProperty = ruleNode
175: .getProperty(DATE_EFFECTIVE_PROPERTY_NAME);
176: return dateEffectiveProperty.getDate();
177: } catch (PathNotFoundException e) {
178: // doesn't have this property
179: return null;
180: } catch (Exception e) {
181: log.error("Caught Exception", e);
182: throw new RulesRepositoryException(e);
183: }
184: }
185:
186: /**
187: * Creates a new version of this object's rule node, updating the effective date for the
188: * rule node.
189: *
190: * @param newDateEffective the new effective date for the rule
191: * @throws RulesRepositoryException
192: */
193: public void updateDateEffective(Calendar newDateEffective)
194: throws RulesRepositoryException {
195: checkIsUpdateable();
196: checkout();
197: try {
198: this .node.setProperty(DATE_EFFECTIVE_PROPERTY_NAME,
199: newDateEffective);
200: } catch (RepositoryException e) {
201: log.error("Caught Exception", e);
202: throw new RulesRepositoryException(e);
203: }
204: }
205:
206: /**
207: * @return the date the rule becomes expired
208: * @throws RulesRepositoryException
209: */
210: public Calendar getDateExpired() throws RulesRepositoryException {
211: try {
212: Node ruleNode = getVersionContentNode();
213:
214: Property dateExpiredProperty = ruleNode
215: .getProperty(DATE_EXPIRED_PROPERTY_NAME);
216: return dateExpiredProperty.getDate();
217: } catch (PathNotFoundException e) {
218: // doesn't have this property
219: return null;
220: } catch (Exception e) {
221: log.error("Caught Exception", e);
222: throw new RulesRepositoryException(e);
223: }
224: }
225:
226: /**
227: * Creates a new version of this object's rule node, updating the expired date for the
228: * rule node.
229: *
230: * @param newDateExpired the new expired date for the rule
231: * @throws RulesRepositoryException
232: */
233: public void updateDateExpired(Calendar newDateExpired)
234: throws RulesRepositoryException {
235: checkout();
236:
237: try {
238: this .node.setProperty(DATE_EXPIRED_PROPERTY_NAME,
239: newDateExpired);
240: } catch (Exception e) {
241: log.error("Caught Exception", e);
242: throw new RulesRepositoryException(e);
243: }
244: }
245:
246: /**
247: * This will update the asset's content (checking it out if it is not already).
248: * This will not save the session or create a new version of the node
249: * (this has to be done seperately, as several properties may change as part of one edit).
250: * This is only used if the asset is a textual asset. For binary, use the updateBinaryContent method
251: * instead.
252: */
253: public AssetItem updateContent(String newRuleContent)
254: throws RulesRepositoryException {
255: checkout();
256: try {
257: this .node
258: .setProperty(CONTENT_PROPERTY_NAME, newRuleContent);
259: return this ;
260: } catch (RepositoryException e) {
261: log.error("Unable to update the asset content", e);
262: throw new RulesRepositoryException(e);
263: }
264: }
265:
266: /**
267: * If the asset is a binary asset, then use this to update the content
268: * (do NOT use text).
269: */
270: public AssetItem updateBinaryContentAttachment(InputStream data) {
271: checkout();
272: try {
273: this .node.setProperty(CONTENT_PROPERTY_BINARY_NAME, data);
274: return this ;
275: } catch (RepositoryException e) {
276: log.error("Unable to update the assets binary content", e);
277: throw new RulesRepositoryException(e);
278: }
279: }
280:
281: /**
282: * Optionally set the filename to be associated with the binary content.
283: */
284: public void updateBinaryContentAttachmentFileName(String name) {
285: updateStringProperty(name, CONTENT_PROPERTY_ATTACHMENT_FILENAME);
286: }
287:
288: /**
289: * This updates a user defined property (not one of the intrinsic ones).
290: */
291: public void updateUserProperty(String propertyName, String value) {
292: if (propertyName.startsWith("drools:")) {
293: throw new IllegalArgumentException(
294: "Can only set the pre defined fields using the appropriate methods.");
295: }
296: updateStringProperty(value, propertyName);
297:
298: }
299:
300: /**
301: * Nicely formats the information contained by the node that this object encapsulates
302: */
303: public String toString() {
304: try {
305: StringBuffer returnString = new StringBuffer();
306: returnString.append("Content of rule item named '"
307: + this .getName() + "':\n");
308: returnString.append("Content: " + this .getContent() + "\n");
309: returnString.append("------\n");
310:
311: returnString
312: .append("Archived: " + this .isArchived() + "\n");
313: returnString.append("------\n");
314:
315: returnString.append("Date Effective: "
316: + this .getDateEffective() + "\n");
317: returnString.append("Date Expired: "
318: + this .getDateExpired() + "\n");
319: returnString.append("------\n");
320:
321: returnString.append("Rule state: ");
322: StateItem stateItem = this .getState();
323: if (stateItem != null) {
324: returnString.append(this .getState().getName() + "\n");
325: } else {
326: returnString.append("NO STATE SET FOR THIS NODE\n");
327: }
328: returnString.append("------\n");
329:
330: returnString.append("Rule tags:\n");
331: for (Iterator it = this .getCategories().iterator(); it
332: .hasNext();) {
333: CategoryItem currentTag = (CategoryItem) it.next();
334: returnString.append(currentTag.getName() + "\n");
335: }
336: returnString.append("--------------\n");
337: return returnString.toString();
338: } catch (Exception e) {
339: throw new RulesRepositoryException(e);
340: }
341: }
342:
343: public VersionableItem getPrecedingVersion()
344: throws RulesRepositoryException {
345: try {
346: Node precedingVersionNode = this .getPrecedingVersionNode();
347: if (precedingVersionNode != null) {
348: return new AssetItem(this .rulesRepository,
349: precedingVersionNode);
350: } else {
351: return null;
352: }
353: } catch (Exception e) {
354: log.error("Caught exception", e);
355: throw new RulesRepositoryException(e);
356: }
357: }
358:
359: public VersionableItem getSucceedingVersion()
360: throws RulesRepositoryException {
361: try {
362: Node succeedingVersionNode = this
363: .getSucceedingVersionNode();
364: if (succeedingVersionNode != null) {
365: return new AssetItem(this .rulesRepository,
366: succeedingVersionNode);
367: } else {
368: return null;
369: }
370: } catch (Exception e) {
371: log.error("Caught exception", e);
372: throw new RulesRepositoryException(e);
373: }
374: }
375:
376: /**
377: * Get the name of the enclosing package.
378: * As assets are stored in versionable subfolders, this means walking up 2 levels in the
379: * hierarchy to get to the enclosing "package" node.
380: */
381: public String getPackageName() {
382: return super .getStringProperty(PACKAGE_NAME_PROPERTY);
383: }
384:
385: /**
386: * @return A property value (for a user defined property).
387: */
388: public String getUserProperty(String property) {
389: return getStringProperty(property);
390: }
391:
392: /**
393: * This will remove the item.
394: * The repository will need to be saved for this to take effect.
395: * Typically the package that contains this should be versioned before removing this,
396: * to make it easy to roll back.
397: */
398: public void remove() {
399: checkIsUpdateable();
400: if (this .getDateExpired() != null) {
401: if (Calendar.getInstance().before(this .getDateExpired())) {
402: throw new RulesRepositoryException(
403: "Can't delete an item before its expiry date.");
404: }
405: }
406: try {
407: this .node.remove();
408: } catch (RepositoryException e) {
409: throw new RulesRepositoryException(e);
410: }
411: }
412:
413: /**
414: *
415: * @return An iterator over the nodes history.
416: */
417: public AssetHistoryIterator getHistory() {
418: return new AssetHistoryIterator(this .rulesRepository, this .node);
419: }
420:
421: /**
422: * This will get the package an asset item belongs to.
423: */
424: public PackageItem getPackage() {
425:
426: try {
427: if (this .isHistoricalVersion()) {
428: throw new UnsupportedOperationException(
429: "Unable to get package for versioned asset. Use base revision.");
430: }
431: return new PackageItem(this .rulesRepository, this .node
432: .getParent().getParent());
433: } catch (RepositoryException e) {
434: throw new RulesRepositoryException(e);
435: }
436: }
437:
438: }
|