View Javadoc

1   /*******************************************************************************
2    *  Imixs Workflow 
3    *  Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,  
4    *  http://www.imixs.com
5    *  
6    *  This program is free software; you can redistribute it and/or 
7    *  modify it under the terms of the GNU General Public License 
8    *  as published by the Free Software Foundation; either version 2 
9    *  of the License, or (at your option) any later version.
10   *  
11   *  This program is distributed in the hope that it will be useful, 
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
14   *  General Public License for more details.
15   *  
16   *  You can receive a copy of the GNU General Public
17   *  License at http://www.gnu.org/licenses/gpl.html
18   *  
19   *  Project: 
20   *  	http://www.imixs.org
21   *  	http://java.net/projects/imixs-workflow
22   *  
23   *  Contributors:  
24   *  	Imixs Software Solutions GmbH - initial API and implementation
25   *  	Ralph Soika - Software Developer
26   *******************************************************************************/
27  
28  package org.imixs.workflow.jee.ejb;
29  
30  import java.io.InputStream;
31  import java.io.Serializable;
32  import java.util.ArrayList;
33  import java.util.Collection;
34  import java.util.Collections;
35  import java.util.Comparator;
36  import java.util.HashMap;
37  import java.util.Iterator;
38  import java.util.List;
39  import java.util.logging.Logger;
40  
41  import javax.annotation.PostConstruct;
42  import javax.annotation.Resource;
43  import javax.annotation.security.DeclareRoles;
44  import javax.annotation.security.RolesAllowed;
45  import javax.ejb.EJB;
46  import javax.ejb.LocalBean;
47  import javax.ejb.SessionContext;
48  import javax.ejb.Stateless;
49  import javax.xml.bind.JAXBContext;
50  import javax.xml.bind.JAXBException;
51  import javax.xml.bind.Unmarshaller;
52  
53  import org.imixs.workflow.ExtendedModel;
54  import org.imixs.workflow.ItemCollection;
55  import org.imixs.workflow.exceptions.AccessDeniedException;
56  import org.imixs.workflow.exceptions.ModelException;
57  import org.imixs.workflow.jee.jpa.EntityIndex;
58  import org.imixs.workflow.xml.EntityCollection;
59  import org.imixs.workflow.xml.XMLItemCollection;
60  import org.imixs.workflow.xml.XMLItemCollectionAdapter;
61  
62  /**
63   * The ModelManager is independend form the IX JEE Entity EJBs and uses the
64   * standard IntemCollection Object as a data transfer object to comunitcate with
65   * clients.
66   * 
67   * 
68   * Since Version 1.7.0
69   * 
70   * The Implementation handles multiple model versions. Different Versions of an
71   * Model Entity can be saved and updated. The Getter methods can be furthermore
72   * Controlled by providing a valid Model Version. If no model version is set
73   * this Implementation automatically defaults to the highest available
74   * ModelVersion
75   * 
76   * @see org.imixs.workflow.ModelManager
77   * @see org.imixs.workflow.jee.ejb.ModelManager
78   * @author rsoika
79   * 
80   */
81  @DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS",
82  		"org.imixs.ACCESSLEVEL.READERACCESS",
83  		"org.imixs.ACCESSLEVEL.AUTHORACCESS",
84  		"org.imixs.ACCESSLEVEL.EDITORACCESS",
85  		"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
86  @RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS",
87  		"org.imixs.ACCESSLEVEL.READERACCESS",
88  		"org.imixs.ACCESSLEVEL.AUTHORACCESS",
89  		"org.imixs.ACCESSLEVEL.EDITORACCESS",
90  		"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
91  @Stateless
92  @LocalBean
93  public class ModelService implements ExtendedModel {
94  
95  	@EJB
96  	EntityService entityService;
97  
98  	@Resource
99  	SessionContext ctx;
100 
101 	private static Logger logger = Logger.getLogger("org.imixs.workflow");
102 
103 	@PostConstruct
104 	void initIndex() throws AccessDeniedException {
105 		// create necessary index entities
106 		entityService.addIndex("numProcessID", EntityIndex.TYP_INT);
107 		entityService.addIndex("numActivityID", EntityIndex.TYP_INT);
108 		entityService.addIndex("$modelversion", EntityIndex.TYP_TEXT);
109 		entityService.addIndex("Type", EntityIndex.TYP_TEXT);
110 		entityService.addIndex("txtname", EntityIndex.TYP_TEXT);
111 		entityService.addIndex("txtworkflowgroup", EntityIndex.TYP_TEXT);
112 	}
113 
114 	/**
115 	 * This method returns the ActivityEntity with the highest Version number
116 	 * ($modelversion)
117 	 */
118 	public ItemCollection getActivityEntity(int processid, int activityid)
119 			throws ModelException {
120 
121 		return findActivityEntity(processid, activityid,
122 				this.getLatestVersion());
123 
124 	}
125 
126 	public ItemCollection getActivityEntityByVersion(int processid,
127 			int activityid, String modelVersion) throws ModelException {
128 		return findActivityEntity(processid, activityid, modelVersion);
129 	}
130 
131 	public ItemCollection getProcessEntity(int processid) throws ModelException {
132 		return findProcessEntity(processid, this.getLatestVersion());
133 	}
134 
135 	public ItemCollection getProcessEntityByVersion(int processid,
136 			String modelversion) throws ModelException {
137 
138 		return findProcessEntity(processid, modelversion);
139 	}
140 
141 	/**
142 	 * returns a collection of ItemCollections representing the model activity
143 	 * Entities for the coresponding processid
144 	 * 
145 	 * @throws ModelException
146 	 * 
147 	 */
148 	public Collection<ItemCollection> getActivityEntityList(int processid)
149 			throws ModelException {
150 
151 		String sQuery = null;
152 		sQuery = "SELECT ";
153 		sQuery += " wi FROM Entity AS wi" + " JOIN wi.integerItems as i1 "
154 				+ " JOIN wi.integerItems as i2 " + " JOIN wi.textItems AS v"
155 				+ " WHERE wi.type= 'ActivityEntity' "
156 				+ " AND i1.itemName = 'numprocessid' AND i1.itemValue = '"
157 				+ processid + "' " + " AND i2.itemName = 'numactivityid' "
158 				+ " AND v.itemName = '$modelversion' AND v.itemValue = '"
159 				+ this.getLatestVersion() + "'" + " ORDER BY i2.itemValue ASC";
160 		return entityService.findAllEntities(sQuery, 0, -1);
161 
162 	}
163 
164 	public Collection<ItemCollection> getActivityEntityListByVersion(
165 			int processid, String aModelVersion) throws ModelException {
166 
167 		String sQuery = null;
168 		sQuery = "SELECT";
169 		sQuery += " wi FROM Entity AS wi" + " JOIN wi.integerItems as i1 "
170 				+ " JOIN wi.integerItems as i2 " + " JOIN wi.textItems AS v"
171 				+ " WHERE wi.type= 'ActivityEntity' "
172 				+ " AND i1.itemName = 'numprocessid' AND i1.itemValue = '"
173 				+ processid + "' " + " AND i2.itemName = 'numactivityid' "
174 				+ " AND v.itemName = '$modelversion' AND v.itemValue = '"
175 				+ aModelVersion + "'" + " ORDER BY i2.itemValue ASC";
176 		return entityService.findAllEntities(sQuery, 0, -1);
177 
178 	}
179 
180 	/**
181 	 * returns a collection of ItemCollections representing the model process
182 	 * Entities
183 	 * 
184 	 */
185 	public Collection<ItemCollection> getProcessEntityList()
186 			throws ModelException {
187 
188 		String sQuery = null;
189 		sQuery = "SELECT";
190 		sQuery += " wi FROM Entity AS wi " + " JOIN wi.integerItems as i  "
191 				+ " JOIN wi.textItems AS v"
192 				+ " WHERE wi.type= 'ProcessEntity' "
193 				+ " AND i.itemName = 'numprocessid' "
194 				+ " AND v.itemName = '$modelversion' AND v.itemValue = '"
195 				+ getLatestVersion() + "'" + " ORDER BY i.itemValue ASC";
196 
197 		Collection<ItemCollection> col = entityService.findAllEntities(sQuery,
198 				0, -1);
199 		return col;
200 	}
201 
202 	/**
203 	 * returns a collection of ItemCollections representing the model process
204 	 * Entities
205 	 * 
206 	 */
207 	public Collection<ItemCollection> getProcessEntityListByVersion(
208 			String aModelVersion) throws ModelException {
209 
210 		String sQuery = null;
211 		sQuery = "SELECT";
212 		sQuery += " wi FROM Entity AS wi " + " JOIN wi.integerItems as i  "
213 				+ " JOIN wi.textItems AS v"
214 				+ " WHERE wi.type= 'ProcessEntity' "
215 				+ " AND i.itemName = 'numprocessid' "
216 				+ " AND v.itemName = '$modelversion' AND v.itemValue = '"
217 				+ aModelVersion + "'" + " ORDER BY i.itemValue ASC";
218 
219 		return entityService.findAllEntities(sQuery, 0, -1);
220 
221 	}
222 
223 	public Collection<ItemCollection> getEnvironmentEntityList()
224 			throws ModelException {
225 
226 		String sQuery = null;
227 		sQuery = "SELECT";
228 		sQuery += " wi FROM Entity as wi "
229 				+ " WHERE wi.type= 'WorkflowEnvironmentEntity'";
230 
231 		return entityService.findAllEntities(sQuery, 0, -1);
232 
233 	}
234 
235 	public Collection<ItemCollection> getEnvironmentEntityListByVersion(
236 			String modelversion) throws ModelException {
237 
238 		String sQuery = null;
239 		sQuery = "SELECT";
240 		sQuery += " environment FROM Entity AS environment "
241 				+ " JOIN environment.textItems as v"
242 				+ " WHERE environment.type = 'WorkflowEnvironmentEntity'"
243 				+ " AND v.itemName = '$modelversion' AND v.itemValue = '"
244 				+ modelversion + "'";
245 		return entityService.findAllEntities(sQuery, 0, -1);
246 
247 	}
248 
249 	/**
250 	 * Saves or updates an ActivityEntity represented by an ItemCollection. The
251 	 * Entity is unique identified of its Attributes 'numProcessID',
252 	 * 'numActivityID' and '$modelversion' The Method verifies that an existing
253 	 * instance will be updated.
254 	 * 
255 	 * @param ic
256 	 * 
257 	 * @throws AccessDeniedException
258 	 * @throws Exception
259 	 */
260 	public void saveActivityEntity(ItemCollection ic) throws ModelException,
261 			AccessDeniedException {
262 		int processid = ic.getItemValueInteger("numProcessID");
263 		if (processid <= 0)
264 			throw new ModelException("invalid ProcessEntity");
265 
266 		int activityid = ic.getItemValueInteger("numActivityID");
267 		if (activityid <= 0)
268 			throw new ModelException("invalid ActivityEntity id: " + activityid);
269 
270 		ic.replaceItemValue("Type", "ActivityEntity");
271 
272 		entityService.save(ic);
273 
274 	}
275 
276 	/**
277 	 * Saves or updates a ProcessEntitiy represented by an ItemCollection. The
278 	 * Entity is unique identified of its Attributes 'numProcessID' and
279 	 * '$modelversion' The Method verifies that an existing instance will be
280 	 * updated.
281 	 * 
282 	 * @param ic
283 	 * 
284 	 * @throws AccessDeniedException
285 	 * @throws ModelException
286 	 */
287 	public void saveProcessEntity(ItemCollection ic) throws ModelException,
288 			AccessDeniedException {
289 		// Verify existing Instance
290 		int processid = ic.getItemValueInteger("numProcessID");
291 		if (processid <= 0)
292 			throw new ModelException("invalid ProcessEntity: " + processid);
293 
294 		ic.replaceItemValue("Type", "ProcessEntity");
295 		entityService.save(ic);
296 
297 	}
298 
299 	/**
300 	 * Saves or updates an EnvironmentEntity represented by an ItemCollection.
301 	 * The Entity is unique identified of its Attributes 'txtName' and
302 	 * '$modelversion' The Method verifies that an existing instance will be
303 	 * updated.
304 	 * 
305 	 * @param ic
306 	 * @throws AccessDeniedException
307 	 * @throws Exception
308 	 */
309 	public void saveEnvironmentEntity(ItemCollection ic) throws ModelException,
310 			AccessDeniedException {
311 		ic.replaceItemValue("Type", "WorkflowEnvironmentEntity");
312 
313 		entityService.save(ic);
314 
315 	}
316 
317 	/**
318 	 * This method removes a spcific ModelVersion defined by param
319 	 * 'modelversion'. If modelVersion is null the method will remove all models
320 	 * 
321 	 * @throws AccessDeniedException
322 	 */
323 	public void removeModelVersion(String modelversion) throws ModelException,
324 			AccessDeniedException {
325 		// remove all existing entities
326 		String sQuery = null;
327 		sQuery = "SELECT";
328 		if (modelversion != null) {
329 			// select model entities for this specific version
330 			sQuery += " entity FROM Entity AS entity "
331 					+ " JOIN entity.textItems as v"
332 					+ " WHERE entity.type IN ('ProcessEntity', 'ActivityEntity', 'WorkflowEnvironmentEntity')"
333 					+ " AND v.itemName = '$modelversion' AND v.itemValue = '"
334 					+ modelversion + "'";
335 		} else {
336 			// select all model entities
337 			sQuery += " entity FROM Entity AS entity "
338 					+ " WHERE entity.type IN ('ProcessEntity', 'ActivityEntity', 'WorkflowEnvironmentEntity')";
339 		}
340 		Collection<ItemCollection> col = entityService.findAllEntities(sQuery,
341 				0, -1);
342 		Iterator<ItemCollection> it = col.iterator();
343 		while (it.hasNext()) {
344 			entityService.remove(it.next());
345 		}
346 
347 	}
348 
349 	/**
350 	 * This mehtode finds a ProcessEntity identified by its processid
351 	 * (numProcessid) and model version ($modelVersion)
352 	 * 
353 	 * @param processid
354 	 *            , modelversion
355 	 * @return returns null if no entity was found
356 	 * @throws Exception
357 	 */
358 	@SuppressWarnings("unused")
359 	private ItemCollection findEnvironmentEntity(String name,
360 			String modelversion) throws ModelException {
361 		String sQuery = null;
362 		sQuery = "SELECT";
363 		sQuery += " environment FROM Entity AS environment "
364 				+ " JOIN environment.textItems AS n "
365 				+ " JOIN environment.textItems as v"
366 				+ " WHERE environment.type = 'WorkflowEnvironmentEntity'"
367 				+ " AND n.itemName = 'txtName' and n.itemValue = '" + name
368 				+ "'" + " AND v.itemName = '$modelversion' AND v.itemValue = '"
369 				+ modelversion + "'";
370 
371 		Collection<ItemCollection> col = entityService.findAllEntities(sQuery,
372 				0, 1);
373 		Iterator<ItemCollection> it = col.iterator();
374 		if (!it.hasNext())
375 			return null;
376 
377 		return col.iterator().next();
378 	}
379 
380 	/**
381 	 * This method finds a ProcessEntity identified by its processid
382 	 * (numProcessid) and model version ($modelVersion)
383 	 * 
384 	 * @param processid
385 	 *            , modelversion
386 	 * @return returns null if no entity was found
387 	 * @throws Exception
388 	 */
389 	private ItemCollection findProcessEntity(int processid, String modelversion)
390 			throws ModelException {
391 		String sQuery = null;
392 		sQuery = "SELECT";
393 		sQuery += " process FROM Entity AS process "
394 				+ " JOIN process.integerItems AS i "
395 				+ " JOIN process.textItems as v"
396 				+ " WHERE process.type = 'ProcessEntity'"
397 				+ " AND i.itemName = 'numprocessid' and i.itemValue = '"
398 				+ processid + "'"
399 				+ " AND v.itemName = '$modelversion' AND v.itemValue = '"
400 				+ modelversion + "'";
401 
402 		Collection<ItemCollection> col = entityService.findAllEntities(sQuery,
403 				0, 1);
404 		Iterator<ItemCollection> it = col.iterator();
405 		if (!it.hasNext())
406 			return null;
407 
408 		return col.iterator().next();
409 	}
410 
411 	/**
412 	 * This method finds a ActivityEntity identified by its processid,
413 	 * activityID (numProcessid, numActivityID) and model version
414 	 * ($modelVersion)
415 	 * 
416 	 * @param processid
417 	 *            , modelversion
418 	 * @return returns null if no entity was found
419 	 * @throws Exception
420 	 */
421 	private ItemCollection findActivityEntity(int processid, int activityid,
422 			String modelversion) throws ModelException {
423 		String sQuery = null;
424 		sQuery = "SELECT";
425 		sQuery += " activity FROM Entity as activity "
426 				+ " JOIN activity.integerItems as i "
427 				+ " JOIN activity.integerItems as i2 "
428 				+ " JOIN activity.textItems as v"
429 				+ " WHERE activity.type = 'ActivityEntity'"
430 				+ " AND i.itemName = 'numprocessid' " + " AND i.itemValue = '"
431 				+ processid + "'"
432 				+ " AND i2.itemName = 'numactivityid' and i2.itemValue = '"
433 				+ activityid + "' "
434 				+ " AND v.itemName = '$modelversion' AND v.itemValue = '"
435 				+ modelversion + "'";
436 
437 		Collection<ItemCollection> col = entityService.findAllEntities(sQuery,
438 				0, 1);
439 		Iterator<ItemCollection> it = col.iterator();
440 		if (!it.hasNext())
441 			return null;
442 
443 		return col.iterator().next();
444 	}
445 
446 	/**
447 	 * This helper method finds the highest Model Version available in the
448 	 * system. Returns an empty String if no version was found!
449 	 * 
450 	 * @return String with the latest model version
451 	 */
452 	public String getLatestVersion() throws ModelException {
453 		String sQuery = "SELECT process FROM Entity AS process"
454 				+ " JOIN process.textItems as v"
455 				+ " JOIN process.textItems as n"
456 				+ " WHERE process.type = 'WorkflowEnvironmentEntity'"
457 				+ " AND n.itemName = 'txtname' AND n.itemValue = 'environment.profile'"
458 				+ " AND v.itemName='$modelversion' "
459 				+ " ORDER BY v.itemValue DESC";
460 
461 		Collection<ItemCollection> col = entityService.findAllEntities(sQuery,
462 				0, 1);
463 
464 		if (col.size() > 0) {
465 			Iterator<ItemCollection> iter = col.iterator();
466 			String sModelVersion = iter.next().getItemValueString(
467 					"$modelversion");
468 			return sModelVersion;
469 		} else
470 			throw new ModelException(
471 					"[ModelService] no model definition found!");
472 	}
473 
474 	/**
475 	 * returns a String list of all accessible Modelversions
476 	 * 
477 	 * @return
478 	 */
479 	public List<String> getAllModelVersions() {
480 		ArrayList<String> colVersions = new ArrayList<String>();
481 
482 		try {
483 			Collection<ItemCollection> colEntities = getEnvironmentEntityList();
484 			for (ItemCollection aworkitem : colEntities) {
485 				String sName = aworkitem.getItemValueString("txtName");
486 				String sVersion = aworkitem.getItemValueString("$modelversion");
487 				if ("environment.profile".equals(sName)) {
488 					if (colVersions.indexOf(sVersion) == -1)
489 						colVersions.add(sVersion);
490 				}
491 
492 			}
493 		} catch (Exception e) {
494 			e.printStackTrace();
495 		}
496 		return colVersions;
497 	}
498 
499 	/**
500 	 * returns all the activities in a list for a corresponding process entity
501 	 * The method returns only Activities where keypublicresult != "0"
502 	 * 
503 	 * @return List<ItemCollection> of activity Entities
504 	 */
505 	public List<ItemCollection> getPublicActivities(int aprocessid) {
506 
507 		return getPublicActivitiesByVersion(aprocessid, null);
508 	}
509 
510 	/**
511 	 * returns all the activities in a list for a corresponding process entity
512 	 * The method returns only Activities where keypublicresult != "0"
513 	 * 
514 	 * @return List<ItemCollection> of activity Entities
515 	 */
516 	public List<ItemCollection> getPublicActivitiesByVersion(int aprocessid,
517 			String version) {
518 		ArrayList<ItemCollection> colActivities = null;
519 		try {
520 			// read activites from workflowmanager
521 			Collection<ItemCollection> colEntities;
522 
523 			if (version != null)
524 				colEntities = getActivityEntityListByVersion(aprocessid,
525 						version);
526 			else
527 				colEntities = getActivityEntityList(aprocessid);
528 
529 			colActivities = new ArrayList<ItemCollection>();
530 			for (ItemCollection aworkitem : colEntities) {
531 				// ad only activities with usercontrolled != No
532 				if (!"0".equals(aworkitem.getItemValueString("keypublicresult")))
533 					colActivities.add(aworkitem);
534 			}
535 		} catch (Exception e) {
536 			e.printStackTrace();
537 		}
538 		return colActivities;
539 	}
540 
541 	/**
542 	 * returns a String list of all existing ProcessGroup Names
543 	 * 
544 	 * @return
545 	 */
546 	public List<String> getAllWorkflowGroups() {
547 		ArrayList<String> colGroups = new ArrayList<String>();
548 
549 		try {
550 			Collection<ItemCollection> colEntities = getProcessEntityList();
551 
552 			for (ItemCollection aworkitem : colEntities) {
553 				String sGroup = aworkitem
554 						.getItemValueString("txtworkflowgroup");
555 				if (colGroups.indexOf(sGroup) == -1)
556 					colGroups.add(sGroup);
557 			}
558 		} catch (Exception e) {
559 			e.printStackTrace();
560 		}
561 		return colGroups;
562 	}
563 
564 	public List<String> getAllWorkflowGroupsByVersion(String version) {
565 		ArrayList<String> colGroups = new ArrayList<String>();
566 
567 		try {
568 			Collection<ItemCollection> colEntities = getProcessEntityListByVersion(version);
569 
570 			for (ItemCollection aworkitem : colEntities) {
571 				String sGroup = aworkitem
572 						.getItemValueString("txtworkflowgroup");
573 				if (colGroups.indexOf(sGroup) == -1)
574 					colGroups.add(sGroup);
575 			}
576 		} catch (Exception e) {
577 			e.printStackTrace();
578 		}
579 		return colGroups;
580 	}
581 
582 	/**
583 	 * returns a list of all ProcessEntities which are the first one in each
584 	 * ProcessGroup. So for each ProcessGroup the ProcessEntity with the lowest
585 	 * processID will be returned. The method builds a cash with the best
586 	 * (lowest) ProcessID for each process group.
587 	 * 
588 	 * @return
589 	 */
590 	public List<ItemCollection> getAllStartProcessEntities() {
591 		return getAllStartProcessEntitiesByVersion(null);
592 	}
593 
594 	/**
595 	 * returns a list of all ProcessEntities which are the first one in each
596 	 * ProcessGroup. The ModelVersion specifies the Model to be analiezed.
597 	 * 
598 	 * So for each ProcessGroup the ProcessEntity with the lowest processID will
599 	 * be returned. The method builds a cash with the best (lowest) ProcessID
600 	 * for each process group.
601 	 * 
602 	 * The collection returned will be sorted by the numProcessID
603 	 * 
604 	 * @return
605 	 */
606 	public List<ItemCollection> getAllStartProcessEntitiesByVersion(
607 			String version) {
608 		HashMap<String, ItemCollection> cashBestProcessID = new HashMap<String, ItemCollection>();
609 		ArrayList<ItemCollection> colStartProcessEntities = new ArrayList<ItemCollection>();
610 
611 		try {
612 			// As the process Entity List can be unordered each processEntity
613 			// will be checked for the lowest ProcessID
614 			Collection<ItemCollection> colEntities;
615 			if (version == null)
616 				colEntities = getProcessEntityList();
617 			else
618 				colEntities = getProcessEntityListByVersion(version);
619 
620 			for (ItemCollection processEntity : colEntities) {
621 				String sGroup = processEntity
622 						.getItemValueString("txtworkflowgroup");
623 				Integer iProcessID = processEntity
624 						.getItemValueInteger("numProcessID");
625 
626 				// check if processid is lower as the current best id
627 				Integer iBestProcessID = null;
628 				ItemCollection itemColBestProcess = cashBestProcessID
629 						.get(sGroup);
630 				if (itemColBestProcess != null)
631 					iBestProcessID = itemColBestProcess
632 							.getItemValueInteger("numProcessID");
633 				if (iBestProcessID == null
634 						|| iProcessID.intValue() < iBestProcessID.intValue()) {
635 					cashBestProcessID.put(sGroup, processEntity);
636 				}
637 
638 			}
639 		} catch (Exception e) {
640 			e.printStackTrace();
641 		}
642 
643 		// now return the arrayList of all collected best processEntities
644 		for (ItemCollection bestProcessEntity : cashBestProcessID.values()) {
645 			colStartProcessEntities.add(bestProcessEntity);
646 		}
647 
648 		// sort processEntites by numProcessID
649 		Collections
650 				.sort(colStartProcessEntities, new ProcessEntityComparator());
651 
652 		return colStartProcessEntities;
653 	}
654 
655 	/**
656 	 * returns a list of all ProcessEntities for a specific ProcessGroup
657 	 * independent from the modelversion. The same workflowGroup contained in to
658 	 * different model versions will be returned twice.
659 	 * 
660 	 * @return
661 	 */
662 	public List<ItemCollection> getAllProcessEntitiesByGroup(String aGroup) {
663 		return getAllProcessEntitiesByGroupByVersion(aGroup, null);
664 	}
665 
666 	/**
667 	 * returns a list of all ProcessEntities for a specific ProcessGroup and
668 	 * modelversion.
669 	 * 
670 	 * @param aGroup
671 	 * @param aversion
672 	 * @return
673 	 */
674 	public List<ItemCollection> getAllProcessEntitiesByGroupByVersion(
675 			String aGroup, String aversion) {
676 		ArrayList<ItemCollection> processList = new ArrayList<ItemCollection>();
677 
678 		String sQuery = "SELECT DISTINCT process FROM Entity AS process "
679 				+ " JOIN process.textItems AS t2"
680 				+ " JOIN process.integerItems AS t3"
681 				+ " WHERE process.type= 'ProcessEntity'"
682 				+ " AND t2.itemName = 'txtworkflowgroup' "
683 				+ " AND t2.itemValue = '" + aGroup + "' "
684 				+ " AND t3.itemName = 'numprocessid'"
685 				+ " ORDER BY t3.itemValue asc";
686 
687 		Collection<ItemCollection> col = entityService.findAllEntities(sQuery,
688 				0, -1);
689 
690 		for (ItemCollection aworkitem : col) {
691 			// test if version matches
692 			String sVersion = aworkitem.getItemValueString("$ModelVersion");
693 			if (aversion != null && !aversion.equals(sVersion))
694 				continue;
695 
696 			processList.add(aworkitem);
697 		}
698 		return processList;
699 	}
700 
701 	/**
702 	 * This method imports a workflow model file from an imputStream object. The
703 	 * method can be used to initialize a workflow system with a workflow model
704 	 * or provide an update service to import new model files without the need
705 	 * to use the Imixs RESTservice api.
706 	 * 
707 	 * The expected file format of the model file is based on the Imixs JAX-B
708 	 * XMLItemCollection.
709 	 * 
710 	 * The file may only contain one modelVersion! The ModelVersion is
711 	 * Identified by the entity type 'WorkflowEnvironmentEntity'. If more than
712 	 * one WorkflowEnvironmentEntity is found the method throws an
713 	 * ModelException.
714 	 * 
715 	 * The method automatically removes an old existing model version.
716 	 * 
717 	 * @throws AccessDeniedException
718 	 *             - if user is not allowed to remove old model
719 	 * @throws ModelException
720 	 *             - if fileformat is invalid
721 	 */
722 	public void importModel(InputStream input) throws ModelException,
723 			AccessDeniedException {
724 		XMLItemCollection entity;
725 		ItemCollection itemCollection;
726 		String sModelVersion = null;
727 
728 		if (input == null)
729 			return;
730 
731 		EntityCollection ecol = null;
732 		logger.info("[ModelService] importModel, verifing file content....");
733 
734 		JAXBContext context;
735 		Object jaxbObject = null;
736 		// unmarshall the model file
737 		try {
738 			context = JAXBContext.newInstance(EntityCollection.class);
739 			Unmarshaller m = context.createUnmarshaller();
740 			jaxbObject = m.unmarshal(input);
741 		} catch (JAXBException e) {
742 			throw new ModelException(
743 					"[ModelService] error - wrong xml file format - unable to import model file: ",
744 					e);
745 		}
746 		if (jaxbObject == null)
747 			throw new ModelException(
748 					"[ModelService] error - wrong xml file format - unable to import model file!");
749 
750 		ecol = (EntityCollection) jaxbObject;
751 		// import the model entities....
752 		if (ecol.getEntity().length > 0) {
753 
754 			/*
755 			 * first iterate over all entities and find the
756 			 * WorkflowEnvironmentEntity. The method expects a model file with
757 			 * exactly one instance of WorkflowEnvironmentEntity. Otherwise an
758 			 * exception is thrown!
759 			 */
760 			for (XMLItemCollection aentity : ecol.getEntity()) {
761 				itemCollection = XMLItemCollectionAdapter
762 						.getItemCollection(aentity);
763 				// test for WorkflowEnvironmentEntity
764 				if ("WorkflowEnvironmentEntity".equals(itemCollection
765 						.getItemValueString("type"))
766 						&& "environment.profile".equals(itemCollection
767 								.getItemValueString("txtName"))) {
768 
769 					if (sModelVersion != null)
770 						throw new ModelException(
771 								"[ModelService] error importModel - file contains more than one modelversion!");
772 
773 					sModelVersion = itemCollection
774 							.getItemValueString("$ModelVersion");
775 
776 				}
777 			}
778 
779 			if (sModelVersion == null)
780 				throw new ModelException(
781 						"[ModelService] error importModel - file did "
782 								+ "not contain a environment.profile entity with a valid modelversion!");
783 
784 			// now remove old model entries....
785 			logger.info("[ModelService] removing existing configuration for model version '"
786 					+ sModelVersion + "'");
787 			removeModelVersion(sModelVersion);
788 
789 			// save new entities into database and update modelversion for all
790 			// entities.....
791 			for (int i = 0; i < ecol.getEntity().length; i++) {
792 				entity = ecol.getEntity()[i];
793 				itemCollection = XMLItemCollectionAdapter
794 						.getItemCollection(entity);
795 
796 				// verify type and update the model version
797 				String sType = itemCollection.getItemValueString("Type");
798 				if ("ProcessEntity".equals(sType)
799 						|| "ActivityEntity".equals(sType)
800 						|| "WorkflowEnvironmentEntity".equals(sType)) {
801 					itemCollection.replaceItemValue("$modelVersion",
802 							sModelVersion);
803 					// save entity
804 					entityService.save(itemCollection);
805 				} else
806 					logger.warning("[ModelService] importModel: unsported entity type=" + sType + "!");
807 			}
808 
809 			logger.info("[ModelService] " + ecol.getEntity().length
810 					+ " model entries sucessfull imported");
811 		}
812 	}
813 
814 	/**
815 	 * compares two processEntities by its numProcessID
816 	 * 
817 	 * @author rsoika
818 	 * 
819 	 */
820 	static class ProcessEntityComparator implements Comparator<ItemCollection>,
821 			Serializable {
822 		private static final long serialVersionUID = 1L;
823 
824 		public int compare(ItemCollection o1, ItemCollection o2) {
825 			return (o1.getItemValueInteger("numProcessID") - o2
826 					.getItemValueInteger("numProcessID"));
827 
828 		}
829 
830 	}
831 
832 }