001: /*--
002:
003: Copyright (C) 2002-2005 Adrian Price.
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: 1. Redistributions of source code must retain the above copyright
011: notice, this list of conditions, and the following disclaimer.
012:
013: 2. Redistributions in binary form must reproduce the above copyright
014: notice, this list of conditions, and the disclaimer that follows
015: these conditions in the documentation and/or other materials
016: provided with the distribution.
017:
018: 3. The names "OBE" and "Open Business Engine" must not be used to
019: endorse or promote products derived from this software without prior
020: written permission. For written permission, please contact
021: adrianprice@sourceforge.net.
022:
023: 4. Products derived from this software may not be called "OBE" or
024: "Open Business Engine", nor may "OBE" or "Open Business Engine"
025: appear in their name, without prior written permission from
026: Adrian Price (adrianprice@users.sourceforge.net).
027:
028: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
029: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
030: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
031: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
032: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
033: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
034: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
035: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
036: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
037: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
038: POSSIBILITY OF SUCH DAMAGE.
039:
040: For more information on OBE, please see
041: <http://obe.sourceforge.net/>.
042:
043: */
044:
045: package org.obe.server.j2ee.repository;
046:
047: import org.apache.commons.logging.Log;
048: import org.obe.client.api.repository.RepositoryException;
049: import org.obe.server.j2ee.J2EEServerConfig;
050: import org.obe.server.j2ee.ejb.AbstractEntityEJB;
051: import org.obe.server.j2ee.ejb.Null;
052: import org.obe.spi.model.AttributedEntity;
053: import org.obe.spi.service.DataConverter;
054: import org.obe.spi.service.ServerConfig;
055: import org.obe.spi.service.ServiceManager;
056: import org.obe.xpdl.model.data.Type;
057: import org.wfmc.wapi.WMFilter;
058:
059: import javax.ejb.CreateException;
060: import javax.ejb.FinderException;
061: import javax.ejb.ObjectNotFoundException;
062: import java.io.Serializable;
063: import java.sql.SQLException;
064: import java.util.Collection;
065: import java.util.Date;
066: import java.util.List;
067:
068: /**
069: * Holds the persistent state of a workflow relevant datum instance.
070: *
071: * @author Adrian Price
072: * @ejb:bean type="CMP"
073: * cmp-version="2.x"
074: * name="AttributeInstance"
075: * display-name="OBE Attribute"
076: * jndi-name="org/obe/ejb/AttributeInstance"
077: * local-jndi-name="org/obe/ejb/AttributeInstanceLocal"
078: * reentrant="False"
079: * schema="ATTRIBUTEINSTANCE"
080: * transaction-type="Container"
081: * view-type="local"
082: * @ejb:home local-class="org.obe.server.j2ee.repository.AttributeInstanceLocalHome"
083: * local-extends="javax.ejb.EJBLocalHome"
084: * @ejb:interface local-class="org.obe.server.j2ee.repository.AttributeInstanceLocal"
085: * local-extends="org.obe.spi.model.AttributeInstance, javax.ejb.EJBLocalObject"
086: * @ejb:persistence table-name="OBEATTRIBUTEINSTANCE"
087: * @ejb:pk class="org.obe.server.j2ee.repository.AttributeInstancePK"
088: * unchecked="true"
089: * generate="false"
090: * @ejb:permission unchecked="true"
091: * @ejb:transaction type="Supports"
092: * @weblogic:transaction-isolation ${transaction.isolation}
093: * @ejb:finder signature="java.util.Collection findAll()"
094: * query="SELECT OBJECT(a) FROM ATTRIBUTEINSTANCE AS a"
095: * result-type-mapping="Local"
096: * unchecked="true"
097: * @ejb:finder signature="java.util.Collection findByName(java.lang.String processDefinitionId, int ownerType, java.lang.String attrName)"
098: * query="SELECT OBJECT(a) FROM ATTRIBUTEINSTANCE AS a WHERE a.processDefinitionId=?1 AND a.ownerType=?2 AND a.name=?3"
099: * result-type-mapping="Local"
100: * unchecked="true"
101: * @ejb:finder signature="java.util.Collection findByOwnerId(java.lang.String ownerId, int ownerType)"
102: * query="SELECT OBJECT(a) FROM ATTRIBUTEINSTANCE AS a WHERE a.ownerId=?1 AND a.ownerType=?2"
103: * result-type-mapping="Local"
104: * unchecked="true"
105: * @ejb:ejb-ref ejb-name="ProcessInstance"
106: * view-type="local"
107: * @weblogic:ejb-local-reference-description ejb-ref-name="ejb/ProcessInstance"
108: * jndi-name="org/obe/ejb/ProcessInstanceLocal"
109: * @ejb:resource-ref res-name="jdbc/TxDataSource"
110: * res-type="javax.sql.DataSource"
111: * res-auth="Container"
112: * @jboss:resource-manager res-man-class="javax.sql.DataSource"
113: * res-man-name="jdbc/TxDataSource"
114: * res-man-jndi-name="java:/${xdoclet.DataSource}"
115: * @weblogic:resource-description res-ref-name="jdbc/TxDataSource"
116: * jndi-name="${xdoclet.DataSource}"
117: * @weblogic:data-source-name java:comp/env/jdbc/TxDataSource
118: * @jboss:create-table false
119: * @weblogic:pool max-beans-in-free-pool="150"
120: * initial-beans-in-free-pool="0"
121: * @weblogic:cache max-beans-in-cache="150"
122: * idle-timeout-seconds="0"
123: * concurrency-strategy="Database"
124: * @weblogic:persistence delay-updates-until-end-of-tx="True"
125: * @jboss:tuned-updates tune="true"
126: * @jboss:container-configuration name="${xdoclet.jboss.container-configuration}"
127: * @jboss:cache-invalidation value="True"
128: * @jboss:cache-invalidation-config invalidation-group-name="AttributeInstance"
129: */
130: public abstract class AttributeInstanceEJB extends AbstractEntityEJB {
131: private static final long serialVersionUID = -2146180028550234465L;
132: private static final Log _logger = getLogger(AttributeInstanceEJB.class);
133: private static final ServiceManager _svcMgr = J2EEServerConfig.svcMgr;
134: private static final DataConverter _dataConverter = _svcMgr
135: .getDataConverter();
136: private static final int MAX_STRING_LEN = 254;
137: private transient Object _value;
138:
139: public AttributeInstanceEJB() {
140: }
141:
142: protected void clear() {
143: super .clear();
144: _value = null;
145: }
146:
147: /**
148: * @ejb:create-method
149: */
150: public AttributeInstancePK ejbCreate(String processDefinitionId,
151: String processInstanceId, String ownerId, int ownerType,
152: String attributeName, int attributeType,
153: Object attributeValue) throws CreateException {
154:
155: if (_logger.isDebugEnabled() && ServerConfig.isVerbose()) {
156: _logger.debug("ejbCreate(" + processDefinitionId + ", "
157: + processInstanceId + ", " + ownerId + ", "
158: + ownerType + ", " + attributeName + ", "
159: + attributeType + ", " + attributeValue + ')');
160: }
161:
162: clear();
163: setProcessDefinitionId(processDefinitionId);
164: setProcessInstanceId(processInstanceId);
165: setOwnerId(ownerId);
166: setOwnerType(ownerType);
167: setName(attributeName);
168: setType(attributeType);
169:
170: // WebLogic Oracle jDriver does not permit the use of
171: // PreparedStatement.setNull() with a field type of java.sql.Types.Blob,
172: // so for Oracle we have to make sure the OBJVALUE field always contains
173: // a non-null value.
174: if (AttributeInstanceDAO.getInstance().isOracle())
175: setObjectValue(Null.INSTANCE);
176: setValue(attributeType, _value = attributeValue);
177:
178: return null;
179: }
180:
181: public void ejbPostCreate(String processDefinitionId,
182: String processInstanceId, String ownerId, int ownerType,
183: String attributeName, int attributeType,
184: Object attributeValue) {
185:
186: if (_logger.isDebugEnabled() && ServerConfig.isVerbose()) {
187: _logger.debug("ejbPostCreate(" + processDefinitionId + ", "
188: + processInstanceId + ", " + ownerId + ", "
189: + ownerType + ", " + attributeName + ", "
190: + attributeType + ", " + attributeValue + ')');
191: }
192: }
193:
194: /**
195: * Counts the number of matching attribute instances.
196: *
197: * @param processInstanceId The ID of the process instance for which to
198: * count attributes; can be <code>null</code>.
199: * @param ownerId The ID of the attribute owner for which to
200: * count attributes; can be <code>null</code>.
201: * @param ownerType The owner type, one of:
202: * {@link AttributedEntity#PROCESS_INSTANCE_TYPE},
203: * {@link AttributedEntity#ACTIVITY_INSTANCE_TYPE},
204: * {@link AttributedEntity#WORKITEM_TYPE}.
205: * @param filter Attribute instance filter; can be <code>null</code>.
206: * @return The number of matching attribute instances.
207: * @ejb:home-method
208: */
209: public int ejbHomeCount(String processDefinitionId,
210: String processInstanceId, String activityDefinitionId,
211: String ownerId, int ownerType, WMFilter filter,
212: String attrName) throws RepositoryException {
213:
214: try {
215: return AttributeInstanceDAO.getInstance().count(
216: processDefinitionId, processInstanceId,
217: activityDefinitionId, ownerId, ownerType, filter,
218: attrName);
219: } catch (SQLException e) {
220: throw new RepositoryException(e);
221: }
222: }
223:
224: /**
225: * Retrieves matching attribute instances.
226: *
227: * @param processInstanceId The ID of the process instance for which to
228: * retrieve attributes; can be <code>null</code>.
229: * @param ownerId The ID of the attribute owner for which to
230: * retrieve attributes; can be <code>null</code>.
231: * @param ownerType The owner type, one of:
232: * {@link AttributedEntity#PROCESS_INSTANCE_TYPE},
233: * {@link AttributedEntity#ACTIVITY_INSTANCE_TYPE},
234: * {@link AttributedEntity#WORKITEM_TYPE}.
235: * @param filter Attribute instance filter; should not be <code>null</code>
236: * (use {@link AttributeInstanceLocalHome#findAll} instead).
237: * @return A collection of matching attribute instances.
238: * @ejb:home-method
239: * @see AttributeInstanceLocalHome#findAll
240: */
241: public Collection ejbHomeXfindByFilter(String processDefinitionId,
242: String processInstanceId, String activityDefinitionId,
243: String ownerId, int ownerType, String attrName,
244: WMFilter filter) throws FinderException,
245: RepositoryException {
246:
247: try {
248: List attributes = AttributeInstanceDAO.getInstance()
249: .findByFilter(processDefinitionId,
250: processInstanceId, activityDefinitionId,
251: ownerId, attrName, ownerType, filter);
252: AttributeInstanceLocalHome home = (AttributeInstanceLocalHome) _ctx
253: .getEJBLocalHome();
254: for (int i = 0; i < attributes.size(); i++) {
255: try {
256: attributes
257: .set(
258: i,
259: home
260: .findByPrimaryKey((AttributeInstancePK) attributes
261: .get(i)));
262: } catch (ObjectNotFoundException e) {
263: // Someone else deleted this PK, so just skip it.
264: attributes.remove(i--);
265: }
266: }
267: return attributes;
268: } catch (SQLException e) {
269: throw new RepositoryException(e);
270: }
271: }
272:
273: /**
274: * @ejb:interface-method
275: * @ejb:persistence column-name="PROCESSDEFINITIONID"
276: */
277: public abstract String getProcessDefinitionId();
278:
279: /**
280: * @ejb:interface-method
281: * @ejb:persistence column-name="PROCESSDEFINITIONID"
282: */
283: public abstract void setProcessDefinitionId(
284: String processDefinitionId);
285:
286: /**
287: * @ejb:interface-method
288: * @ejb:persistence column-name="PROCESSINSTANCEID"
289: */
290: public abstract String getProcessInstanceId();
291:
292: /**
293: * @ejb:interface-method
294: * @ejb:persistence column-name="PROCESSINSTANCEID"
295: */
296: public abstract void setProcessInstanceId(String processInstanceId);
297:
298: /**
299: * @ejb:persistence column-name="OWNERID"
300: * @ejb:pk-field
301: */
302: public abstract String getOwnerId();
303:
304: /**
305: * @ejb:persistence column-name="OWNERID"
306: */
307: public abstract void setOwnerId(String ownerId);
308:
309: /**
310: * @ejb:interface-method
311: * @ejb:persistence column-name="NAME"
312: * @ejb:pk-field
313: */
314: public abstract String getName();
315:
316: /**
317: * @ejb:persistence column-name="NAME"
318: */
319: public abstract void setName(String name);
320:
321: /**
322: * @ejb:interface-method
323: * @ejb:persistence column-name="OWNERTYPE"
324: * @ejb:pk-field
325: */
326: public abstract int getOwnerType();
327:
328: /**
329: * @ejb:persistence column-name="OWNERTYPE"
330: */
331: public abstract void setOwnerType(int type);
332:
333: /**
334: * @ejb:interface-method
335: * @ejb:persistence column-name="TYPE"
336: */
337: public abstract int getType();
338:
339: /**
340: * @ejb:persistence column-name="TYPE"
341: */
342: public abstract void setType(int type);
343:
344: /**
345: * @ejb:persistence column-name="BOOLVALUE"
346: * jdbc-type="BIT"
347: */
348: public abstract Boolean getBooleanValue();
349:
350: /**
351: * @ejb:persistence column-name="BOOLVALUE"
352: * jdbc-type="BIT"
353: */
354: public abstract void setBooleanValue(Boolean value);
355:
356: /**
357: * @ejb:persistence column-name="DATEVALUE"
358: */
359: public abstract Date getDateValue();
360:
361: /**
362: * @ejb:persistence column-name="DATEVALUE"
363: */
364: public abstract void setDateValue(Date value);
365:
366: /**
367: * @ejb:persistence column-name="DBLVALUE"
368: */
369: public abstract Double getDoubleValue();
370:
371: /**
372: * @ejb:persistence column-name="DBLVALUE"
373: */
374: public abstract void setDoubleValue(Double value);
375:
376: /**
377: * @ejb:persistence column-name="INTVALUE"
378: */
379: public abstract Integer getIntValue();
380:
381: /**
382: * @ejb:persistence column-name="INTVALUE"
383: */
384: public abstract void setIntValue(Integer value);
385:
386: /**
387: * @ejb:persistence column-name="OBJVALUE"
388: */
389: public abstract Object getObjectValueCmp();
390:
391: /**
392: * @ejb:persistence column-name="OBJVALUE"
393: */
394: public abstract void setObjectValueCmp(Object value);
395:
396: private Object getObjectValue() {
397: Object value = getObjectValueCmp();
398:
399: // This particular value really means null.
400: if (value == Null.INSTANCE)
401: value = null;
402: return value;
403: }
404:
405: private void setObjectValue(Object value) {
406: // WebLogic can't store null in an Oracle BLOB - store a token instead.
407: if (value == null
408: && AttributeInstanceDAO.getInstance().isOracle())
409: value = Null.INSTANCE;
410: setObjectValueCmp(value);
411: }
412:
413: /**
414: * @ejb:persistence column-name="STRVALUE"
415: */
416: public abstract String getStringValue();
417:
418: /**
419: * @ejb:persistence column-name="STRVALUE"
420: */
421: public abstract void setStringValue(String value);
422:
423: /**
424: * @ejb:interface-method
425: */
426: public AttributedEntity getOwner() {
427: AttributedEntity owner;
428: try {
429: switch (getOwnerType()) {
430: case AttributedEntity.PROCESS_INSTANCE_TYPE:
431: ProcessInstanceLocalHome piHome = EJBLocalHelper
432: .getProcessInstanceHome();
433: owner = piHome.findByPrimaryKey(getOwnerId());
434: break;
435: case AttributedEntity.ACTIVITY_INSTANCE_TYPE:
436: ActivityInstanceLocalHome aiHome = EJBLocalHelper
437: .getActivityInstanceHome();
438: owner = aiHome.findByPrimaryKey(getOwnerId());
439: break;
440: case AttributedEntity.WORKITEM_TYPE:
441: WorkItemLocalHome wiHome = EJBLocalHelper
442: .getWorkItemHome();
443: owner = wiHome.findByPrimaryKey(getOwnerId());
444: break;
445: default:
446: owner = null;
447: }
448: } catch (FinderException e) {
449: owner = null;
450: }
451: return owner;
452: }
453:
454: /**
455: * @ejb:interface-method
456: */
457: public Object getValue() {
458: if (_value == null) {
459: Object value;
460: switch (getType()) {
461: case Type.BOOLEAN_TYPE:
462: value = getBooleanValue();
463: break;
464: case Type.FLOAT_TYPE:
465: value = getDoubleValue();
466: break;
467: case Type.INTEGER_TYPE:
468: value = getIntValue();
469: break;
470: case Type.DATETIME_TYPE:
471: value = getDateValue();
472: break;
473: case Type.SCHEMA_TYPE:
474: value = _dataConverter.toDocument(getObjectValue());
475: break;
476: case Type.STRING_TYPE:
477: // If the string value is null or is shorter than the
478: // maximum for a VARCHAR column, return the value from the
479: // VARCHAR column. Otherwise, return the value from the
480: // BLOB column.
481: value = getStringValue();
482: if (value == null
483: || ((CharSequence) value).length() < MAX_STRING_LEN) {
484:
485: break;
486: }
487: case Type.EXTERNAL_REFERENCE_TYPE:
488: case Type.REFERENCE_TYPE:
489: case Type.DECLARED_TYPE:
490: case Type.RECORD_TYPE:
491: case Type.UNION_TYPE:
492: case Type.ARRAY_TYPE:
493: case Type.LIST_TYPE:
494: value = getObjectValue();
495: break;
496: default:
497: value = getStringValue();
498: }
499: _value = value;
500: }
501: return _value;
502: }
503:
504: /**
505: * @ejb:interface-method
506: */
507: public void setValue(int type, Object value) {
508: if (type != Type.DEFAULT_TYPE && type != getType())
509: _logger.warn("Attribute type cannot be changed");
510:
511: boolean storeStringCopy;
512: type = getType();
513: value = _dataConverter.convertValue(value, type);
514: switch (type) {
515: case Type.BOOLEAN_TYPE:
516: setBooleanValue((Boolean) value);
517: storeStringCopy = true;
518: break;
519: case Type.FLOAT_TYPE:
520: setDoubleValue((Double) value);
521: storeStringCopy = true;
522: break;
523: case Type.INTEGER_TYPE:
524: setIntValue((Integer) value);
525: storeStringCopy = true;
526: break;
527: case Type.DATETIME_TYPE:
528: setDateValue((Date) value);
529: storeStringCopy = true;
530: break;
531: case Type.STRING_TYPE:
532: // If a string value exceeds the maximum for a VARCHAR column,
533: // we'll store the first 254 characters in the VARCHAR column
534: // and serialize the entire string to the BLOB column.
535: String strValue = (String) value;
536: Object objValue = value;
537: if (strValue != null && strValue.length() >= MAX_STRING_LEN) {
538: strValue = strValue.substring(0, MAX_STRING_LEN - 3)
539: + "...";
540: } else {
541: objValue = null;
542: }
543: setStringValue(strValue);
544: setObjectValue(objValue);
545: storeStringCopy = false;
546: break;
547: case Type.SCHEMA_TYPE:
548: // If value is a DOM, we can only persist it as such if the DOM
549: // implementation is serializable. Either way, value is a BLOB.
550: if (!(value instanceof Serializable))
551: value = _dataConverter.toString(value);
552: case Type.EXTERNAL_REFERENCE_TYPE:
553: case Type.REFERENCE_TYPE:
554: case Type.RECORD_TYPE:
555: case Type.UNION_TYPE:
556: case Type.ARRAY_TYPE:
557: case Type.LIST_TYPE:
558: // Non-serializable objects cannot be persisted.
559: setObjectValue(value instanceof Serializable ? value : null);
560: storeStringCopy = false;
561: break;
562: case Type.DECLARED_TYPE:
563: throw new IllegalArgumentException(
564: "DeclaredType cannot be used directly");
565: default:
566: setStringValue((String) value);
567: storeStringCopy = false;
568: }
569:
570: // If required, store a string copy of the value to facilitate
571: // queries which span multiple workflow definitions or packages. Of
572: // course, non-string values represented as strings do not support
573: // meaningful magnitude comparisons or orderings, but this is a
574: // limitation we're prepared to accept for the time being.
575: if (storeStringCopy)
576: setStringValue(_dataConverter.toString(value,
577: MAX_STRING_LEN));
578:
579: _value = value;
580: }
581:
582: /**
583: * @ejb:interface-method
584: */
585: public String toString() {
586: return "AttributeInstance[processInstanceId='"
587: + getProcessInstanceId() + "', owner=" + getOwner()
588: + ", name='" + getName() + "', type=" + getType()
589: + ", value=" + getValue() + ']';
590: }
591:
592: protected final Log getLogger() {
593: return _logger;
594: }
595: }
|