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.ejb.AbstractEntityEJB;
050: import org.obe.spi.service.ServerConfig;
051: import org.obe.xpdl.model.misc.RedefinableHeader;
052: import org.obe.xpdl.model.pkg.XPDLPackage;
053: import org.obe.xpdl.model.workflow.WorkflowProcess;
054: import org.wfmc.wapi.WMFilter;
055:
056: import javax.ejb.*;
057: import java.sql.SQLException;
058: import java.util.ArrayList;
059: import java.util.Collection;
060: import java.util.Iterator;
061: import java.util.List;
062:
063: /**
064: * Persists a package definition. Also creates a record for each process
065: * definition contained within the package.
066: *
067: * @author Adrian Price
068: * @ejb:bean type="CMP"
069: * cmp-version="2.x"
070: * name="XPDLPackage"
071: * display-name="OBE Package Definition"
072: * jndi-name="org/obe/ejb/Package"
073: * local-jndi-name="org/obe/ejb/PackageLocal"
074: * primkey-field="packageId"
075: * schema="PACKAGE"
076: * reentrant="False"
077: * transaction-type="Container"
078: * view-type="local"
079: * @ejb:persistence table-name="OBEPACKAGE"
080: * @ejb:pk class="java.lang.String"
081: * unchecked="true"
082: * @ejb:home local-class="org.obe.server.j2ee.repository.PackageLocalHome"
083: * local-extends="org.obe.server.j2ee.repository.PackageROHome,javax.ejb.EJBLocalHome"
084: * @ejb:interface local-class="org.obe.server.j2ee.repository.PackageLocal"
085: * local-extends="org.obe.server.j2ee.repository.PackageRO,javax.ejb.EJBLocalObject"
086: * @ejb:permission unchecked="true"
087: * @ejb:transaction type="Supports"
088: * @weblogic:transaction-isolation ${transaction.isolation}
089: * @ejb:finder signature="java.util.Collection findAll()"
090: * query="SELECT OBJECT(p) FROM PACKAGE AS p"
091: * result-type-mapping="Local"
092: * unchecked="true"
093: * @ejb:ejb-ref ejb-name="ProcessDefinition"
094: * view-type="local"
095: * @weblogic:ejb-local-reference-description ejb-ref-name="ejb/ProcessDefinition"
096: * jndi-name="org/obe/ejb/ProcessDefinitionLocal"
097: * @weblogic:invalidation-target ejb-name="XPDLPackageRO"
098: * @ejb:resource-ref res-name="jdbc/TxDataSource"
099: * res-type="javax.sql.DataSource"
100: * res-auth="Container"
101: * @jboss:resource-manager res-man-class="javax.sql.DataSource"
102: * res-man-name="jdbc/TxDataSource"
103: * res-man-jndi-name="java:/${xdoclet.DataSource}"
104: * @weblogic:resource-description res-ref-name="jdbc/TxDataSource"
105: * jndi-name="${xdoclet.DataSource}"
106: * @weblogic:data-source-name java:comp/env/jdbc/TxDataSource
107: * @jboss:create-table false
108: * @weblogic:pool max-beans-in-free-pool="200"
109: * initial-beans-in-free-pool="0"
110: * @weblogic:cache max-beans-in-cache="200"
111: * idle-timeout-seconds="0"
112: * concurrency-strategy="Database"
113: * @weblogic:persistence delay-updates-until-end-of-tx="True"
114: * @jboss:tuned-updates tune="true"
115: * @jboss:container-configuration name="${xdoclet.jboss.container-configuration}"
116: * @jboss:cache-invalidation value="True"
117: * @jboss:cache-invalidation-config invalidation-group-name="Package"
118: */
119: public abstract class PackageEJB extends AbstractEntityEJB {
120: private static final long serialVersionUID = -783170684190924323L;
121: protected static final Log _logger = getLogger(PackageEJB.class);
122:
123: public PackageEJB() {
124: }
125:
126: /**
127: * @ejb:create-method
128: */
129: public String ejbCreate(XPDLPackage pkg) throws CreateException {
130: if (_logger.isDebugEnabled() && ServerConfig.isVerbose())
131: _logger.debug("ejbCreate(" + pkg + ')');
132:
133: setPackageId(pkg.getId());
134:
135: // These fields are set again when ejbPostCreate() calls update(), but
136: // we must set them here because NAME and XPDLPACKAGE are non-nullable
137: // columns. By default, WebLogic defers inserts until after
138: // ejbPostCreate, but other app servers (e.g., JBoss) perform the insert
139: // immediately.
140: setName(pkg.getName());
141: setPackageCmp(pkg);
142:
143: return null;
144: }
145:
146: public void ejbPostCreate(XPDLPackage pkg) throws CreateException {
147: if (_logger.isDebugEnabled() && ServerConfig.isVerbose())
148: _logger.debug("ejbPostCreate(" + pkg + ')');
149:
150: // Nasty workaround for JBoss bug:
151: try {
152: update(pkg, true);
153: } catch (RemoveException e) {
154: // This won't ever happen - update() doesn't call remove()
155: // during package creation.
156: _logger.error("Unexpected exception during create", e);
157: throw new CreateException(e.getMessage());
158: }
159: }
160:
161: public void ejbRemove() throws RemoveException {
162: // Cascade delete to all process definitions associated with this
163: // package.
164: // N.B. Must copy the collection because remove() alters it.
165: Collection coll = new ArrayList(getProcessDefinitionsCmr());
166: for (Iterator iter = coll.iterator(); iter.hasNext();)
167: ((EJBLocalObject) iter.next()).remove();
168:
169: // _procDefs = _umProcDefs = null;
170: }
171:
172: /**
173: * Counts the number of matching packages.
174: *
175: * @param filter Package filter; can be <code>null</code>.
176: * @return The number of matching packages.
177: * @ejb:home-method
178: */
179: public int ejbHomeCount(WMFilter filter) throws RepositoryException {
180: try {
181: return PackageDAO.getInstance().count(filter);
182: } catch (SQLException e) {
183: throw new RepositoryException(e);
184: }
185: }
186:
187: /**
188: * Retrieves matching packages.
189: *
190: * @param filter Process definition filter; should not be <code>null</code>
191: * (use {@link PackageLocalHome#findAll} instead).
192: * @return A collection of matching packages.
193: * @ejb:home-method
194: * @see PackageLocalHome#findAll
195: */
196: public Collection ejbHomeXfindByFilter(WMFilter filter)
197: throws FinderException, RepositoryException {
198:
199: try {
200: List pkgs = PackageDAO.getInstance().findByFilter(filter);
201: PackageLocalHome home = (PackageLocalHome) _ctx
202: .getEJBLocalHome();
203: for (int i = 0; i < pkgs.size(); i++) {
204: try {
205: pkgs.set(i, home.findByPrimaryKey((String) pkgs
206: .get(i)));
207: } catch (ObjectNotFoundException e) {
208: // Someone else deleted this PK, so just skip it.
209: pkgs.remove(i--);
210: }
211: }
212: return pkgs;
213: } catch (SQLException e) {
214: throw new RepositoryException(e);
215: }
216: }
217:
218: /**
219: * @ejb:interface-method
220: * @ejb:persistence column-name="PACKAGEID"
221: */
222: public abstract String getPackageId();
223:
224: /**
225: * @ejb:persistence column-name="PACKAGEID"
226: */
227: public abstract void setPackageId(String packageId);
228:
229: /**
230: * @ejb:persistence column-name="NAME"
231: */
232: public abstract String getName();
233:
234: /**
235: * @ejb:persistence column-name="NAME"
236: */
237: public abstract void setName(String name);
238:
239: /**
240: * @ejb:persistence column-name="DESCRIPTION"
241: */
242: public abstract String getDescription();
243:
244: /**
245: * @ejb:persistence column-name="DESCRIPTION"
246: */
247: public abstract void setDescription(String description);
248:
249: /**
250: * @ejb:persistence column-name="VERSION"
251: */
252: public abstract String getVersion();
253:
254: /**
255: * @ejb:persistence column-name="VERSION"
256: */
257: public abstract void setVersion(String version);
258:
259: /**
260: * @ejb:persistence column-name="AUTHOR"
261: */
262: public abstract String getAuthor();
263:
264: /**
265: * @ejb:persistence column-name="AUTHOR"
266: */
267: public abstract void setAuthor(String author);
268:
269: /**
270: * @ejb:persistence column-name="VENDOR"
271: */
272: public abstract String getVendor();
273:
274: /**
275: * @ejb:persistence column-name="VENDOR"
276: */
277: public abstract void setVendor(String vendor);
278:
279: /**
280: * @ejb:persistence column-name="XPDLPACKAGE"
281: */
282: public abstract XPDLPackage getPackageCmp();
283:
284: /**
285: * @ejb:persistence column-name="XPDLPACKAGE"
286: */
287: public abstract void setPackageCmp(XPDLPackage pkg);
288:
289: /**
290: * @ejb:interface-method
291: */
292: public XPDLPackage getXPDLPackageBean() {
293: return getPackageCmp();
294: }
295:
296: /**
297: * @ejb:relation name="Package-ProcessDefinition"
298: * role-name="Package-Has-ProcessDefinitions"
299: */
300: public abstract Collection getProcessDefinitionsCmr();
301:
302: public abstract void setProcessDefinitionsCmr(Collection procDefs);
303:
304: /**
305: * @ejb:interface-method
306: */
307: public void addProcessDefinition(WorkflowProcess workflow)
308: throws CreateException {
309:
310: ProcessDefinitionLocal procDef = EJBLocalHelper
311: .getProcessDefinitionHome().create(workflow);
312: getProcessDefinitionsCmr().add(procDef);
313: }
314:
315: /**
316: * @ejb:interface-method
317: */
318: public void setXPDLPackageBean(XPDLPackage pkg)
319: throws RepositoryException {
320: try {
321: update(pkg, false);
322: } catch (CreateException e) {
323: throw new RepositoryException(e);
324: } catch (RemoveException e) {
325: throw new RepositoryException(e);
326: }
327: }
328:
329: /**
330: * @ejb:interface-method
331: */
332: public void setProcessDefinitionState(String processDefinitionId,
333: int state) {
334:
335: XPDLPackage pkg = getPackageCmp();
336: WorkflowProcess workflow = pkg
337: .getWorkflowProcess(processDefinitionId);
338: if (workflow != null) {
339: workflow.setState(state);
340:
341: // Force JBoss to update CMP field by changing the contents.
342: setPackageCmp(null);
343: setPackageCmp(pkg);
344: }
345: }
346:
347: private void update(XPDLPackage pkg, boolean creating)
348: throws RemoveException, CreateException {
349:
350: if (!getPackageId().equals(pkg.getId()))
351: throw new IllegalArgumentException(
352: "XPDLPackage ID mismatch");
353:
354: // Update the package fields.
355: setName(pkg.getName());
356: setDescription(pkg.getPackageHeader().getDescription());
357: setVendor(pkg.getPackageHeader().getVendor());
358: RedefinableHeader rhdr = pkg.getRedefinableHeader();
359: if (rhdr != null) {
360: setAuthor(rhdr.getAuthor());
361: setVersion(rhdr.getVersion());
362: }
363: setPackageCmp(pkg);
364:
365: // For each workflow process, create or update the corresponding
366: // process definition record.
367: WorkflowProcess[] workflows = pkg.getWorkflowProcess();
368: if (creating) {
369: // Create a ProcessDefinition record for each workflow process in
370: // the package.
371: for (int i = 0; i < workflows.length; i++)
372: addProcessDefinition(workflows[i]);
373: } else {
374: // Update any existing process definitions from this package and
375: // remove any that exist on disk but not in the package.
376: // N.B. It is necessary to make a static copy of the collection,
377: // because calling remove() on a CMR collection member has the side
378: // effect of removing that member from the CMR collection; under
379: // JBoss, the CMR collection iterator doesn't compensate for this
380: // and as a consequence cannot be used to remove the members.
381: Collection procDefColl = getProcessDefinitionsCmr();
382: ProcessDefinitionLocal[] procDefs = (ProcessDefinitionLocal[]) procDefColl
383: .toArray(new ProcessDefinitionLocal[procDefColl
384: .size()]);
385: for (int i = 0; i < workflows.length; i++) {
386: String wfId = workflows[i].getId();
387: boolean exists = false;
388: for (int j = 0; j < procDefs.length; j++) {
389: if (wfId.equals(procDefs[j]
390: .getProcessDefinitionId())) {
391: procDefs[j].update(workflows[i]);
392: exists = true;
393: break;
394: }
395: }
396: if (!exists)
397: addProcessDefinition(workflows[i]);
398: }
399: for (int i = 0; i < procDefs.length; i++) {
400: String procDefId = procDefs[i].getProcessDefinitionId();
401: boolean exists = false;
402: for (int j = 0; j < workflows.length; j++) {
403: if (workflows[j].getId().equals(procDefId)) {
404: exists = true;
405: break;
406: }
407: }
408: if (!exists)
409: procDefs[i].remove();
410: }
411: }
412: }
413:
414: protected final Log getLogger() {
415: return _logger;
416: }
417: }
|