diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/event/CycleCompensatingEventListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/event/CycleCompensatingEventListener.java
new file mode 100644
index 0000000000..102549dc8b
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/event/CycleCompensatingEventListener.java
@@ -0,0 +1,11 @@
+package org.activiti.cycle.event;
+
+/**
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public interface CycleCompensatingEventListener extends CycleEventListener {
+
+ public void compensateEvent(T event);
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/event/CycleEventListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/event/CycleEventListener.java
new file mode 100644
index 0000000000..158c071289
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/event/CycleEventListener.java
@@ -0,0 +1,15 @@
+package org.activiti.cycle.event;
+
+import org.activiti.cycle.annotations.CycleComponent;
+
+/**
+ * An interface for EventListeners. Can be implemented by a
+ * {@link CycleComponent} to be notified of Cycle Events.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public interface CycleEventListener {
+
+ void onEvent(T event);
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/action/CreateUrlActionImpl.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/action/CreateUrlActionImpl.java
index e9a620073f..3ae1ee3601 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/action/CreateUrlActionImpl.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/action/CreateUrlActionImpl.java
@@ -2,6 +2,8 @@ package org.activiti.cycle.impl.action;
import java.net.URL;
+import org.activiti.cycle.RepositoryArtifact;
+import org.activiti.cycle.RepositoryConnector;
import org.activiti.cycle.action.CreateUrlAction;
import org.activiti.cycle.impl.AbstractArtifactActionImpl;
@@ -21,5 +23,9 @@ public abstract class CreateUrlActionImpl extends AbstractArtifactActionImpl imp
public CreateUrlActionImpl(String actionId) {
super(actionId);
}
+
+ public String getWarning(RepositoryConnector connector, RepositoryArtifact repositoryArtifact) {
+ return null;
+}
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/action/fom/FormHandler.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/action/fom/FormHandler.java
index fecc6383a9..60df79a0c2 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/action/fom/FormHandler.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/action/fom/FormHandler.java
@@ -64,6 +64,8 @@ public class FormHandler {
getExpressions(inputElements, expressionMap);
NodeList textAreaElements = document.getElementsByTagName("textarea");
getExpressions(textAreaElements, expressionMap);
+ textAreaElements = document.getElementsByTagName("output");
+ getExpressions(textAreaElements, expressionMap);
return expressionMap;
}
@@ -86,8 +88,15 @@ public class FormHandler {
} else if ("checkbox".equals(currentElement.getAttribute("type"))) {
nameAttribute = currentElement.getAttribute("name");
valueAttribute = currentElement.getAttribute("property");
+ } else if ("hidden".equals(currentElement.getAttribute("type"))) {
+ nameAttribute = currentElement.getAttribute("name");
+ valueAttribute = currentElement.getAttribute("value");
}
}
+ if ("output".equals(currentElement.getTagName())) {
+ nameAttribute = currentElement.getAttribute("value");
+ valueAttribute = currentElement.getAttribute("value");
+ }
if ("checkbox".equals(currentElement.getTagName())) {
nameAttribute = currentElement.getAttribute("name");
valueAttribute = currentElement.getAttribute("property");
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/components/CycleEmailDispatcher.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/components/CycleEmailDispatcher.java
new file mode 100644
index 0000000000..1f9cf05381
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/components/CycleEmailDispatcher.java
@@ -0,0 +1,114 @@
+package org.activiti.cycle.impl.components;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.engine.ProcessEngines;
+import org.activiti.engine.impl.ProcessEngineImpl;
+import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.apache.commons.mail.Email;
+import org.apache.commons.mail.HtmlEmail;
+
+/**
+ * {@link CycleComponent} for asynchronous email-dispatching (extremely basic
+ * implementation)
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class CycleEmailDispatcher {
+
+ private static int QUEUE_SIZE = 100;
+
+ Logger logger = Logger.getLogger(CycleEmailDispatcher.class.getName());
+
+ private final BlockingQueue emailQueue = new ArrayBlockingQueue(QUEUE_SIZE);
+
+ private class EmailDto {
+ private String from, address, subject, message;
+ public String toString() {
+ return "Email from '"+from+"' to '" + address + "' with subject '" + subject + "'";
+ }
+ }
+
+ public void sendEmail(String from, String address, String subject, String message) {
+ EmailDto dto = new EmailDto();
+ dto.address = address;
+ dto.from = from;
+ dto.subject = subject;
+ dto.message = message;
+ sendEmail(dto);
+ }
+
+ private void sendEmail(EmailDto email) {
+ // if the queue is full, the email is simply not added.
+ emailQueue.offer(email);
+ startDispatchment();
+ }
+
+ public synchronized void startDispatchment() {
+ if (!emailDispatcherThread.isAlive()) {
+ try {
+ emailDispatcherThread.start();
+ } catch (Exception e) {
+ emailDispatcherThread = new EmailDispatcherThread();
+ emailDispatcherThread.start();
+ }
+ }
+ }
+
+ public synchronized void stopDispatchment() {
+ if (!emailDispatcherThread.isInterrupted())
+ emailDispatcherThread.interrupt();
+ }
+
+ private Thread emailDispatcherThread = new EmailDispatcherThread();
+
+ private class EmailDispatcherThread extends Thread {
+
+ public void run() {
+ while (!isInterrupted()) {
+ EmailDto mailDto = null;
+ try {
+ mailDto = emailQueue.take();
+ Email email = new HtmlEmail();
+ email.setFrom(mailDto.from);
+ email.setMsg(mailDto.message);
+ email.setSubject(mailDto.subject);
+ email.addTo(mailDto.address);
+ setMailServerProperties(email);
+ email.send();
+ } catch (InterruptedException e) {
+ // just terminate
+ return;
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Could not send " + mailDto, e);
+ // TODO: retry?
+ }
+ }
+ }
+ }
+
+ /* copied from MailActivityBehaviour in engine */
+ protected void setMailServerProperties(Email email) {
+ // for the moment, simply reuse activiti-engine mailconfiguration
+ ProcessEngineConfigurationImpl processEngineConfiguration = ((ProcessEngineImpl) ProcessEngines.getDefaultProcessEngine()).getProcessEngineConfiguration();
+
+ String host = processEngineConfiguration.getMailServerHost();
+ email.setHostName(host);
+
+ int port = processEngineConfiguration.getMailServerPort();
+ email.setSmtpPort(port);
+
+ String user = processEngineConfiguration.getMailServerUsername();
+ String password = processEngineConfiguration.getMailServerPassword();
+ if (user != null && password != null) {
+ email.setAuthentication(user, password);
+ }
+ }/* end copied from MailActivityBehaviour in engine */
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/components/RuntimeConnectorList.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/components/RuntimeConnectorList.java
index 5b00ad660a..65a6c9a7e5 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/components/RuntimeConnectorList.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/components/RuntimeConnectorList.java
@@ -1,17 +1,22 @@
package org.activiti.cycle.impl.components;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.activiti.cycle.CycleComponentFactory;
import org.activiti.cycle.RepositoryConnector;
import org.activiti.cycle.annotations.CycleComponent;
import org.activiti.cycle.context.CycleApplicationContext;
import org.activiti.cycle.context.CycleContextType;
import org.activiti.cycle.impl.connector.view.TagConnector;
+import org.activiti.cycle.impl.processsolution.connector.ProcessSolutionConnector;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.service.CycleServiceFactory;
@CycleComponent(context = CycleContextType.SESSION)
public class RuntimeConnectorList implements Serializable {
@@ -32,12 +37,24 @@ public class RuntimeConnectorList implements Serializable {
public synchronized RepositoryConnector getConnectorById(String id) {
init();
+
+ if (id.startsWith("ps-")) {
+ return new ProcessSolutionConnector(id.substring(3));
+ }
+
return connectors.get(id);
+
}
public synchronized List getConnectors() {
init();
- return connectorList;
+ List resultList = new ArrayList(connectorList);
+ // add virtual connectors for process solutions:
+ for (ProcessSolution processSolution : CycleServiceFactory.getProcessSolutionService().getProcessSolutions()) {
+ ProcessSolutionConnector psConnector = new ProcessSolutionConnector(processSolution.getId());
+ resultList.add(psConnector);
+ }
+ return resultList;
}
protected synchronized void init() {
@@ -56,6 +73,7 @@ public class RuntimeConnectorList implements Serializable {
// sort connector-list
Collections.sort(connectorList, new Comparator() {
+
public int compare(RepositoryConnector o1, RepositoryConnector o2) {
String name1 = o1.getName();
String name2 = o2.getName();
@@ -68,7 +86,6 @@ public class RuntimeConnectorList implements Serializable {
RepositoryConnector tagConnector = new TagConnector();
connectors.put(tagConnector.getId(), tagConnector);
connectorList.add(0, tagConnector);
-
}
public synchronized void discardConnectors() {
@@ -76,4 +93,12 @@ public class RuntimeConnectorList implements Serializable {
connectorList = null;
}
+ public void registerConnector(RepositoryConnector connector) {
+ connectors.put(connector.getId(), connector);
+ connectorList.add(connector);
+ }
+
+ public static RepositoryConnector getMyConnectorById(String id) {
+ return CycleComponentFactory.getCycleComponentInstance(RuntimeConnectorList.class, RuntimeConnectorList.class).getConnectorById(id);
+ }
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/AbstractFileSystemBasedRepositoryConnector.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/AbstractFileSystemBasedRepositoryConnector.java
index b8cb41b197..b5813c13e9 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/AbstractFileSystemBasedRepositoryConnector.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/AbstractFileSystemBasedRepositoryConnector.java
@@ -85,4 +85,12 @@ public abstract class AbstractFileSystemBasedRepositoryConnector extends Abstrac
RepositoryArtifact artifact = getRepositoryArtifact(artifactId);
return BasicRepositoryArtifactType.getDefaultContentRepresentation((BasicRepositoryArtifactType) artifact.getArtifactType());
}
+
+ public String concatenateNodeId(String prefix, String suffix) {
+ String concatChar = "/";
+ if(prefix.endsWith(concatChar) || suffix.startsWith("/")) {
+ concatChar = "";
+ }
+ return prefix + concatChar + suffix;
+ }
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/AbstractRestClientConnector.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/AbstractRestClientConnector.java
index 6d2a6ba9f5..4252156448 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/AbstractRestClientConnector.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/AbstractRestClientConnector.java
@@ -27,165 +27,165 @@ import org.restlet.representation.Representation;
*/
public abstract class AbstractRestClientConnector extends AbstractRepositoryConnector implements PasswordEnabledRepositoryConnector {
-// protected Map properties;
-// protected transient Client restletClient;
-// protected Context context;
-//
-// public AbstractRestClientConnector() {
-// }
-//
-// public Client initClient() {
-// // TODO: Check timeout on client and re-create it
-//
-// if (restletClient == null) {
-// // TODO: check for additional options
-// // Create and initialize HTTP client for HTTP REST API calls
-// context.getParameters().set("maxConnectionsPerHost", "20");
-// restletClient = new Client(context, Protocol.HTTP);
-// }
-// return restletClient;
-// }
-//
-// public Response sendRequest(Request request) throws IOException {
-// injectCustomSecurityMechanism(request);
-//
-// if (log.isLoggable(Level.FINE)) {
-// RestClientLogHelper.logHttpRequest(log, Level.FINE, request);
-// }
-//
-// Client client = initClient();
-// Response response = client.handle(request);
-//
-// if (log.isLoggable(Level.FINE)) {
-// RestClientLogHelper.logHttpResponse(log, Level.FINE, response);
-// }
-//
-// if (response.getStatus().isSuccess()) {
-// return response;
-// } else if (response.getStatus().equals(Status.CLIENT_ERROR_UNAUTHORIZED)) {
-// // retry request with additional authentication
-// Request retryRequest = createChallengeResponse(response);
-// return sendRequest(retryRequest);
-// }
-//
-// throw new RepositoryException("Encountered error while retrieving http response (HttpStatus: " + response.getStatus() + ", Body: "
-// + response.getEntity().getText() + ")");
-// }
-//
-// /**
-// * Overwrite this method to add required security mechanisms to requests.
-// * For example: add a token to request headers
-// * @param request
-// * @return modified request enhanced with custom security mechanism
-// */
-// protected abstract Request injectCustomSecurityMechanism(Request request);
-//
-// private Request createChallengeResponse(Response resourceNeedsAuth) {
-// ChallengeResponse cResponse = null;
-//
-// for (ChallengeRequest challengeRequest : resourceNeedsAuth.getChallengeRequests()) {
-// ChallengeScheme cs = challengeRequest.getScheme();
-//
-// if (ChallengeScheme.HTTP_BASIC.equals(cs)) {
-// if (log.isLoggable(Level.INFO)) {
-// log.info("Received 401 Error -> retrying request with HTTP_BASIC AUTH now!");
-// }
-// cResponse = new ChallengeResponse(ChallengeScheme.HTTP_BASIC, getUsername(), getPassword());
-// break;
-// }
-// }
-//
-// if (cResponse != null) {
-// Request authenticatedRequest = resourceNeedsAuth.getRequest();
-// authenticatedRequest.setChallengeResponse(cResponse);
-// return authenticatedRequest;
-// }
-//
-// throw new IllegalStateException(new AuthenticationException("Unable to determine required authentication scheme!"));
-// }
-//
-// private Request createRequest(Reference reference, Representation representation) {
-// if (reference != null) {
-// Request request = new Request();
-// request.setResourceRef(reference);
-// if (representation != null) {
-// request.setEntity(representation);
-// }
-//
-// return request;
-// }
-//
-// throw new RepositoryException("Reference object is null!");
-// }
-//
-// private Request createJsonRequest(Reference reference, Representation representation) {
-// Request jsonRequest = createRequest(reference, representation);
-// jsonRequest.getClientInfo().getAcceptedMediaTypes().add(new Preference(MediaType.APPLICATION_JSON));
-//
-// return jsonRequest;
-// }
-//
-// private Request createXmlRequest(Reference reference, Representation representation) {
-// Request xmlRequest = createRequest(reference, representation);
-// xmlRequest.getClientInfo().getAcceptedMediaTypes().add(new Preference(MediaType.APPLICATION_XML));
-//
-// return xmlRequest;
-// }
-//
-// public Response getJson(Reference reference, Representation representation) throws IOException {
-// Request getRequest = createJsonRequest(reference, representation);
-// getRequest.setMethod(Method.GET);
-//
-// return sendRequest(getRequest);
-// }
-//
-// public Response postJson(Reference reference, Representation representation) throws IOException {
-// Request postRequest = createJsonRequest(reference, representation);
-// postRequest.setMethod(Method.POST);
-//
-// return sendRequest(postRequest);
-// }
-//
-// public Response putJson(Reference reference, Representation representation) throws IOException {
-// Request putRequest = createJsonRequest(reference, representation);
-// putRequest.setMethod(Method.PUT);
-//
-// return sendRequest(putRequest);
-// }
-//
-// public Response deleteJson(Reference reference, Representation representation) throws IOException {
-// Request deleteRequest = createJsonRequest(reference, representation);
-// deleteRequest.setMethod(Method.DELETE);
-//
-// return sendRequest(deleteRequest);
-// }
-//
-// public Response getXml(Reference reference, Representation representation) throws IOException {
-// Request getRequest = createXmlRequest(reference, representation);
-// getRequest.setMethod(Method.GET);
-//
-// return sendRequest(getRequest);
-// }
-//
-// public Response postXml(Reference reference, Representation representation) throws IOException {
-// Request postRequest = createXmlRequest(reference, representation);
-// postRequest.setMethod(Method.POST);
-//
-// return sendRequest(postRequest);
-// }
-//
-// public Response putXml(Reference reference, Representation representation) throws IOException {
-// Request putRequest = createXmlRequest(reference, representation);
-// putRequest.setMethod(Method.PUT);
-//
-// return sendRequest(putRequest);
-// }
-//
-// public Response deleteXml(Reference reference, Representation representation) throws IOException {
-// Request deleteRequest = createXmlRequest(reference, representation);
-// deleteRequest.setMethod(Method.DELETE);
-//
-// return sendRequest(deleteRequest);
-// }
+ protected Map properties;
+ protected transient Client restletClient;
+ protected Context context;
+
+ public AbstractRestClientConnector() {
+ }
+
+ public Client initClient() {
+ // TODO: Check timeout on client and re-create it
+
+ if (restletClient == null) {
+ // TODO: check for additional options
+ // Create and initialize HTTP client for HTTP REST API calls
+ context.getParameters().set("maxConnectionsPerHost", "20");
+ restletClient = new Client(context, Protocol.HTTP);
+ }
+ return restletClient;
+ }
+
+ public Response sendRequest(Request request) throws IOException {
+ injectCustomSecurityMechanism(request);
+
+ if (log.isLoggable(Level.FINE)) {
+ RestClientLogHelper.logHttpRequest(log, Level.FINE, request);
+ }
+
+ Client client = initClient();
+ Response response = client.handle(request);
+
+ if (log.isLoggable(Level.FINE)) {
+ RestClientLogHelper.logHttpResponse(log, Level.FINE, response);
+ }
+
+ if (response.getStatus().isSuccess()) {
+ return response;
+ } else if (response.getStatus().equals(Status.CLIENT_ERROR_UNAUTHORIZED)) {
+ // retry request with additional authentication
+ Request retryRequest = createChallengeResponse(response);
+ return sendRequest(retryRequest);
+ }
+
+ throw new RepositoryException("Encountered error while retrieving http response (HttpStatus: " + response.getStatus() + ", Body: "
+ + response.getEntity().getText() + ")");
+ }
+
+ /**
+ * Overwrite this method to add required security mechanisms to requests.
+ * For example: add a token to request headers
+ * @param request
+ * @return modified request enhanced with custom security mechanism
+ */
+ protected abstract Request injectCustomSecurityMechanism(Request request);
+
+ private Request createChallengeResponse(Response resourceNeedsAuth) {
+ ChallengeResponse cResponse = null;
+
+ for (ChallengeRequest challengeRequest : resourceNeedsAuth.getChallengeRequests()) {
+ ChallengeScheme cs = challengeRequest.getScheme();
+
+ if (ChallengeScheme.HTTP_BASIC.equals(cs)) {
+ if (log.isLoggable(Level.INFO)) {
+ log.info("Received 401 Error -> retrying request with HTTP_BASIC AUTH now!");
+ }
+ cResponse = new ChallengeResponse(ChallengeScheme.HTTP_BASIC, getUsername(), getPassword());
+ break;
+ }
+ }
+
+ if (cResponse != null) {
+ Request authenticatedRequest = resourceNeedsAuth.getRequest();
+ authenticatedRequest.setChallengeResponse(cResponse);
+ return authenticatedRequest;
+ }
+
+ throw new IllegalStateException(new AuthenticationException("Unable to determine required authentication scheme!"));
+ }
+
+ private Request createRequest(Reference reference, Representation representation) {
+ if (reference != null) {
+ Request request = new Request();
+ request.setResourceRef(reference);
+ if (representation != null) {
+ request.setEntity(representation);
+ }
+
+ return request;
+ }
+
+ throw new RepositoryException("Reference object is null!");
+ }
+
+ private Request createJsonRequest(Reference reference, Representation representation) {
+ Request jsonRequest = createRequest(reference, representation);
+ jsonRequest.getClientInfo().getAcceptedMediaTypes().add(new Preference(MediaType.APPLICATION_JSON));
+
+ return jsonRequest;
+ }
+
+ private Request createXmlRequest(Reference reference, Representation representation) {
+ Request xmlRequest = createRequest(reference, representation);
+ xmlRequest.getClientInfo().getAcceptedMediaTypes().add(new Preference(MediaType.APPLICATION_XML));
+
+ return xmlRequest;
+ }
+
+ public Response getJson(Reference reference, Representation representation) throws IOException {
+ Request getRequest = createJsonRequest(reference, representation);
+ getRequest.setMethod(Method.GET);
+
+ return sendRequest(getRequest);
+ }
+
+ public Response postJson(Reference reference, Representation representation) throws IOException {
+ Request postRequest = createJsonRequest(reference, representation);
+ postRequest.setMethod(Method.POST);
+
+ return sendRequest(postRequest);
+ }
+
+ public Response putJson(Reference reference, Representation representation) throws IOException {
+ Request putRequest = createJsonRequest(reference, representation);
+ putRequest.setMethod(Method.PUT);
+
+ return sendRequest(putRequest);
+ }
+
+ public Response deleteJson(Reference reference, Representation representation) throws IOException {
+ Request deleteRequest = createJsonRequest(reference, representation);
+ deleteRequest.setMethod(Method.DELETE);
+
+ return sendRequest(deleteRequest);
+ }
+
+ public Response getXml(Reference reference, Representation representation) throws IOException {
+ Request getRequest = createXmlRequest(reference, representation);
+ getRequest.setMethod(Method.GET);
+
+ return sendRequest(getRequest);
+ }
+
+ public Response postXml(Reference reference, Representation representation) throws IOException {
+ Request postRequest = createXmlRequest(reference, representation);
+ postRequest.setMethod(Method.POST);
+
+ return sendRequest(postRequest);
+ }
+
+ public Response putXml(Reference reference, Representation representation) throws IOException {
+ Request putRequest = createXmlRequest(reference, representation);
+ putRequest.setMethod(Method.PUT);
+
+ return sendRequest(putRequest);
+ }
+
+ public Response deleteXml(Reference reference, Representation representation) throws IOException {
+ Request deleteRequest = createXmlRequest(reference, representation);
+ deleteRequest.setMethod(Method.DELETE);
+
+ return sendRequest(deleteRequest);
+ }
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/ConnectorLoginInterceptor.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/ConnectorLoginInterceptor.java
index 644ddb0bc7..3957cdf75c 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/ConnectorLoginInterceptor.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/ConnectorLoginInterceptor.java
@@ -3,6 +3,8 @@ package org.activiti.cycle.impl.connector;
import java.lang.reflect.Method;
import java.util.SortedSet;
import java.util.TreeSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import org.activiti.cycle.RepositoryAuthenticationException;
import org.activiti.cycle.RepositoryConnector;
@@ -15,6 +17,8 @@ import org.activiti.cycle.impl.Interceptor;
*/
public class ConnectorLoginInterceptor implements Interceptor {
+ private Logger log = Logger.getLogger(ConnectorLoginInterceptor.class.getName());
+
// set of method-names we do not want to intercept calls to (check whether the
// connector is loggedin)
private static final SortedSet ignoredMethods = new TreeSet();
@@ -42,10 +46,26 @@ public class ConnectorLoginInterceptor implements Interceptor {
}
// for all other methods, check whether the connector is logged in.
if (!connector.isLoggedIn()) {
- // the connector is not logged in, block invocation
- throw new RepositoryAuthenticationException("Connector '" + connector.getName() + "' is not logged in ", connector.getId());
+ // the connector is not logged in, try to login:
+ boolean loginSuccessful = false;
+ Exception exception = null;
+ try {
+ PasswordEnabledRepositoryConnector pwEnabledRepositoryConnector = (PasswordEnabledRepositoryConnector) connector;
+ String username = pwEnabledRepositoryConnector.getUsername();
+ String password = pwEnabledRepositoryConnector.getPassword();
+ loginSuccessful = pwEnabledRepositoryConnector.login(username, password);
+ } catch (Exception e) {
+ log.log(Level.WARNING, "Could not login connector '" + connector.getName() + "' with provided credentials.", e);
+ // we cannot login with the provided credentials
+ exception = e;
+ }
+ if (!loginSuccessful) {
+ if (exception != null) {
+ throw new RepositoryAuthenticationException("Connector '" + connector.getName() + "' is not logged in ", connector.getId(), exception);
+ }
+ throw new RepositoryAuthenticationException("Connector '" + connector.getName() + "' is not logged in ", connector.getId());
+ }
}
-
}
public void afterInvoke(Method m, Object object, Object invocationResult, Object... args) {
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/ci/hudson/action/CreateHudsonJob.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/ci/hudson/action/CreateHudsonJob.java
new file mode 100644
index 0000000000..eb6254544e
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/ci/hudson/action/CreateHudsonJob.java
@@ -0,0 +1,139 @@
+package org.activiti.cycle.impl.connector.ci.hudson.action;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.activiti.cycle.RepositoryException;
+import org.activiti.engine.impl.util.IoUtil;
+import org.restlet.data.MediaType;
+import org.restlet.data.Reference;
+import org.restlet.engine.Engine;
+import org.restlet.representation.Representation;
+import org.restlet.representation.StringRepresentation;
+import org.restlet.resource.ClientResource;
+
+/**
+ * Allows to create a basic Maven2/3 continuous integration job in
+ * Hudson/Jenkins.
+ *
+ * @author christian.lipphardt@camunda.com
+ */
+public class CreateHudsonJob {
+
+ public static final String CONFIG_XML_TEMPLATE = "template.config.xml";
+
+ public static final String PARAM_HUDSON_CREATE_JOB = "createItem";
+ public static final String PATTERN_JOB_DESCRIPTION = "@JOB_DESCRIPTION@";
+ public static final String PATTERN_SVN_LOCATION = "@SVN_LOCATION@";
+ public static final String PATTERN_EMAIL_ADDRESSES = "@EMAIL_ADDRESSES@";
+
+ public static final String DEFAULT_JOB_NAME = "CreatedByActivitiCycle";
+
+ public boolean createCIJobForProject(String hudsonUrl, String projectSVNUrl, String jobName, String jobDescription, List emailAddresses) {
+ // TODO: check supplied values or not ;)
+ if (hudsonUrl == null || hudsonUrl.length() == 0 || projectSVNUrl == null || projectSVNUrl.length() == 0) {
+ throw new RepositoryException("HudsonUrl and ProjectSVNUrl must not be null or empty");
+ }
+
+ // retrieve template.config.xml and enrich it with given data
+ String configXml = getTemplate();
+
+ // convert emails to single string with whitespace between them
+ String emailString = transformEmailAddress(emailAddresses);
+
+ configXml = replaceVarsInTemplate(configXml, jobDescription, projectSVNUrl, emailString);
+
+ hudsonUrl = transformUrl(hudsonUrl);
+ Reference reference = new Reference(hudsonUrl + PARAM_HUDSON_CREATE_JOB);
+
+ if (jobName == null || jobName.length() == 0) {
+ // create a default name
+ jobName = DEFAULT_JOB_NAME + "-" + projectSVNUrl;
+ }
+ reference.addQueryParameter("name", jobName);
+ ClientResource clientResource = new ClientResource(reference);
+
+ StringRepresentation configFileRep = new StringRepresentation(configXml, MediaType.APPLICATION_XML);
+ try {
+ Representation postResult = clientResource.post(configFileRep);
+ clientResource.release();
+ return Boolean.TRUE;
+ } catch (Exception e) {
+ throw new RepositoryException("Encountered an error while creating Hudson CI Job for Project '" + jobName + "'", e);
+ } finally {
+ Engine.clearThreadLocalVariables();
+ }
+ }
+
+ private String getTemplate() {
+ InputStream is = null;
+ try {
+ is = this.getClass().getResourceAsStream(CONFIG_XML_TEMPLATE);
+ if (is == null) {
+ throw new RepositoryException("Template '" + CONFIG_XML_TEMPLATE + "' doesn't exist in classpath");
+ }
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ StringBuilder sb = new StringBuilder();
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line + "\n");
+ }
+ is.close();
+
+ return sb.toString();
+ } catch (Exception ex) {
+ throw new RepositoryException("Exception while creating template '" + CONFIG_XML_TEMPLATE + "'", ex);
+ } finally {
+ IoUtil.closeSilently(is);
+ }
+ }
+
+ private String transformEmailAddress(List emailAddresses) {
+ StringBuffer emailString = new StringBuffer();
+
+ for (String emailAddress : emailAddresses) {
+ emailString.append(emailAddress + " ");
+ }
+
+ return emailString.toString();
+ }
+
+ private String transformUrl(String url) {
+ if (url != null && !url.endsWith("/")) {
+ url = url + "/";
+ }
+
+ return url;
+ }
+
+ private String replaceVarsInTemplate(String template, String jobDescription, String projectSVNUrl, String emailAddresses) {
+ template = template.replaceFirst(PATTERN_JOB_DESCRIPTION, "");
+ template = template.replaceFirst(PATTERN_SVN_LOCATION, projectSVNUrl);
+ template = template.replaceFirst(PATTERN_EMAIL_ADDRESSES, emailAddresses);
+
+ return template;
+ }
+
+ public static void main(String[] args) {
+ String hudsonUrl = "http://localhost:8080/hudson/";
+ String projectSVNUrl = "projectLocationinSVN";
+ String jobName = "JavaGenerated-" + System.currentTimeMillis();
+ List emailAddresses = new ArrayList();
+
+ emailAddresses.add("christian.lipphardt@camunda.com");
+ emailAddresses.add("bernd.ruecker@camunda.com");
+
+ CreateHudsonJob createHudsonJob = new CreateHudsonJob();
+ String jobDescription = "see test";
+ Boolean successful = createHudsonJob.createCIJobForProject(hudsonUrl, projectSVNUrl, jobName, jobDescription , emailAddresses);
+ if (successful) {
+ System.out.println("Successfully created Hudson CI Job for Project '" + jobName + "'");
+ } else {
+ System.out.println("Unable to create Hudson CI Job for Project '" + jobName + "'");
+ }
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/fs/FileSystemConnector.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/fs/FileSystemConnector.java
index 89453d5985..2d52fed892 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/fs/FileSystemConnector.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/fs/FileSystemConnector.java
@@ -231,7 +231,7 @@ public class FileSystemConnector extends AbstractFileSystemBasedRepositoryConnec
return getRepositoryArtifact(getRepositoryNodeId(parentFolderId, artifactName));
}
-
+
public RepositoryArtifact createArtifactFromContentRepresentation(String parentFolderId, String artifactName, String artifactType,
String contentRepresentationName, Content artifactContent) throws RepositoryNodeNotFoundException {
return createArtifact(parentFolderId, artifactName, artifactType, artifactContent);
@@ -281,17 +281,16 @@ public class FileSystemConnector extends AbstractFileSystemBasedRepositoryConnec
}
private String getLocalPath(String path) {
- String basePath = getBasePath();
- if ("".equals(basePath)) {
+ if ("".equals(getBasePath())) {
// if root is configured in Unix ("/" without trailing slash = "")
return path;
- } else if (path.startsWith(basePath)) {
- path = path.replace(basePath, "");
+ } else if (path.startsWith(getBasePath())) {
+ path = path.replace(getBasePath(), "");
// replace windows style slashes
path = path.replace("\\", "/");
return path;
}
- throw new RepositoryException("Unable to determine local path! (path: '" + path + "', base path: '" + basePath + "')");
+ throw new RepositoryException("Unable to determine local path! ('" + path + "')");
}
public String[] getConfigurationKeys() {
@@ -323,5 +322,6 @@ public class FileSystemConnector extends AbstractFileSystemBasedRepositoryConnec
}
return path;
}
+
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnector.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnector.java
index 2b616ece6a..e40e7dcadc 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnector.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnector.java
@@ -12,8 +12,15 @@
*/
package org.activiti.cycle.impl.connector.signavio;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.net.URLEncoder;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -46,6 +53,18 @@ import org.activiti.cycle.impl.connector.signavio.repositoryartifacttype.Signavi
import org.activiti.cycle.impl.connector.signavio.repositoryartifacttype.SignavioDefaultArtifactType;
import org.activiti.cycle.impl.connector.signavio.util.SignavioJsonHelper;
import org.activiti.cycle.impl.connector.util.RestClientLogHelper;
+import org.activiti.cycle.impl.util.IoUtils;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntity;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.entity.mime.content.StringBody;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -60,7 +79,6 @@ import org.restlet.data.Preference;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.data.Status;
-import org.restlet.engine.Engine;
import org.restlet.ext.json.JsonRepresentation;
import org.restlet.representation.Representation;
@@ -130,13 +148,7 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
return restletClient;
}
- public void sendRequest(Request request) throws IOException {
- Response response = sendRequestGetResponse(request);
- response.release();
- Engine.clearThreadLocalVariables();
- }
-
- public Response sendRequestGetResponse(Request request) throws IOException {
+ public Response sendRequest(Request request) throws IOException {
injectSecurityToken(request);
if (log.isLoggable(Level.FINE)) {
@@ -145,7 +157,6 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
Client client = initClient();
Response response = client.handle(request);
- request.release();
if (log.isLoggable(Level.FINE)) {
RestClientLogHelper.logHttpResponse(log, Level.FINE, response);
@@ -166,25 +177,12 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
return request;
}
-
- public static void main(String[] args) throws IOException {
- SignavioConnector connector = new SignavioConnector();
- System.out.println(
- connector.getJsonResponse("http://localhost:8080/activiti-modeler/p/model/C:;Activiti;releases;activiti-5.2;apps;apache-tomcat-6.0.29;bin;..;..;..;workspace;activiti-modeler-examples;TwitterDemoProcess.signavio.xml/info")
- );
- }
- public String getJsonResponse(String url) throws IOException {
+ public Response getJsonResponse(String url) throws IOException {
Request jsonRequest = new Request(Method.GET, new Reference(url));
jsonRequest.getClientInfo().getAcceptedMediaTypes().add(new Preference(MediaType.APPLICATION_JSON));
- Response response = sendRequestGetResponse(jsonRequest);
- String responseString = response.getEntityAsText();
- response.release();
-
- Engine.clearThreadLocalVariables();
-
- return responseString;
+ return sendRequest(jsonRequest);
}
protected boolean registerUserWithSignavio(String firstname, String lastname, String email, String password) throws IOException {
@@ -199,12 +197,13 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
Representation registrationRep = registrationForm.getWebRepresentation();
Request registrationRequest = new Request(Method.POST, getConfiguration().getRegistrationUrl(), registrationRep);
- Response registrationResponse = sendRequestGetResponse(registrationRequest);
- boolean success = registrationResponse.getStatus().equals(Status.SUCCESS_CREATED);
- registrationResponse.release();
- Engine.clearThreadLocalVariables();
-
- return success;
+ Response registrationResponse = sendRequest(registrationRequest);
+
+ if (registrationResponse.getStatus().equals(Status.SUCCESS_CREATED)) {
+ return true;
+ } else {
+ return false;
+ }
}
public boolean login(String username, String password) {
@@ -224,15 +223,13 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
Representation loginRep = loginForm.getWebRepresentation();
Request loginRequest = new Request(Method.POST, getConfiguration().getLoginUrl(), loginRep);
- Response loginResponse = sendRequestGetResponse(loginRequest);
- token = loginResponse.getEntity().getText();
- loginResponse.release();
+ Response loginResponse = sendRequest(loginRequest);
+
+ Representation representation = loginResponse.getEntity();
+ token = representation.getText();
} catch (Exception ex) {
throw new RepositoryException("Error during login to connector '" + getName() + "'", ex);
}
- finally {
- Engine.clearThreadLocalVariables();
- }
if (token.matches("[a-f0-9]{32}")) {
setSecurityToken(token);
log.fine("SecurityToken: " + getSecurityToken());
@@ -370,8 +367,8 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
if (getConfiguration() instanceof OryxConnectorConfiguration) {
nodes = getChildrenFromOryxBackend(id);
} else {
- String directoryResponse = getJsonResponse(getConfiguration().getDirectoryUrl(id));
- JsonRepresentation jsonData = new JsonRepresentation(directoryResponse);
+ Response directoryResponse = getJsonResponse(getConfiguration().getDirectoryUrl(id));
+ JsonRepresentation jsonData = new JsonRepresentation(directoryResponse.getEntity());
JSONArray relJsonArray = jsonData.getJsonArray();
if (log.isLoggable(Level.FINEST)) {
@@ -433,8 +430,8 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
for (int i = modelRefs.length() - 1; (i >= 0 && i > modelRefs.length() - pageSize); i--) {
String modelRef = modelRefs.getString(i);
String modelId = connectorConfiguration.getModelIdFromUrl(modelRef);
- String infoResponse = getJsonResponse(connectorConfiguration.getModelInfoUrl(modelId));
- JsonRepresentation jsonRepresentation = new JsonRepresentation(infoResponse);
+ Response infoResponse = getJsonResponse(connectorConfiguration.getModelInfoUrl(modelId));
+ JsonRepresentation jsonRepresentation = new JsonRepresentation(infoResponse.getEntity());
RepositoryArtifact fileInfo = getArtifactInfoFromFile(modelId, jsonRepresentation.getJsonObject());
nodes.add(fileInfo);
}
@@ -458,8 +455,8 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
for (int i = modelRefs.length() - 1 - pageNumber * pageSize; (i >= 0 && i > modelRefs.length() - (pageNumber + 1) * pageSize); i--) {
String modelRef = modelRefs.getString(i);
String modelId = connectorConfiguration.getModelIdFromUrl(modelRef);
- String infoResponse = getJsonResponse(connectorConfiguration.getModelInfoUrl(modelId));
- JsonRepresentation jsonRepresentation = new JsonRepresentation(infoResponse);
+ Response infoResponse = getJsonResponse(connectorConfiguration.getModelInfoUrl(modelId));
+ JsonRepresentation jsonRepresentation = new JsonRepresentation(infoResponse.getEntity());
RepositoryArtifact fileInfo = getArtifactInfoFromFile(modelId, jsonRepresentation.getJsonObject());
nodes.add(fileInfo);
}
@@ -470,24 +467,24 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
private JSONArray getModelIdsFromOryxBackend(String stencilsetNamespace) throws IOException, JSONException {
String type = URLEncoder.encode(stencilsetNamespace, "UTF-8");
- String filterResponse = getJsonResponse(getConfiguration().getRepositoryBackendUrl() + "filter?type=" + type + "&sort=rating");
- JsonRepresentation jsonRepresentation = new JsonRepresentation(filterResponse);
+ Response filterResponse = getJsonResponse(getConfiguration().getRepositoryBackendUrl() + "filter?type=" + type + "&sort=rating");
+ JsonRepresentation jsonRepresentation = new JsonRepresentation(filterResponse.getEntity());
JSONArray modelRefs = jsonRepresentation.getJsonArray();
return modelRefs;
}
private JSONArray getStencilsets() throws IOException, JSONException {
JSONArray stencilsets;
- String stencilsetsResponse = getJsonResponse(getConfiguration().getStencilsetsUrl());
- JsonRepresentation stencilsetsJsonRepresentation = new JsonRepresentation(stencilsetsResponse);
+ Response stencilsetsResponse = getJsonResponse(getConfiguration().getStencilsetsUrl());
+ JsonRepresentation stencilsetsJsonRepresentation = new JsonRepresentation(stencilsetsResponse.getEntity());
stencilsets = stencilsetsJsonRepresentation.getJsonArray();
return stencilsets;
}
public RepositoryFolder getRepositoryFolder(String id) {
try {
- String directoryResponse = getJsonResponse(getConfiguration().getDirectoryUrl(id));
- JsonRepresentation jsonData = new JsonRepresentation(directoryResponse);
+ Response directoryResponse = getJsonResponse(getConfiguration().getDirectoryUrl(id));
+ JsonRepresentation jsonData = new JsonRepresentation(directoryResponse.getEntity());
JSONArray jsonArray = jsonData.getJsonArray();
// search for rel: "info" in jsonArray to get directory specified by id;
@@ -521,8 +518,8 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
JSONObject jsonObject = null;
try {
- String modelResponse = getJsonResponse(getConfiguration().getModelUrl(id) + "/info");
- jsonData = new JsonRepresentation(modelResponse);
+ Response modelResponse = getJsonResponse(getConfiguration().getModelUrl(id) + "/info");
+ jsonData = new JsonRepresentation(modelResponse.getEntity());
jsonObject = jsonData.getJsonObject();
return getArtifactInfoFromFile(id, jsonObject);
} catch (Exception ex) {
@@ -552,6 +549,19 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
// TODO: create response object which contains the ID or maybe skip the
// whole artifact returning?
+
+ // HACK: getting the created folder through iteration of the child
+ // nodes of the parent folder and comparing the names...
+ RepositoryNodeCollection nodes = getChildren(parentFolderId);
+ for (RepositoryNode node : nodes.asList()) {
+ if (!(node instanceof RepositoryFolder)) {
+ continue;
+ }
+ if (node.getMetadata().getName().equals(name)) {
+ return (RepositoryFolder) node;
+ }
+ }
+
return null;
} catch (Exception ex) {
// throw new RepositoryNodeNotFoundException(getConfiguration().getName(),
@@ -624,7 +634,7 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
}
public RepositoryArtifact createEmptyArtifact(String parentFolderId, String artifactName, String artifactType) throws RepositoryNodeNotFoundException {
- return createArtifactFromJSON(parentFolderId, artifactName, artifactType, "{}");
+ return createArtifactFromJSON(parentFolderId, artifactName, artifactType, SignavioJsonHelper.getEmptypModelTemplate());
}
public RepositoryArtifact createArtifact(String parentFolderId, String artifactName, String artifactType, Content artifactContent)
@@ -733,29 +743,64 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
}
/**
- * FIXME: unfinished but maybe it works... method accepts a xml String and
- * returns the json representation
+ * FIXME: This implementation uses the bpmn2_0-import servlet which returns a
+ * temporary(?) nodeid for which we then retrieve JSon Content.
+ *
+ * NOTE: I could not make this work using Restlet. Maybe I just dont get it...
+ * (daniel)
*/
- public String transformBpmn20XmltoJson(String xmlData) {
+ public String transformBpmn20XmltoJson(String xml) {
+ File tmpfile = null;
try {
- Form dataForm = new Form();
- dataForm.add("data", xmlData);
- Representation xmlDataRep = dataForm.getWebRepresentation();
-
- Request request = new Request(Method.POST, new Reference(getConfiguration().getBpmn20XmlImportServletUrl()), xmlDataRep);
- request.getClientInfo().getAcceptedMediaTypes().add(new Preference(MediaType.APPLICATION_JSON));
- Response jsonResponse = sendRequestGetResponse(request);
- String xml = jsonResponse.getEntity().getText();
- jsonResponse.release();
- Engine.clearThreadLocalVariables();
-
- return new JSONObject(xml).toString();
-
+ HttpClient client = new DefaultHttpClient();
+ client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
+ String postUrl = getConfiguration().getBpmn20XmlImportServletUrl();
+ HttpPost post = new HttpPost(postUrl);
+ post.addHeader("token", getSecurityToken());
+ MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
+
+ // creating a temporary file
+ tmpfile = File.createTempFile(UUID.randomUUID().toString(), ".xml");
+ OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpfile));
+ InputStream is = new ByteArrayInputStream(xml.getBytes(Charset.forName("utf-8")));
+ IoUtils.copyBytes(is, os);
+ os.flush();
+ os.close();
+ entity.addPart("bpmn2_0file", new FileBody(tmpfile));
+ // apparently this works:
+ entity.addPart("directory", new StringBody("/directory", Charset.forName("UTF-8")));
+ post.setEntity(entity);
+
+ // get the response (id of the temporary model)
+ String response = EntityUtils.toString(client.execute(post).getEntity(), "UTF-8");
+
+ // the returned response is of the form ["..."], I suggest this is the
+ // case because an xml-file can contain multiple process definitions. For
+ // the moment we just cut the json-list brackets.
+ String artifactId = response.substring(2);
+ artifactId = artifactId.substring(0, artifactId.length() - 2);
+
+ HttpGet get = new HttpGet(getConfiguration().getEditorUrl(artifactId) + "&data");
+ get.addHeader("token", getSecurityToken());
+ // let's pretend we're Firefox on Windows
+ get.addHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13");
+
+ // get the json Representation for the temporary model
+ response = EntityUtils.toString(client.execute(get).getEntity(), "UTF-8");
+
+ client.getConnectionManager().shutdown();
+
+ JSONObject jsonObj = new JSONObject(response);
+ return jsonObj.getString("model");
+
} catch (Exception ex) {
throw new RepositoryException("Error while transforming BPMN2_0_XML to BPMN2_0_JSON", ex);
+ } finally {
+ if (tmpfile != null) {
+ tmpfile.delete();
+ }
}
}
-
public void updateContent(String artifactId, Content content) throws RepositoryNodeNotFoundException {
throw new RepositoryException("Moving artifacts is not (yet) supported by the Signavio Connector");
}
@@ -765,7 +810,11 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
}
public boolean isLoggedIn() {
- return loggedIn || !getConfigValue(CONFIG_KEY_LOGIN_REQUIRED, Boolean.class);
+ Boolean loginRequired = getConfigValue(CONFIG_KEY_LOGIN_REQUIRED, Boolean.class);
+ if (loginRequired==null) {
+ throw new RepositoryException("Configuration of Signavio Connector must contain the configuration attribute '"+CONFIG_KEY_LOGIN_REQUIRED+"'");
+ }
+ return loggedIn || !loginRequired;
}
public Content getContent(String artifactId) throws RepositoryNodeNotFoundException {
@@ -814,4 +863,8 @@ public class SignavioConnector extends AbstractRepositoryConnector implements Si
return configuration;
}
+ public String concatenateNodeId(String prefix, String suffix) {
+ return prefix + ";" + suffix;
+ }
+
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnectorConfiguration.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnectorConfiguration.java
index fbe6ac69fe..61e8c56822 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnectorConfiguration.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnectorConfiguration.java
@@ -25,7 +25,7 @@ import org.activiti.cycle.RepositoryException;
*
* @author bernd.ruecker@camunda.com
*/
-public class SignavioConnectorConfiguration {
+public class SignavioConnectorConfiguration {
// these values differ between Oryx and Signavio
protected static String REPOSITORY_BACKEND_URL_SUFFIX = "p/";
@@ -42,14 +42,15 @@ public class SignavioConnectorConfiguration {
public static String STENCILSETS_URL_SUFFIX = "stencilsets/stencilsets.json";
public static String BPMN_20_EXPORT_SERVLET = "bpmn2_0serialization";
- public static String BPMN_20_IMPORT_SERVLET = "bpmn2_0deserialization";
+ // public static String BPMN_20_IMPORT_SERVLET = "bpmn2_0deserialization";
+ public static String BPMN_20_IMPORT_SERVLET = "bpmn2_0-import";
protected SignavioConnector connector;
-
+
public SignavioConnectorConfiguration(SignavioConnector connector) {
this.connector = connector;
}
-
+
public String getSignavioUrl() {
return connector.getSignavioUrl();
}
@@ -172,7 +173,7 @@ public class SignavioConnectorConfiguration {
}
public String getBpmn20XmlImportServletUrl() {
- return getEditorBackendUrl() + BPMN_20_IMPORT_SERVLET;
+ return getSignavioUrl() + REPOSITORY_BACKEND_URL_SUFFIX + BPMN_20_IMPORT_SERVLET;
}
/**
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnectorInterface.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnectorInterface.java
index b45f04fd94..3a40f6fa42 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnectorInterface.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/SignavioConnectorInterface.java
@@ -5,6 +5,8 @@ import java.io.IOException;
import org.activiti.cycle.RepositoryArtifact;
import org.activiti.cycle.RepositoryConnector;
import org.restlet.Client;
+import org.restlet.Request;
+import org.restlet.Response;
/**
* Public interface for the Signavio Connector.
@@ -26,8 +28,10 @@ public interface SignavioConnectorInterface extends RepositoryConnector {
public String getSecurityToken();
- public String getJsonResponse(String url) throws IOException;
+ public Response getJsonResponse(String url) throws IOException;
- public SignavioConnectorConfiguration getConfiguration();
+ public Response sendRequest(Request request) throws IOException;
+
+ public abstract SignavioConnectorConfiguration getConfiguration();
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/CreateMavenProjectAction.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/CreateMavenProjectAction.java
index 9ec2581d95..98fdec5923 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/CreateMavenProjectAction.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/CreateMavenProjectAction.java
@@ -3,21 +3,26 @@ package org.activiti.cycle.impl.connector.signavio.action;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.activiti.cycle.Content;
+import org.activiti.cycle.CycleComponentFactory;
import org.activiti.cycle.RepositoryArtifact;
import org.activiti.cycle.RepositoryArtifactLink;
import org.activiti.cycle.RepositoryConnector;
import org.activiti.cycle.RepositoryException;
+import org.activiti.cycle.RepositoryFolder;
import org.activiti.cycle.annotations.CycleComponent;
-import org.activiti.cycle.annotations.ExcludesCycleComponents;
import org.activiti.cycle.context.CycleContextType;
-import org.activiti.cycle.impl.action.DefaultCopyArtifactAction;
+import org.activiti.cycle.impl.components.RuntimeConnectorList;
import org.activiti.cycle.impl.connector.signavio.provider.ActivitiCompliantBpmn20Provider;
import org.activiti.cycle.impl.connector.util.TransactionalConnectorUtils;
import org.activiti.cycle.impl.db.entity.RepositoryArtifactLinkEntity;
@@ -47,7 +52,7 @@ public class CreateMavenProjectAction extends CreateTechnicalBpmnXmlAction {
super("Create default maven project");
try {
ACTIVITI_HOME_PATH = new File("../../../").getCanonicalPath();
- } catch (IOException e) {
+ } catch (IOException e) {
ACTIVITI_HOME_PATH = new File(".").getAbsolutePath();
log.log(Level.WARNING, "Couldn't set Activiti Home path (see nested exception for the cause), using '" + ACTIVITI_HOME_PATH + "' instead.", e);
}
@@ -62,34 +67,41 @@ public class CreateMavenProjectAction extends CreateTechnicalBpmnXmlAction {
RepositoryConnector targetConnector = (RepositoryConnector) getParameter(parameters, PARAM_TARGET_CONNECTOR, true, null, RepositoryConnector.class);
boolean createLink = (Boolean) getParameter(parameters, CREATE_LINK_NAME, true, Boolean.TRUE, Boolean.class);
- String bpmnXml = ActivitiCompliantBpmn20Provider.createBpmnXml(connector, artifact);
+ List processes = new ArrayList();
+ processes.add(artifact);
+ RepositoryFolder targetFolder = targetConnector.createFolder(targetFolderId, targetName);
+ createMavenProject(targetFolder.getNodeId(), targetName, comment, targetConnector, createLink, processes);
- RepositoryArtifact bpmnArtifact = null;
+ }
+ public Map createMavenProject(String targetFolderId, String targetName, String comment,
+ RepositoryConnector targetConnector, boolean createLink, List processes) {
+ Map bpmnArtifacts = null;
TransactionalConnectorUtils.beginTransaction(targetConnector);
try {
// create maven project
- bpmnArtifact = createProject(targetConnector, targetFolderId, targetName, bpmnXml);
+ bpmnArtifacts = createProject(targetConnector, targetFolderId, targetName, processes);
TransactionalConnectorUtils.commitTransaction(targetConnector, comment);
} catch (RepositoryException e) {
TransactionalConnectorUtils.rollbackTransaction(targetConnector);
throw e;
}
- if (createLink && bpmnArtifact != null) {
+ if (createLink && bpmnArtifacts != null) {
// TODO: We cannot link to a folder at the moment!
// RepositoryFolder targetFolder =
// targetConnector.getRepositoryFolder(targetFolderId);
-
- RepositoryArtifactLink link = new RepositoryArtifactLinkEntity();
- link.setSourceArtifact(artifact);
- link.setTargetArtifact(bpmnArtifact);
- link.setComment(comment);
- link.setLinkType(getLinkType());
- CycleRepositoryService repositoryService = CycleServiceFactory.getRepositoryService();
- repositoryService.addArtifactLink(link);
+ for (Entry processMappedToXml : bpmnArtifacts.entrySet()) {
+ RepositoryArtifactLink link = new RepositoryArtifactLinkEntity();
+ link.setSourceArtifact(processMappedToXml.getKey());
+ link.setTargetArtifact(processMappedToXml.getValue());
+ link.setComment(comment);
+ link.setLinkType(getLinkType());
+ CycleRepositoryService repositoryService = CycleServiceFactory.getRepositoryService();
+ repositoryService.addArtifactLink(link);
+ }
}
+ return bpmnArtifacts;
}
-
public String getProcessName(RepositoryArtifact artifact) {
return artifact.getMetadata().getName();
}
@@ -98,16 +110,16 @@ public class CreateMavenProjectAction extends CreateTechnicalBpmnXmlAction {
* create a project from the Maven template and return the RepositoryArtifact
* representing the bpmn process model
*/
- public RepositoryArtifact createProject(RepositoryConnector connector, String rootFolderId, String projectName, String processDefinitionXml) {
- RepositoryArtifact result = null;
+ public Map createProject(RepositoryConnector connector, String rootFolderId, String projectName,
+ List processes) {
+ Map resultList = null;
try {
ZipInputStream projectTemplateInputStream = new ZipInputStream(getProjectTemplate());
ZipEntry zipEntry = null;
- String rootSubstitution = null;
-
while ((zipEntry = projectTemplateInputStream.getNextEntry()) != null) {
String zipName = zipEntry.getName();
+ zipName = zipName.replaceAll("activiti-cycle-maven-template/", "");
if (zipName.endsWith("/")) {
zipName = zipName.substring(0, zipName.length() - 1);
}
@@ -117,39 +129,21 @@ public class CreateMavenProjectAction extends CreateTechnicalBpmnXmlAction {
path = zipName.substring(0, zipName.lastIndexOf("/"));
name = zipName.substring(zipName.lastIndexOf("/") + 1);
}
- if ("".equals(path)) {
- // root folder is named after the project, not like the
- // template
- // folder name
- rootSubstitution = name;
- name = projectName;
- } else {
- // rename the root folder in all other paths as well
- path = path.replace(rootSubstitution, projectName);
- }
String absolutePath = rootFolderId + "/" + path;
- boolean isBpmnModel = false;
if (zipEntry.isDirectory()) {
- connector.createFolder(absolutePath, name);
+ if (!zipEntry.getName().equals("activiti-cycle-maven-template/")) {
+ connector.createFolder(absolutePath, name);
+ }
} else {
- Content content = new Content();
-
if ("template.bpmn20.xml".equals(name)) {
- // This file shall be replaced with the process
- // definition
- content.setValue(processDefinitionXml);
- name = projectName + ".bpmn20.xml";
- isBpmnModel = true;
- log.log(Level.INFO, "Create processdefinition from Signavio process model " + projectName);
+ resultList = generateProcesses(processes, absolutePath, connector);
} else {
+ Content content = new Content();
byte[] bytes = IoUtil.readInputStream(projectTemplateInputStream, "ZIP entry '" + zipName + "'");
String txtContent = new String(bytes).replaceAll(REPLACE_STRING, projectName).replaceAll("@@ACTIVITI.HOME@@", ACTIVITI_HOME_PATH);
content.setValue(txtContent);
- }
- log.log(Level.INFO, "Create new artifact from zip entry '" + zipEntry.getName() + "' in folder '" + absolutePath + "' with name '" + name + "'");
- RepositoryArtifact artifact = connector.createArtifact(absolutePath, name, null, content);
- if (isBpmnModel) {
- result = artifact;
+ log.log(Level.INFO, "Create new artifact from zip entry '" + zipEntry.getName() + "' in folder '" + absolutePath + "' with name '" + name + "'");
+ connector.createArtifact(absolutePath, name, null, content);
}
}
projectTemplateInputStream.closeEntry();
@@ -158,9 +152,26 @@ public class CreateMavenProjectAction extends CreateTechnicalBpmnXmlAction {
} catch (IOException ex) {
throw new RepositoryException("Couldn't create maven project due to IO errors", ex);
}
- return result;
+ return resultList;
}
+ /**
+ * generates bpmn20.xml artifacts for the provided process models
+ */
+ protected Map generateProcesses(List processes, String path, RepositoryConnector targetConnector) {
+ Map resultList = new HashMap();
+ RuntimeConnectorList runtimeConnectorList = CycleComponentFactory.getCycleComponentInstance(RuntimeConnectorList.class, RuntimeConnectorList.class);
+ for (RepositoryArtifact process : processes) {
+ log.log(Level.INFO, "Create processdefinition from Signavio process model " + process.getMetadata().getName());
+ String name = getProcessName(process) + ".bpmn20.xml";
+ Content content = new Content();
+ RepositoryConnector connector = runtimeConnectorList.getConnectorById(process.getConnectorId());
+ content.setValue(ActivitiCompliantBpmn20Provider.createBpmnXml(connector, process));
+ resultList.put(process, targetConnector.createArtifact(path, name, null, content));
+ }
+ return resultList;
+
+ }
protected InputStream getProjectTemplate() {
return this.getClass().getResourceAsStream("activiti-cycle-maven-template.zip");
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/OpenModelerAction.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/OpenModelerAction.java
index d0010373ac..5516786a5d 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/OpenModelerAction.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/OpenModelerAction.java
@@ -28,6 +28,9 @@ import org.activiti.cycle.impl.action.CreateUrlActionImpl;
import org.activiti.cycle.impl.connector.signavio.SignavioConnectorConfiguration;
import org.activiti.cycle.impl.connector.signavio.SignavioConnectorInterface;
import org.activiti.cycle.impl.connector.signavio.repositoryartifacttype.SignavioBpmn20ArtifactType;
+import org.activiti.cycle.impl.processsolution.ProcessSolutionAction;
+import org.activiti.cycle.impl.processsolution.connector.ProcessSolutionArtifact;
+import org.activiti.cycle.processsolution.ProcessSolutionState;
/**
* Action to open the signavio modeler
@@ -35,7 +38,7 @@ import org.activiti.cycle.impl.connector.signavio.repositoryartifacttype.Signavi
* @author bernd.ruecker@camunda.com
*/
@CycleComponent(context = CycleContextType.APPLICATION)
-public class OpenModelerAction extends CreateUrlActionImpl {
+public class OpenModelerAction extends CreateUrlActionImpl implements ProcessSolutionAction {
private static final long serialVersionUID = 1L;
@@ -58,6 +61,21 @@ public class OpenModelerAction extends CreateUrlActionImpl {
}
}
+ public String getWarning(RepositoryConnector connector, RepositoryArtifact repositoryArtifact) {
+ if (repositoryArtifact instanceof ProcessSolutionArtifact) {
+ ProcessSolutionArtifact psArtifact = (ProcessSolutionArtifact) repositoryArtifact;
+ if (psArtifact.getWrappedNode() != null && psArtifact.getVirtualRepositoryFolder() != null) {
+ if ("Processes".equals(psArtifact.getVirtualRepositoryFolder().getType())) {
+ if (psArtifact.getProcessSolution().getState().equals(ProcessSolutionState.IN_IMPLEMENTATION)) {
+ return "This project is currently in implementation. Changes to the process models "
+ + "could potentially be overwritten in the next iteration.
Are you shure you want to continue?";
+ }
+ }
+ }
+ }
+ return null;
+
+ }
public Set getArtifactTypes() {
return types;
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/ValidateActivitiDeployment.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/ValidateActivitiDeployment.java
index 35c2c7486a..c4e07b8367 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/ValidateActivitiDeployment.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/action/ValidateActivitiDeployment.java
@@ -4,13 +4,18 @@ import java.util.Map;
import org.activiti.cycle.RepositoryArtifact;
import org.activiti.cycle.RepositoryConnector;
+import org.activiti.cycle.RepositoryNode;
import org.activiti.cycle.impl.connector.signavio.SignavioConnector;
+import org.activiti.cycle.impl.connector.signavio.SignavioConnectorInterface;
import org.activiti.cycle.impl.connector.signavio.provider.ActivitiCompliantBpmn20Provider;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.impl.ProcessEngineImpl;
+import org.activiti.engine.impl.bpmn.parser.BpmnParse;
import org.activiti.engine.impl.bpmn.parser.BpmnParser;
+import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.el.ExpressionManager;
import org.activiti.engine.impl.repository.DeploymentEntity;
+import org.activiti.engine.impl.repository.ProcessDefinitionEntity;
public class ValidateActivitiDeployment extends CreateTechnicalBpmnXmlAction {
@@ -28,13 +33,20 @@ public class ValidateActivitiDeployment extends CreateTechnicalBpmnXmlAction {
}
public void execute(RepositoryConnector connector, RepositoryArtifact artifact, Map parameters) throws Exception {
+ BpmnParse parse = createParseObject(connector, artifact);
+ // parse to validate
+ parse.execute();
+ // That's it, now we get an exception if the file is invalid
+ }
+
+ public static BpmnParse createParseObject(RepositoryConnector connector, RepositoryArtifact artifact) {
+ String bpmnXml = ActivitiCompliantBpmn20Provider.createBpmnXml((SignavioConnectorInterface) connector, artifact);
+
// TODO: Okay, this needs more serious thinking where we get the engine
// from!
ExpressionManager expressionManager = ((ProcessEngineImpl) ProcessEngines.getDefaultProcessEngine()).getProcessEngineConfiguration().getExpressionManager();
-
- String bpmnXml = ActivitiCompliantBpmn20Provider.createBpmnXml((SignavioConnector) connector, artifact);
-
BpmnParser bpmnParser = new BpmnParser(expressionManager);
+ Context.setProcessEngineConfiguration( ((ProcessEngineImpl)ProcessEngines.getDefaultProcessEngine()).getProcessEngineConfiguration());
// Unfortunately the deployment id is requested while parsing, so we have to
// set a DeploymentEntity to avoid an NPE
@@ -42,8 +54,8 @@ public class ValidateActivitiDeployment extends CreateTechnicalBpmnXmlAction {
deployment.setId("VALIDATION_DEPLOYMENT");
// parse to validate
- bpmnParser.createParse().deployment(deployment).sourceString(bpmnXml).name(artifact.getNodeId()).execute();
- // That's it, now we get an exception if the file is invalid
+ BpmnParse parse = bpmnParser.createParse().deployment(deployment).sourceString(bpmnXml).name(artifact.getNodeId());
+ return parse;
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/AbstractPngProvider.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/AbstractPngProvider.java
index 2c70929b30..cdddf1efda 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/AbstractPngProvider.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/AbstractPngProvider.java
@@ -14,6 +14,8 @@ package org.activiti.cycle.impl.connector.signavio.provider;
import java.io.InputStream;
import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import org.activiti.cycle.Content;
import org.activiti.cycle.MimeType;
@@ -25,11 +27,18 @@ import org.activiti.cycle.context.CycleSessionContext;
import org.activiti.cycle.impl.components.RuntimeConnectorList;
import org.activiti.cycle.impl.connector.signavio.SignavioConnectorConfiguration;
import org.activiti.cycle.impl.connector.signavio.SignavioConnectorInterface;
+import org.activiti.cycle.impl.connector.signavio.action.ValidateActivitiDeployment;
import org.activiti.cycle.impl.mimetype.PngMimeType;
+import org.activiti.engine.ActivitiException;
+import org.activiti.engine.impl.bpmn.diagram.ProcessDiagramGenerator;
+import org.activiti.engine.impl.bpmn.parser.BpmnParse;
+import org.activiti.engine.impl.repository.ProcessDefinitionEntity;
public abstract class AbstractPngProvider extends SignavioContentRepresentationProvider {
private static final long serialVersionUID = 1L;
+
+ private static Logger log = Logger.getLogger(AbstractPngProvider.class.getName());
public Content getContent(RepositoryArtifact artifact) {
try {
@@ -40,6 +49,30 @@ public abstract class AbstractPngProvider extends SignavioContentRepresentationP
String modelAsPngUrl = configuration.getPngUrl(artifact.getNodeId(), signavioConnector.getSecurityToken());
InputStream is = new URL(modelAsPngUrl).openStream();
content.setValue(is);
+
+ if (is.available() <= 201) { // 201 bytes is the missing PNG
+ // The Signavio PNG is very often missing if the model was not yet saved
+ // in the Modeler
+ // so we use the Activiti PNG for the moment to have anything
+ try {
+ BpmnParse parse = ValidateActivitiDeployment.createParseObject(signavioConnector, artifact);
+ try {
+ parse.execute();
+ }
+ catch (ActivitiException ex) {
+ // ignore parsing erros
+ // TODO: Think about it
+ }
+ if (parse.getProcessDefinitions().size()>0) {
+ // TODO: Only get the first pool (breaks for multiple pools!!)
+ is = ProcessDiagramGenerator.generatePngDiagram(parse.getProcessDefinitions().get(0));
+ content.setValue(is);
+ }
+ } catch (Exception ex) {
+ log.log(Level.SEVERE, "Couldn't create PNG from BPMN 2.0 XML. Ignoring.", ex);
+ }
+ }
+
return content;
} catch (Exception ex) {
throw new RepositoryException("Exception while accessing Signavio repository", ex);
@@ -57,7 +90,7 @@ public abstract class AbstractPngProvider extends SignavioContentRepresentationP
public RenderInfo getRenderInfo() {
return RenderInfo.IMAGE;
}
-
+
public boolean isForDownload() {
return true;
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/Bpmn20Provider.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/Bpmn20Provider.java
index e76a398d45..a2b4bbaa72 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/Bpmn20Provider.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/Bpmn20Provider.java
@@ -43,8 +43,8 @@ public class Bpmn20Provider extends SignavioContentRepresentationProvider {
// use the bpmn2_0_serialization export servlet to provide bpmn20 xml
// by doing this, we can support different signavio versions instead of
// the commercial Signavio only
- String jsonResponse = getJsonResponse(signavioConnector, artifact, "/json");
- JSONObject jsonData = new JSONObject(jsonResponse);
+ Response jsonResponse = getJsonResponse(signavioConnector, artifact, "/json");
+ JSONObject jsonData = new JSONObject(jsonResponse.getEntity().getText());
String result = signavioConnector.transformJsonToBpmn20Xml(jsonData.toString());
// This would have been the alternative that works only for signavio but
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/EmbeddableModelProvider.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/EmbeddableModelProvider.java
index fd679854d8..8f8e537b23 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/EmbeddableModelProvider.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/EmbeddableModelProvider.java
@@ -39,7 +39,6 @@ import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Preference;
import org.restlet.data.Reference;
-import org.restlet.engine.Engine;
import org.restlet.ext.json.JsonRepresentation;
import org.restlet.representation.Representation;
@@ -116,16 +115,12 @@ public class EmbeddableModelProvider extends SignavioContentRepresentationProvid
// Send the request and retrieve the response
Response embeddedBOResponse = client.handle(embeddedBORequest);
- embeddedBORequest.release();
// Get the ResponseBody as JSON
JsonRepresentation jsonData = new JsonRepresentation(embeddedBOResponse.getEntity());
-
+
// Transform to JSONArray
JSONArray jsonArray = jsonData.getJsonArray();
-
- embeddedBOResponse.release();
- Engine.clearThreadLocalVariables();
// Content of jsonArray above with modelID
// /model/6fd6be02c610475c9daab28a046282e2
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/Jpdl4Provider.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/Jpdl4Provider.java
index 7ccf1b7886..d40f50e0aa 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/Jpdl4Provider.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/Jpdl4Provider.java
@@ -27,7 +27,7 @@ import org.activiti.cycle.impl.connector.signavio.SignavioConnectorInterface;
import org.activiti.cycle.impl.connector.signavio.repositoryartifacttype.SignavioJpdl4ArtifactType;
import org.activiti.cycle.impl.mimetype.XmlMimeType;
import org.activiti.cycle.impl.transform.XmlToTextTransformation;
-import org.restlet.ext.json.JsonRepresentation;
+import org.restlet.Response;
import org.restlet.ext.xml.DomRepresentation;
@CycleComponent(context = CycleContextType.APPLICATION)
@@ -42,8 +42,8 @@ public class Jpdl4Provider extends SignavioContentRepresentationProvider {
SignavioConnectorInterface signavioConnector = (SignavioConnectorInterface) CycleSessionContext.get(RuntimeConnectorList.class).getConnectorById(artifact.getConnectorId());
Content content = new Content();
- String jpdlResponse = getJsonResponse(signavioConnector, artifact, "/jpdl4");
- DomRepresentation xmlData = new DomRepresentation(new JsonRepresentation( jpdlResponse ));
+ Response jpdlResponse = getJsonResponse(signavioConnector, artifact, "/jpdl4");
+ DomRepresentation xmlData = new DomRepresentation(jpdlResponse.getEntity());
XmlToTextTransformation transformation = CycleApplicationContext.get(XmlToTextTransformation.class);
String jpdl4AsString = transformation.getXmlAsString(xmlData.getDomSource());
// log.finest("JPDL4 String: " + jpdl4AsString);
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/JsonProvider.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/JsonProvider.java
index 2b29c7b020..4424134958 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/JsonProvider.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/JsonProvider.java
@@ -38,7 +38,9 @@ public class JsonProvider extends SignavioContentRepresentationProvider {
try {
SignavioConnectorInterface signavioConnector = (SignavioConnectorInterface) CycleSessionContext.get(RuntimeConnectorList.class).getConnectorById(artifact.getConnectorId());
Content content = new Content();
- String jsonString = getJsonResponse(signavioConnector, artifact, "/json");
+ Response jsonResponse = getJsonResponse(signavioConnector, artifact, "/json");
+
+ String jsonString = jsonResponse.getEntity().getText();
JSONObject jsonObj = new JSONObject(jsonString);
content.setValue(jsonObj.toString(2));
return content;
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/SignavioContentRepresentationProvider.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/SignavioContentRepresentationProvider.java
index f1c616adf8..3b2862a94e 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/SignavioContentRepresentationProvider.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/provider/SignavioContentRepresentationProvider.java
@@ -17,12 +17,13 @@ import java.io.IOException;
import org.activiti.cycle.ContentRepresentation;
import org.activiti.cycle.RepositoryArtifact;
import org.activiti.cycle.impl.connector.signavio.SignavioConnectorInterface;
+import org.restlet.Response;
public abstract class SignavioContentRepresentationProvider implements ContentRepresentation {
private static final long serialVersionUID = 1L;
- public static String getJsonResponse(SignavioConnectorInterface connector, RepositoryArtifact artifact, String urlSuffix) throws IOException {
+ public static Response getJsonResponse(SignavioConnectorInterface connector, RepositoryArtifact artifact, String urlSuffix) throws IOException {
String url = connector.getModelUrl(artifact) + urlSuffix;
return connector.getJsonResponse(url);
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/transform/pattern/RemedyTemporarySignavioIncompatibility.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/transform/pattern/RemedyTemporarySignavioIncompatibility.java
index 42eff7bef8..86f75e37d1 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/transform/pattern/RemedyTemporarySignavioIncompatibility.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/transform/pattern/RemedyTemporarySignavioIncompatibility.java
@@ -9,6 +9,8 @@ package org.activiti.cycle.impl.connector.signavio.transform.pattern;
* @author ruecker
*/
public class RemedyTemporarySignavioIncompatibility {
+
+ // name="SequenceFlow" --> name=""
public String transformBpmn20Xml(String xml, String processName) {
// set process id and name
@@ -35,6 +37,9 @@ public class RemedyTemporarySignavioIncompatibility {
xml = exchangeAttributeText(xml, "gatewayDirection", "converging", "Converging");
xml = exchangeAttributeText(xml, "gatewayDirection", "mixed", "Mixed");
+ // Signavio sets the default name to "SequenceFlow" in the BPMN 2.0 Export, which is a bit annoying in the diagram, so remove it
+ xml = exchangeAttributeText(xml, "sequenceFlow", "SequenceFlow", "");
+
xml = removeAttribute(xml, "processType");
// add namespace (yeah, pretty hacky, I know)
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/util/SignavioJsonHelper.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/util/SignavioJsonHelper.java
index 2593f539ad..db0173ab1b 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/util/SignavioJsonHelper.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/signavio/util/SignavioJsonHelper.java
@@ -1,7 +1,14 @@
package org.activiti.cycle.impl.connector.signavio.util;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
import java.util.Date;
+import org.activiti.cycle.impl.connector.signavio.SignavioConnector;
import org.json.JSONException;
import org.json.JSONObject;
@@ -28,4 +35,28 @@ public class SignavioJsonHelper {
return null;
}
}
+
+ public static String getEmptypModelTemplate() {
+ BufferedReader reader = null;
+ try {
+ InputStream is = SignavioConnector.class.getResourceAsStream("emptyProcessModelTemplate.json");
+ reader = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
+ StringWriter resultWriter = new StringWriter();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ resultWriter.append(line);
+ }
+ reader.close();
+ return resultWriter.toString();
+ } catch (IOException e) {
+ if (reader == null)
+ return null;
+ try {
+ reader.close();
+ } catch (IOException ex) {
+
+ }
+ return null;
+ }
+ }
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/view/TagConnector.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/view/TagConnector.java
index 0f005168cc..fd923fc08f 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/view/TagConnector.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/connector/view/TagConnector.java
@@ -69,7 +69,7 @@ public class TagConnector implements RepositoryConnector {
return tagFolderList;
}
-
+
public RepositoryArtifact createEmptyArtifact(String parentFolderId, String artifactName, String artifactType) throws RepositoryNodeNotFoundException {
throw new UnsupportedOperationException("Cannot create artifact in TagConnector, use real RepositoryConnector istead.");
}
@@ -173,4 +173,8 @@ public class TagConnector implements RepositoryConnector {
public void addConfigurationEntry(String key, Object value) {
// this connector is not configured
}
+
+ public String concatenateNodeId(String prefix, String suffix) {
+ return null;
+ }
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/CycleProcessSolutionDao.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/CycleProcessSolutionDao.java
new file mode 100644
index 0000000000..f805004460
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/CycleProcessSolutionDao.java
@@ -0,0 +1,32 @@
+package org.activiti.cycle.impl.db;
+
+import java.util.List;
+
+import org.activiti.cycle.impl.db.entity.ProcessSolutionEntity;
+import org.activiti.cycle.impl.db.entity.VirtualRepositoryFolderEntity;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+
+/**
+ * DAO for process solutions
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public interface CycleProcessSolutionDao {
+
+ public ProcessSolutionEntity getProcessSolutionById(String id);
+
+ public void deleteProcessSolutionById(String id);
+
+ public List getProcessSolutionList();
+
+ public VirtualRepositoryFolder getVirtualRepositoryFolderById(String id);
+
+ public List getVirtualForldersByProcessSolutionId(String id);
+
+ public ProcessSolutionEntity saveProcessSolution(ProcessSolutionEntity processSolution);
+
+ public List addVirtualFoldersToSolution(String id, List folders);
+
+ public void deleteVirtualRepositoryFolderById(String id);
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/entity/ProcessSolutionEntity.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/entity/ProcessSolutionEntity.java
new file mode 100644
index 0000000000..63fd12a8c0
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/entity/ProcessSolutionEntity.java
@@ -0,0 +1,64 @@
+package org.activiti.cycle.impl.db.entity;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.processsolution.ProcessSolutionState;
+import org.activiti.engine.impl.db.PersistentObject;
+
+/**
+ * Represents a {@link ProcessSolution}
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class ProcessSolutionEntity implements Serializable, PersistentObject, ProcessSolution {
+
+ private static final long serialVersionUID = 1L;
+
+ protected String id;
+ protected String label;
+ protected ProcessSolutionState state;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public ProcessSolutionState getState() {
+ return state;
+ }
+
+ public String getStateName() {
+ return state.name();
+ }
+
+ public void setStateName(String stateName) {
+ this.state = ProcessSolutionState.valueOf(stateName);
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public void setState(ProcessSolutionState state) {
+ this.state = state;
+ }
+
+ public Object getPersistentState() {
+ Map persistentState = new HashMap();
+ persistentState.put("id", id);
+ persistentState.put("label", label);
+ persistentState.put("state", state.name());
+ return persistentState;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/entity/RepositoryArtifactLinkEntity.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/entity/RepositoryArtifactLinkEntity.java
index 2f54a6f7ff..d51dd6c50c 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/entity/RepositoryArtifactLinkEntity.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/entity/RepositoryArtifactLinkEntity.java
@@ -29,6 +29,7 @@ public class RepositoryArtifactLinkEntity implements PersistentObject, Repositor
public static final String TYPE_REFINES = "refines";
public static final String TYPE_UNSPECIFIED = "unspecified link";
public static final String TYPE_COPY = "copy";
+ public static final String TYPE_REQUIREMENT = "defines requirement";
/**
* artificial id used as primary key to identify this link auto generated
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/entity/VirtualRepositoryFolderEntity.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/entity/VirtualRepositoryFolderEntity.java
new file mode 100644
index 0000000000..89922bc45b
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/entity/VirtualRepositoryFolderEntity.java
@@ -0,0 +1,90 @@
+package org.activiti.cycle.impl.db.entity;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+import org.activiti.engine.impl.db.PersistentObject;
+
+/**
+ * Represents a {@link VirtualRepositoryFolder}.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class VirtualRepositoryFolderEntity implements VirtualRepositoryFolder, Serializable, PersistentObject {
+
+ private static final long serialVersionUID = 1L;
+
+ private String id;
+
+ private String label;
+
+ private String connectorId;
+
+ private String referencedNodeId;
+
+ private String processSolutionId;
+
+ private String type;
+
+ public Object getPersistentState() {
+ Map persistentState = new HashMap();
+ persistentState.put("id", id);
+ persistentState.put("label", label);
+ persistentState.put("connectorId", connectorId);
+ persistentState.put("processSolutionId", processSolutionId);
+ persistentState.put("type", type);
+ persistentState.put("referencedNodeId", referencedNodeId);
+ return persistentState;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public String getConnectorId() {
+ return connectorId;
+ }
+
+ public void setConnectorId(String connectorId) {
+ this.connectorId = connectorId;
+ }
+
+ public String getReferencedNodeId() {
+ return referencedNodeId;
+ }
+
+ public void setReferencedNodeId(String referencedNodeId) {
+ this.referencedNodeId = referencedNodeId;
+ }
+
+ public String getProcessSolutionId() {
+ return processSolutionId;
+ }
+
+ public void setProcessSolutionId(String processSolutionId) {
+ this.processSolutionId = processSolutionId;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/impl/CycleDaoMyBatisImpl.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/impl/CycleDaoMyBatisImpl.java
index 0f5fad2215..91971744bf 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/impl/CycleDaoMyBatisImpl.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/db/impl/CycleDaoMyBatisImpl.java
@@ -14,22 +14,26 @@ import org.activiti.cycle.impl.CycleTagContentImpl;
import org.activiti.cycle.impl.conf.RepositoryConnectorConfigurationImpl;
import org.activiti.cycle.impl.db.CycleCommentDao;
import org.activiti.cycle.impl.db.CycleConfigurationDao;
+import org.activiti.cycle.impl.db.CycleProcessSolutionDao;
import org.activiti.cycle.impl.db.CycleRepositoryConnectorConfigurationDao;
import org.activiti.cycle.impl.db.CycleLinkDao;
import org.activiti.cycle.impl.db.CyclePeopleLinkDao;
import org.activiti.cycle.impl.db.CycleTagDao;
import org.activiti.cycle.impl.db.entity.CycleConfigEntity;
import org.activiti.cycle.impl.db.entity.CycleRepositoryConnectorConfigurationEntity;
+import org.activiti.cycle.impl.db.entity.ProcessSolutionEntity;
import org.activiti.cycle.impl.db.entity.RepositoryArtifactLinkEntity;
import org.activiti.cycle.impl.db.entity.RepositoryNodeCommentEntity;
import org.activiti.cycle.impl.db.entity.RepositoryNodePeopleLinkEntity;
import org.activiti.cycle.impl.db.entity.RepositoryNodeTagEntity;
+import org.activiti.cycle.impl.db.entity.VirtualRepositoryFolderEntity;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.identity.Group;
import org.apache.ibatis.session.SqlSession;
public class CycleDaoMyBatisImpl extends AbstractCycleDaoMyBatisImpl implements CycleCommentDao, CycleRepositoryConnectorConfigurationDao, CycleLinkDao,
- CyclePeopleLinkDao, CycleTagDao, CycleConfigurationDao {
+ CyclePeopleLinkDao, CycleTagDao, CycleConfigurationDao, CycleProcessSolutionDao {
private static Logger log = Logger.getLogger(CycleDaoMyBatisImpl.class.getName());
@@ -406,4 +410,105 @@ public class CycleDaoMyBatisImpl extends AbstractCycleDaoMyBatisImpl implements
}
}
+ public ProcessSolutionEntity getProcessSolutionById(String id) {
+ SqlSession sqlSession = openSession();
+ try {
+ return (ProcessSolutionEntity) sqlSession.selectOne("selectProcessSolutionById", id);
+ } finally {
+ sqlSession.close();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getProcessSolutionList() {
+ SqlSession sqlSession = openSession();
+ try {
+ return (List) sqlSession.selectList("selectProcessSolutions");
+ } finally {
+ sqlSession.close();
+ }
+ }
+
+ public VirtualRepositoryFolderEntity getVirtualRepositoryFolderById(String id) {
+ SqlSession sqlSession = openSession();
+ try {
+ return (VirtualRepositoryFolderEntity) sqlSession.selectOne("selectVirtualRepositoryFolderById", id);
+ } finally {
+ sqlSession.close();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getVirtualForldersByProcessSolutionId(String id) {
+ SqlSession sqlSession = openSession();
+ try {
+ return (List) sqlSession.selectList("selectVirtualRepositoryFolderByProcessSolutionId", id);
+ } finally {
+ sqlSession.close();
+ }
+ }
+
+ public List addVirtualFoldersToSolution(String id, List folders) {
+ SqlSession sqlSession = openSession();
+ try {
+ for (VirtualRepositoryFolderEntity virtualRepositoryFolderEntity : folders) {
+ virtualRepositoryFolderEntity.setProcessSolutionId(id);
+ virtualRepositoryFolderEntity.setId(UUID.randomUUID().toString());
+ sqlSession.insert("insertVirtualRepositoryFolder", virtualRepositoryFolderEntity);
+ }
+ sqlSession.commit();
+ return folders;
+ } catch (Exception e) {
+ sqlSession.rollback();
+ throw new RuntimeException(e);
+ } finally {
+ sqlSession.close();
+ }
+ }
+
+ public ProcessSolutionEntity saveProcessSolution(ProcessSolutionEntity processSolution) {
+ SqlSession sqlSession = openSession();
+ try {
+ if (processSolution.getId() == null) {
+ processSolution.setId(UUID.randomUUID().toString());
+ sqlSession.insert("insertProcessSolution", processSolution);
+ } else {
+ sqlSession.update("updateProcessSolution", processSolution);
+ }
+ sqlSession.commit();
+ return processSolution;
+ } catch (Exception e) {
+ sqlSession.rollback();
+ throw new RuntimeException(e);
+ } finally {
+ sqlSession.close();
+ }
+ }
+
+ public void deleteProcessSolutionById(String id) {
+ SqlSession sqlSession = openSession();
+ try {
+ sqlSession.delete("deleteProcessSolution", id);
+ sqlSession.commit();
+ } catch (Exception e) {
+ sqlSession.rollback();
+ throw new RuntimeException(e);
+ } finally {
+ sqlSession.close();
+ }
+ }
+
+ public void deleteVirtualRepositoryFolderById(String id) {
+ SqlSession sqlSession = openSession();
+ try {
+ sqlSession.delete("deleteVirtualRepositoryFolder", id);
+ sqlSession.commit();
+ } catch (Exception e) {
+ sqlSession.rollback();
+ throw new RuntimeException(e);
+ } finally {
+ sqlSession.close();
+ }
+ }
+
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/event/CycleEvents.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/event/CycleEvents.java
new file mode 100644
index 0000000000..35b760a865
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/event/CycleEvents.java
@@ -0,0 +1,71 @@
+package org.activiti.cycle.impl.event;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleApplicationContext;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.event.CycleEventListener;
+
+/**
+ * {@link CycleContextType#APPLICATION}-scoped component for managing events.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class CycleEvents {
+
+ protected Map, Set>> eventListenerMap = null;
+
+ public Set> getEventListeners(Class forEvent) {
+ Set> resultSet = new HashSet>();
+ init();
+ if (eventListenerMap.get(forEvent) != null) {
+ for (CycleEventListener< ? > cycleEventListener : eventListenerMap.get(forEvent)) {
+ resultSet.add((CycleEventListener) cycleEventListener);
+ }
+ }
+ return resultSet;
+ }
+
+ protected void init() {
+ if (eventListenerMap == null) {
+ synchronized (this) {
+ if (eventListenerMap == null) {
+ eventListenerMap = new HashMap, Set>>();
+ Set> allImplementations = CycleComponentFactory.getAllImplementations(CycleEventListener.class);
+ for (Class class1 : allImplementations) {
+ Type[] parameterizedTypes = class1.getGenericInterfaces();
+ for (Type type : parameterizedTypes) {
+ if (!(type instanceof ParameterizedType)) {
+ continue;
+ }
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ for (Type eventType : parameterizedType.getActualTypeArguments()) {
+ if (!(eventType instanceof Class)) {
+ continue;
+ }
+ // found the event type
+ Class< ? > eventClass = (Class< ? >) eventType;
+ Set> listenersForThisType = eventListenerMap.get(eventClass);
+ if (listenersForThisType == null) {
+ listenersForThisType = new HashSet>();
+ eventListenerMap.put(eventClass, listenersForThisType);
+ }
+ listenersForThisType.add(CycleApplicationContext.get(class1));
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/CurrentProcessesFolder.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/CurrentProcessesFolder.java
new file mode 100644
index 0000000000..bdec333108
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/CurrentProcessesFolder.java
@@ -0,0 +1,34 @@
+package org.activiti.cycle.impl.processsolution;
+
+import java.io.Serializable;
+
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+
+/**
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(name = "currentProcessesFolder", context = CycleContextType.SESSION) // TODO: make REQUEST-scoped
+public class CurrentProcessesFolder implements Serializable {
+
+ private String connectorId;
+ private String folderId;
+
+ public String getConnectorId() {
+ return connectorId;
+ }
+
+ public void setConnectorId(String connectorId) {
+ this.connectorId = connectorId;
+ }
+
+ public String getFolderId() {
+ return folderId;
+ }
+
+ public void setFolderId(String folderId) {
+ this.folderId = folderId;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/DefaultProcessSolutionTemplate.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/DefaultProcessSolutionTemplate.java
new file mode 100644
index 0000000000..99150dbf21
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/DefaultProcessSolutionTemplate.java
@@ -0,0 +1,83 @@
+package org.activiti.cycle.impl.processsolution;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.impl.db.entity.VirtualRepositoryFolderEntity;
+import org.activiti.cycle.processsolution.ProcessSolutionState;
+import org.activiti.cycle.processsolution.ProcessSolutionTemplate;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+import org.activiti.cycle.service.CycleConfigurationService;
+import org.activiti.cycle.service.CycleServiceFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Application-scoped {@link CycleComponent} representing the default
+ * {@link ProcessSolutionTemplate}
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class DefaultProcessSolutionTemplate implements ProcessSolutionTemplate {
+
+ private CycleConfigurationService configurationService = CycleServiceFactory.getConfigurationService();
+
+ public List getVirtualRepositoryFolders() {
+ try {
+ return getVirtualRepositoryFoldersFromConfiguration();
+ } catch (Exception e) {
+ throw new RuntimeException("Default ProcessSolutionTemplate coult not be loaded: " + e.getMessage(), e);
+ }
+ }
+
+ private List getVirtualRepositoryFoldersFromConfiguration() throws Exception {
+ // load template using the configurationService
+ String configurationString = configurationService.getConfigurationValue("processSolutionTemplates", "default");
+ if (configurationString == null) {
+ throw new RuntimeException("No ProcessSolutionTemplate found using group='processSolutionTemplates' and key='default'");
+ }
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ DocumentBuilder db = dbf.newDocumentBuilder();
+
+ Document configurationDocument = db.parse(new ByteArrayInputStream(configurationString.getBytes()));
+
+ List resultList = new ArrayList();
+
+ NodeList vFolderElementList = configurationDocument.getElementsByTagName("vFolder");
+ for (int i = 0; i < vFolderElementList.getLength(); i++) {
+ Node node = vFolderElementList.item(i);
+ if (!(node instanceof Element)) {
+ continue;
+ }
+ Element element = (Element) node;
+ String type = element.getAttribute("type");
+ String name = element.getAttribute("name");
+ String connectorId = element.getAttribute("connectorId");
+ String referencedNodeId = element.getAttribute("referencedNodeId");
+ VirtualRepositoryFolderEntity virtualRepositoryFolderEntity = new VirtualRepositoryFolderEntity();
+ virtualRepositoryFolderEntity.setType(type);
+ virtualRepositoryFolderEntity.setLabel(name);
+ virtualRepositoryFolderEntity.setConnectorId(connectorId);
+ virtualRepositoryFolderEntity.setReferencedNodeId(referencedNodeId);
+ resultList.add(virtualRepositoryFolderEntity);
+ }
+
+ return resultList;
+
+ }
+
+ public ProcessSolutionState getInitialState() {
+ return ProcessSolutionState.IN_SPECIFICATION;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/LinkRequirementWithProcessDiagramAction.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/LinkRequirementWithProcessDiagramAction.java
new file mode 100644
index 0000000000..5aa77cf40b
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/LinkRequirementWithProcessDiagramAction.java
@@ -0,0 +1,87 @@
+package org.activiti.cycle.impl.processsolution;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.RepositoryArtifact;
+import org.activiti.cycle.RepositoryArtifactLink;
+import org.activiti.cycle.RepositoryArtifactType;
+import org.activiti.cycle.RepositoryConnector;
+import org.activiti.cycle.action.ArtifactAwareParameterizedAction;
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.impl.ParameterizedHtmlFormTemplateAction;
+import org.activiti.cycle.impl.db.entity.RepositoryArtifactLinkEntity;
+import org.activiti.cycle.impl.processsolution.connector.ProcessSolutionArtifact;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+import org.activiti.cycle.service.CycleProcessSolutionService;
+import org.activiti.cycle.service.CycleRepositoryService;
+import org.activiti.cycle.service.CycleServiceFactory;
+
+/**
+ * Action for linking Requirements with ProcessDiagrams
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class LinkRequirementWithProcessDiagramAction extends ParameterizedHtmlFormTemplateAction implements ArtifactAwareParameterizedAction,
+ ProcessSolutionAction {
+
+ public LinkRequirementWithProcessDiagramAction() {
+ super("Link Requirement with Process Model");
+ }
+
+ public void execute(RepositoryConnector connector, RepositoryArtifact artifact, Map parameters) throws Exception {
+ CycleRepositoryService repositoryService = CycleServiceFactory.getRepositoryService();
+
+ String comment = (String) parameters.get("comment");
+ String targetArtifactId = (String) parameters.get("targetArtifactId");
+ RepositoryConnector targetConnector = (RepositoryConnector) parameters.get("targetConnectorId");
+ RepositoryArtifact requirementsArtifact = artifact;
+ RepositoryArtifact processModelArtifact = repositoryService.getRepositoryArtifact(targetConnector.getId(), targetArtifactId);
+
+ RepositoryArtifactLink link = new RepositoryArtifactLinkEntity();
+ link.setComment(comment);
+ link.setSourceArtifact(requirementsArtifact);
+ link.setTargetArtifact(processModelArtifact);
+ link.setLinkType(RepositoryArtifactLinkEntity.TYPE_REQUIREMENT);
+ repositoryService.addArtifactLink(link);
+
+ }
+ public Set getArtifactTypes() {
+ // "null" means for all.
+ return null;
+ }
+
+ public boolean isApplicable(RepositoryArtifact toArtifact) {
+ if (toArtifact instanceof ProcessSolutionArtifact) {
+ ProcessSolutionArtifact processSolutionArtifact = (ProcessSolutionArtifact) toArtifact;
+ if (processSolutionArtifact.getVirtualRepositoryFolder() == null) {
+ return false;
+ }
+ if ("Requirements".equals(processSolutionArtifact.getVirtualRepositoryFolder().getType())) {
+ // set values for the 'currentProcessesFolder' in the request context
+ CurrentProcessesFolder currentProcessesFolder = CycleComponentFactory.getCycleComponentInstance("currentProcessesFolder", CurrentProcessesFolder.class);
+
+ CycleProcessSolutionService processService = CycleServiceFactory.getProcessSolutionService();
+ List folders = processService.getFoldersForProcessSolution(processSolutionArtifact.getVirtualRepositoryFolder()
+ .getProcessSolutionId());
+ for (VirtualRepositoryFolder virtualRepositoryFolder : folders) {
+ if (virtualRepositoryFolder.getType().equals("Processes")) {
+ currentProcessesFolder.setFolderId(virtualRepositoryFolder.getProcessSolutionId() + "/" + virtualRepositoryFolder.getId());
+ currentProcessesFolder.setConnectorId("ps-" + virtualRepositoryFolder.getProcessSolutionId());
+ }
+ }
+
+ return true;
+ }
+ }
+ return false;
+ }
+ public String getFormResourceName() {
+ return getDefaultFormName();
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/ProcessSolutionAction.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/ProcessSolutionAction.java
new file mode 100644
index 0000000000..9abbcf6f2a
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/ProcessSolutionAction.java
@@ -0,0 +1,14 @@
+package org.activiti.cycle.impl.processsolution;
+
+
+import org.activiti.cycle.action.Action;
+import org.activiti.cycle.impl.processsolution.connector.ProcessSolutionArtifact;
+
+/**
+ * Marker-interface for actions on {@link ProcessSolutionArtifact}s.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public interface ProcessSolutionAction extends Action {
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/ProcessSolutionCreate.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/ProcessSolutionCreate.java
new file mode 100644
index 0000000000..e55597ebf7
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/ProcessSolutionCreate.java
@@ -0,0 +1,154 @@
+package org.activiti.cycle.impl.processsolution;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.RepositoryConnector;
+import org.activiti.cycle.RepositoryFolder;
+import org.activiti.cycle.RepositoryNodeCollection;
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.impl.components.RuntimeConnectorList;
+import org.activiti.cycle.impl.connector.util.TransactionalConnectorUtils;
+import org.activiti.cycle.impl.db.entity.ProcessSolutionEntity;
+import org.activiti.cycle.impl.db.entity.VirtualRepositoryFolderEntity;
+import org.activiti.cycle.impl.service.CycleProcessSolutionServiceImpl;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.processsolution.ProcessSolutionTemplate;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+import org.activiti.cycle.service.CycleServiceFactory;
+
+/**
+ * Cycle Component for creating new {@link ProcessSolution}s
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(name = "processSolutionCreate", context = CycleContextType.REQUEST)
+public class ProcessSolutionCreate {
+
+ private CycleProcessSolutionServiceImpl processSolutionService = (CycleProcessSolutionServiceImpl) CycleServiceFactory.getProcessSolutionService();
+ private RuntimeConnectorList connectorList = CycleComponentFactory.getCycleComponentInstance(RuntimeConnectorList.class, RuntimeConnectorList.class);
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * returns a name for the {@link ProcessSolution} to be used as folder name in
+ * connectors.
+ */
+ protected String getNameForFs() {
+ String name = "";
+ for (char c : this.name.toCharArray()) {
+ if (Character.isLetter(c) || Character.isDigit(c)) { // only use letters
+ // and digits
+ name += c;
+ }
+ }
+ return name;
+ }
+
+ public String createNewProcessSolution() {
+ ProcessSolutionTemplate template = processSolutionService.getDefaultProcessSolutionTemplate();
+
+ ProcessSolutionEntity processSolutionEntity = createProcessSolutionEntity(template);
+
+ List createdVirtualFolders = new ArrayList();
+
+ // get participating connectors
+ Set participatingConnectors = getParticipatingConnectors(template);
+ // begin transactions:
+ for (RepositoryConnector participatingConnector : participatingConnectors) {
+ TransactionalConnectorUtils.beginTransaction(participatingConnector);
+ }
+
+ try {
+ // create folders in repositories
+ for (VirtualRepositoryFolder folderTemplate : template.getVirtualRepositoryFolders()) {
+ createdVirtualFolders.add(createVirtualFolder(folderTemplate, processSolutionEntity));
+ }
+
+ processSolutionService.getDao().addVirtualFoldersToSolution(processSolutionEntity.getId(), createdVirtualFolders);
+
+ // commit repository transactions:
+ for (RepositoryConnector participatingConnector : participatingConnectors) {
+ TransactionalConnectorUtils.commitTransaction(participatingConnector, "created new process solution " + name);
+ }
+
+ return processSolutionEntity.getId();
+
+ } catch (Exception e) {
+
+ // rollback repository transactions:
+ for (RepositoryConnector participatingConnector : participatingConnectors) {
+ TransactionalConnectorUtils.rollbackTransaction(participatingConnector);
+ }
+
+ // delete virtual folders:
+ for (VirtualRepositoryFolder virtualFolder : createdVirtualFolders) {
+ processSolutionService.getDao().deleteVirtualRepositoryFolderById(virtualFolder.getId());
+ }
+
+ // delete process solution:
+ processSolutionService.getDao().deleteProcessSolutionById(processSolutionEntity.getId());
+
+ throw new RuntimeException("Could not create ProcessSolution " + e.getMessage(), e);
+ }
+ }
+
+ private ProcessSolutionEntity createProcessSolutionEntity(ProcessSolutionTemplate template) {
+ ProcessSolutionEntity processSolutionEntity = new ProcessSolutionEntity();
+ processSolutionEntity.setLabel(name);
+ processSolutionEntity.setState(template.getInitialState());
+ return processSolutionService.getDao().saveProcessSolution(processSolutionEntity);
+ }
+
+ private VirtualRepositoryFolderEntity createVirtualFolder(VirtualRepositoryFolder folderTemplate, ProcessSolutionEntity processSolutionEntity) {
+ String nodeId = folderTemplate.getReferencedNodeId();
+ String connectorId = folderTemplate.getConnectorId();
+ RepositoryConnector connector = connectorList.getConnectorById(connectorId);
+ RepositoryFolder parentFolder = connector.getRepositoryFolder(nodeId);
+
+ // create or get folder for this processSolution using connector
+ RepositoryNodeCollection childNodeCollection = connector.getChildren(parentFolder.getNodeId());
+ RepositoryFolder folderForThisProcessSolution = null;
+ for (RepositoryFolder folder : childNodeCollection.getFolderList()) {
+ if (folder.getMetadata().getName().equals(getNameForFs())) {
+ // found folder for this processSolution
+ folderForThisProcessSolution = folder;
+ }
+ }
+ if (folderForThisProcessSolution == null) {
+ // create new folder for this processSolution
+ folderForThisProcessSolution = connector.createFolder(parentFolder.getNodeId(), getNameForFs());
+ }
+
+ // create new folder under folder for this ProcessSolution:
+ RepositoryFolder actualFolder = connector.createFolder(folderForThisProcessSolution.getNodeId(), folderTemplate.getLabel());
+
+ // create entity
+ VirtualRepositoryFolderEntity entity = new VirtualRepositoryFolderEntity();
+ entity.setLabel(folderTemplate.getLabel());
+ entity.setConnectorId(connectorId);
+ entity.setProcessSolutionId(processSolutionEntity.getId());
+ entity.setReferencedNodeId(actualFolder.getNodeId());
+ entity.setType(folderTemplate.getType());
+ return entity;
+ }
+
+ private Set getParticipatingConnectors(ProcessSolutionTemplate template) {
+ Set resultList = new HashSet();
+ for (VirtualRepositoryFolder virtualFolder : template.getVirtualRepositoryFolders()) {
+ resultList.add(connectorList.getConnectorById(virtualFolder.getConnectorId()));
+ }
+ return resultList;
+ }
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/ProcessSolutionUtils.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/ProcessSolutionUtils.java
new file mode 100644
index 0000000000..09e43558f2
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/ProcessSolutionUtils.java
@@ -0,0 +1,73 @@
+package org.activiti.cycle.impl.processsolution;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.RepositoryArtifact;
+import org.activiti.cycle.RepositoryConnector;
+import org.activiti.cycle.RepositoryFolder;
+import org.activiti.cycle.RepositoryNodeCollection;
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.impl.components.RuntimeConnectorList;
+import org.activiti.cycle.impl.connector.signavio.repositoryartifacttype.SignavioBpmn20ArtifactType;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+import org.activiti.cycle.service.CycleProcessSolutionService;
+import org.activiti.cycle.service.CycleServiceFactory;
+
+/**
+ * Utils for handling process solutions
+ *
+ * TODO: expose some of these methods in the {@link CycleProcessSolutionService}
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.NONE)
+public class ProcessSolutionUtils {
+
+ private CycleProcessSolutionService processSolutionService = CycleServiceFactory.getProcessSolutionService();
+
+ public List getProcessModels(VirtualRepositoryFolder processes) {
+ RepositoryConnector connector = RuntimeConnectorList.getMyConnectorById(processes.getConnectorId());
+ List resultList = new ArrayList();
+ String currentFolder = processes.getReferencedNodeId();
+ getProcessModelsRec(connector, currentFolder, resultList);
+ return resultList;
+ }
+
+ public VirtualRepositoryFolder getImplementationFolder(ProcessSolution processSolution) {
+ // TODO: add dedicate query for this
+ List virtualFolders = processSolutionService.getFoldersForProcessSolution(processSolution.getId());
+ for (VirtualRepositoryFolder virtualRepositoryFolder : virtualFolders) {
+ if ("Implementation".equals(virtualRepositoryFolder.getType())) {
+ return virtualRepositoryFolder;
+ }
+ }
+ return null;
+ }
+
+ public VirtualRepositoryFolder getProcessesFolder(ProcessSolution processSolution) {
+ // TODO: add dedicate query for this
+ List virtualFolders = processSolutionService.getFoldersForProcessSolution(processSolution.getId());
+ for (VirtualRepositoryFolder virtualRepositoryFolder : virtualFolders) {
+ if ("Processes".equals(virtualRepositoryFolder.getType())) {
+ return virtualRepositoryFolder;
+ }
+ }
+ return null;
+ }
+
+ private void getProcessModelsRec(RepositoryConnector connector, String currentFolder, List resultList) {
+ RepositoryNodeCollection childNodes = connector.getChildren(currentFolder);
+ for (RepositoryArtifact repositoryArtifact : childNodes.getArtifactList()) {
+ if (repositoryArtifact.getArtifactType().equals(CycleComponentFactory.getCycleComponentInstance(SignavioBpmn20ArtifactType.class))) {
+ resultList.add(repositoryArtifact);
+ }
+ }
+ for (RepositoryFolder folder : childNodes.getFolderList()) {
+ getProcessModelsRec(connector, folder.getNodeId(), resultList);
+ }
+ }
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/artifacttype/ProcessSolutionHomeArtifactType.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/artifacttype/ProcessSolutionHomeArtifactType.java
new file mode 100644
index 0000000000..48bdb84d8a
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/artifacttype/ProcessSolutionHomeArtifactType.java
@@ -0,0 +1,21 @@
+package org.activiti.cycle.impl.processsolution.artifacttype;
+
+import org.activiti.cycle.RepositoryArtifactType;
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.impl.artifacttype.AbstractRepositoryArtifactType;
+import org.activiti.cycle.processsolution.ProcessSolution;
+
+/**
+ * {@link RepositoryArtifactType} for {@link ProcessSolution}-Home folders
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class ProcessSolutionHomeArtifactType extends AbstractRepositoryArtifactType implements RepositoryArtifactType {
+
+ public String getName() {
+ return "Process Solution Home";
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionArtifact.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionArtifact.java
new file mode 100644
index 0000000000..54086d5ecb
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionArtifact.java
@@ -0,0 +1,29 @@
+package org.activiti.cycle.impl.processsolution.connector;
+
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.RepositoryArtifact;
+import org.activiti.cycle.RepositoryArtifactType;
+import org.activiti.cycle.impl.processsolution.artifacttype.ProcessSolutionHomeArtifactType;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+
+public class ProcessSolutionArtifact extends ProcessSolutionRepositoryNode implements RepositoryArtifact {
+
+ public ProcessSolutionArtifact(String connectorId, String nodeId, VirtualRepositoryFolder virtualFolder, ProcessSolution processSolution,
+ RepositoryArtifact wrappedArtifact) {
+ super(connectorId, nodeId, virtualFolder, processSolution, wrappedArtifact);
+ }
+
+ public RepositoryArtifactType getArtifactType() {
+ if (wrappedNode != null) {
+ return ((RepositoryArtifact) wrappedNode).getArtifactType();
+ } else if (getNodeId().endsWith(ProcessSolutionConnector.PS_HOME_NAME)) {
+ return CycleComponentFactory.getCycleComponentInstance(ProcessSolutionHomeArtifactType.class, ProcessSolutionHomeArtifactType.class);
+ }
+ return null;
+ }
+
+ public RepositoryArtifact getWrappedNode() {
+ return (RepositoryArtifact) super.getWrappedNode();
+ }
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionConnector.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionConnector.java
new file mode 100644
index 0000000000..c16c13e3cf
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionConnector.java
@@ -0,0 +1,350 @@
+package org.activiti.cycle.impl.processsolution.connector;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.activiti.cycle.Content;
+import org.activiti.cycle.ContentRepresentation;
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.RepositoryArtifact;
+import org.activiti.cycle.RepositoryConnector;
+import org.activiti.cycle.RepositoryException;
+import org.activiti.cycle.RepositoryFolder;
+import org.activiti.cycle.RepositoryNode;
+import org.activiti.cycle.RepositoryNodeCollection;
+import org.activiti.cycle.RepositoryNodeNotFoundException;
+import org.activiti.cycle.action.ParameterizedAction;
+import org.activiti.cycle.context.CycleRequestContext;
+import org.activiti.cycle.impl.RepositoryNodeCollectionImpl;
+import org.activiti.cycle.impl.components.RuntimeConnectorList;
+import org.activiti.cycle.impl.processsolution.ProcessSolutionAction;
+import org.activiti.cycle.impl.processsolution.representation.ProcessSolutionHomeContentRepresentation;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+import org.activiti.cycle.service.CyclePluginService;
+import org.activiti.cycle.service.CycleProcessSolutionService;
+import org.activiti.cycle.service.CycleRepositoryService;
+import org.activiti.cycle.service.CycleServiceFactory;
+
+/**
+ * Virtual {@link RepositoryConnector} for {@link ProcessSolution}s
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class ProcessSolutionConnector implements RepositoryConnector {
+
+ protected CycleProcessSolutionService processSolutionService = CycleServiceFactory.getProcessSolutionService();
+
+ protected CycleRepositoryService repositoryService = CycleServiceFactory.getRepositoryService();
+
+ public static String PS_HOME_NAME = "/PSHOME";
+
+ protected final String processSolutionId;
+
+ public ProcessSolutionConnector(String id) {
+ processSolutionId = id;
+ }
+
+ public boolean login(String username, String password) {
+ // does not require login
+ return true;
+ }
+
+ public static String getProcessSolutionId(String nodeId) {
+ if (nodeId == null) {
+ return null;
+ }
+ if (!nodeId.contains("/")) {
+ return nodeId;
+ }
+ return nodeId.substring(0, nodeId.indexOf("/"));
+ }
+
+ public static String getVirtualFolderId(String nodeId) {
+ if (nodeId == null) {
+ return null;
+ }
+ String[] parts = nodeId.split("/");
+ if (parts.length >= 2) {
+ return parts[1];
+ }
+ return null;
+ }
+
+ public RepositoryNode getRepositoryNode(String id) throws RepositoryNodeNotFoundException {
+
+ String processSolutionId = getProcessSolutionId(id);
+ String virtualFolderId = getVirtualFolderId(id);
+ VirtualRepositoryFolder virtualFolder = null;
+ if (virtualFolderId != null) {
+ virtualFolder = processSolutionService.getVirtualRepositoryFolderById(virtualFolderId);
+ if (virtualFolder == null) {
+ virtualFolderId = null;
+ }
+ }
+ if ("".equals(id) || id == null) {
+ throw new RepositoryNodeNotFoundException(id);
+ }
+
+ if ("/".equals(id)) {
+ processSolutionId = this.processSolutionId;
+ }
+
+ if (!processSolutionId.equals(this.processSolutionId)) {
+ processSolutionId = null;
+ }
+ ProcessSolution processSolution;
+ RepositoryConnector connector;
+ // get the vFolderId from the request:
+ String vFolderId = CycleRequestContext.get("vFolderId", String.class);
+ if (vFolderId != null && virtualFolderId == null && processSolutionId == null) {
+ virtualFolderId = vFolderId;
+ virtualFolder = processSolutionService.getVirtualRepositoryFolderById(vFolderId);
+ processSolutionId = virtualFolder.getProcessSolutionId();
+ connector = CycleComponentFactory.getCycleComponentInstance(RuntimeConnectorList.class, RuntimeConnectorList.class).getConnectorById(
+ virtualFolder.getConnectorId());
+ processSolution = processSolutionService.getProcessSolutionById(processSolutionId);
+ if (virtualFolder.getReferencedNodeId().equals(id)) {
+ return new ProcessSolutionFolder(getId(), "/" + processSolutionId + "/" + virtualFolderId, null, processSolution, null);
+ }
+ } else {
+
+ if (processSolutionId == null) {
+ throw new RepositoryNodeNotFoundException(id);
+ }
+
+ if (processSolutionId.equals(this.processSolutionId) == false) {
+ throw new RepositoryNodeNotFoundException(id);
+ }
+ // id=id of a processSolution
+ processSolution = processSolutionService.getProcessSolutionById(processSolutionId);
+
+ if (id.endsWith(PS_HOME_NAME)) {
+ return new ProcessSolutionArtifact(getId(), id, null, processSolution, null);
+ }
+
+ if (virtualFolderId == null) {
+ return new ProcessSolutionFolder(getId(), id, null, processSolution, null);
+ }
+
+ virtualFolder = processSolutionService.getVirtualRepositoryFolderById(virtualFolderId);
+ String relativePath = id.replace(processSolutionId + "/" + virtualFolderId, "");
+ if (relativePath.length() == 0) {
+ // id == processsolution/virtualFolderId
+ return new ProcessSolutionFolder(getId(), id, virtualFolder, processSolution, null);
+ }
+ relativePath = id.replace(processSolutionId + "/" + virtualFolderId + "/", "");
+
+ // id == processsolution/virtualFolderId/...
+ connector = CycleComponentFactory.getCycleComponentInstance(RuntimeConnectorList.class, RuntimeConnectorList.class).getConnectorById(
+ virtualFolder.getConnectorId());
+ id = connector.concatenateNodeId(virtualFolder.getReferencedNodeId(), relativePath);
+ }
+
+ try {
+ RepositoryFolder folder = connector.getRepositoryFolder(id);
+ return new ProcessSolutionFolder(getId(), id, virtualFolder, processSolution, folder);
+ } catch (Exception e) {
+ RepositoryArtifact artifact = connector.getRepositoryArtifact(id);
+ return new ProcessSolutionArtifact(getId(), id, virtualFolder, processSolution, artifact);
+ }
+
+ }
+ public RepositoryArtifact getRepositoryArtifact(String id) throws RepositoryNodeNotFoundException {
+ return (RepositoryArtifact) getRepositoryNode(id);
+ }
+
+ public Content getRepositoryArtifactPreview(String artifactId) throws RepositoryNodeNotFoundException {
+ return null;
+ }
+
+ public RepositoryFolder getRepositoryFolder(String id) throws RepositoryNodeNotFoundException {
+ return (RepositoryFolder) getRepositoryNode(id);
+ }
+
+ public RepositoryNodeCollection getChildren(String id) throws RepositoryNodeNotFoundException {
+ List resultList = new ArrayList();
+ ProcessSolutionFolder processSolutionFolder = (ProcessSolutionFolder) getRepositoryNode(id);
+ VirtualRepositoryFolder virtualFolder = processSolutionFolder.getVirtualRepositoryFolder();
+ RepositoryFolder wrappedFolder = (RepositoryFolder) processSolutionFolder.getWrappedNode();
+ RepositoryNodeCollection childNodes = null;
+ if (wrappedFolder != null) {
+ // get child nodes of wrapped folder
+ childNodes = repositoryService.getChildren(wrappedFolder.getConnectorId(), wrappedFolder.getNodeId());
+ } else if (virtualFolder != null) {
+ // get child nodes of virtual folder
+ childNodes = repositoryService.getChildren(virtualFolder.getConnectorId(), virtualFolder.getReferencedNodeId());
+ }
+ if (childNodes != null) {
+ for (RepositoryNode childNode : childNodes.asList()) {
+ String childNodeId = childNode.getNodeId();
+ childNodeId = childNodeId.replace(processSolutionFolder.getVirtualRepositoryFolder().getReferencedNodeId(), "");
+ childNodeId = processSolutionId + "/" + processSolutionFolder.getVirtualRepositoryFolder().getId() + "/" + childNodeId;
+ if (childNode instanceof RepositoryArtifact) {
+ resultList.add(new ProcessSolutionArtifact(getId(), childNodeId, processSolutionFolder.getVirtualRepositoryFolder(), processSolutionFolder
+ .getProcessSolution(), (RepositoryArtifact) childNode));
+ } else {
+ resultList.add(new ProcessSolutionFolder(getId(), childNodeId, processSolutionFolder.getVirtualRepositoryFolder(), processSolutionFolder
+ .getProcessSolution(), (RepositoryFolder) childNode));
+ }
+ }
+ } else {
+ // add Home folder:
+// resultList.add(new ProcessSolutionArtifact(getId(), processSolutionId + PS_HOME_NAME, null, processSolutionFolder.processSolution, null));
+
+ // get children of process solution:
+ for (VirtualRepositoryFolder virtualChildfolder : processSolutionService.getFoldersForProcessSolution(processSolutionId)) {
+ String childNodeId = processSolutionId + "/" + virtualChildfolder.getId();
+ resultList.add(new ProcessSolutionFolder(getId(), childNodeId, virtualChildfolder, processSolutionFolder.getProcessSolution(), null));
+ }
+ }
+ return new RepositoryNodeCollectionImpl(resultList);
+ }
+
+ public RepositoryArtifact createArtifact(String parentFolderId, String artifactName, String artifactType, Content artifactContent)
+ throws RepositoryNodeNotFoundException {
+ ProcessSolutionFolder folder = (ProcessSolutionFolder) getRepositoryNode(parentFolderId);
+ if (folder.getVirtualRepositoryFolder() == null) {
+ throw new RepositoryException("Cannot create artifact in the top-level folder. ");
+ }
+ RepositoryArtifact newArtifact = null;
+ if (folder.getWrappedNode() == null) {
+ newArtifact = repositoryService.createArtifact(folder.getVirtualRepositoryFolder().getConnectorId(), folder.getVirtualRepositoryFolder()
+ .getReferencedNodeId(), artifactName, artifactType, artifactContent);
+ } else {
+ newArtifact = repositoryService.createArtifact(folder.getWrappedNode().getConnectorId(), folder.getWrappedNode().getNodeId(), artifactName, artifactType,
+ artifactContent);
+ }
+ String relativePath = newArtifact.getNodeId().replace(folder.getVirtualRepositoryFolder().getReferencedNodeId(), "");
+ String virtualPath = folder.getNodeId() + "/" + relativePath;
+ return new ProcessSolutionArtifact(getId(), virtualPath, folder.getVirtualRepositoryFolder(), folder.getProcessSolution(), newArtifact);
+ }
+
+ public RepositoryArtifact createArtifactFromContentRepresentation(String parentFolderId, String artifactName, String artifactType,
+ String contentRepresentationName, Content artifactContent) throws RepositoryNodeNotFoundException {
+ return createArtifact(parentFolderId, artifactName, artifactType, artifactContent);
+ }
+
+ public RepositoryArtifact createEmptyArtifact(String parentFolderId, String artifactName, String artifactType) throws RepositoryNodeNotFoundException {
+ ProcessSolutionFolder folder = (ProcessSolutionFolder) getRepositoryNode(parentFolderId);
+ if (folder.getVirtualRepositoryFolder() == null) {
+ throw new RepositoryException("Cannot create artifact in the top-level folder. ");
+ }
+ RepositoryArtifact newArtifact = null;
+ if (folder.getWrappedNode() == null) {
+ newArtifact = repositoryService.createEmptyArtifact(folder.getVirtualRepositoryFolder().getConnectorId(), folder.getVirtualRepositoryFolder()
+ .getReferencedNodeId(), artifactName, artifactType);
+ } else {
+ newArtifact = repositoryService.createEmptyArtifact(folder.getWrappedNode().getConnectorId(), folder.getWrappedNode().getNodeId(), artifactName,
+ artifactType);
+ }
+ String relativePath = newArtifact.getNodeId().replace(folder.getVirtualRepositoryFolder().getReferencedNodeId(), "");
+ String virtualPath = folder.getNodeId() + "/" + relativePath;
+ return new ProcessSolutionArtifact(getId(), virtualPath, folder.getVirtualRepositoryFolder(), folder.getProcessSolution(), newArtifact);
+ }
+
+ public RepositoryFolder createFolder(String parentFolderId, String name) throws RepositoryNodeNotFoundException {
+ ProcessSolutionFolder folder = (ProcessSolutionFolder) getRepositoryNode(parentFolderId);
+ if (folder.getVirtualRepositoryFolder() == null) {
+ throw new RepositoryException("Cannot create artifact in the top-level folder. ");
+ }
+ if (folder.getWrappedNode() == null) {
+ return repositoryService.createFolder(folder.getVirtualRepositoryFolder().getConnectorId(), folder.getVirtualRepositoryFolder().getReferencedNodeId(),
+ name);
+ }
+ return repositoryService.createFolder(folder.getWrappedNode().getConnectorId(), folder.getWrappedNode().getNodeId(), name);
+ }
+
+ public Content getContent(String artifactId) throws RepositoryNodeNotFoundException {
+ ProcessSolutionArtifact artifact = (ProcessSolutionArtifact) getRepositoryNode(artifactId);
+ if (artifactId.endsWith(PS_HOME_NAME)) {
+ return CycleComponentFactory.getCycleComponentInstance(ProcessSolutionHomeContentRepresentation.class, ProcessSolutionHomeContentRepresentation.class)
+ .getContent(artifact);
+ }
+ return repositoryService.getContent(artifact.wrappedNode.getConnectorId(), artifact.wrappedNode.getNodeId());
+ }
+
+ public void updateContent(String artifactId, Content content) throws RepositoryNodeNotFoundException {
+ ProcessSolutionArtifact artifact = (ProcessSolutionArtifact) getRepositoryNode(artifactId);
+ repositoryService.updateContent(artifact.wrappedNode.getConnectorId(), artifact.wrappedNode.getNodeId(), content);
+ }
+
+ public void updateContent(String artifactId, String contentRepresentationName, Content content) throws RepositoryNodeNotFoundException {
+ updateContent(artifactId, content);
+ }
+
+ public void deleteArtifact(String artifactId) throws RepositoryNodeNotFoundException {
+ ProcessSolutionArtifact artifact = (ProcessSolutionArtifact) getRepositoryNode(artifactId);
+ repositoryService.deleteArtifact(artifact.wrappedNode.getConnectorId(), artifact.wrappedNode.getNodeId());
+ }
+
+ public void deleteFolder(String folderId) throws RepositoryNodeNotFoundException {
+ ProcessSolutionFolder folder = (ProcessSolutionFolder) getRepositoryNode(folderId);
+ repositoryService.deleteFolder(folder.wrappedNode.getConnectorId(), folder.wrappedNode.getNodeId());
+ }
+
+ public void executeParameterizedAction(String artifactId, String actionId, Map parameters) throws Exception {
+ ProcessSolutionArtifact artifact = (ProcessSolutionArtifact) getRepositoryNode(artifactId);
+ CyclePluginService pluginService = CycleServiceFactory.getCyclePluginService();
+ ParameterizedAction action = pluginService.getParameterizedActionById(actionId);
+ if (action instanceof ProcessSolutionAction) {
+ // execute ProcessSolutionActions using the ProcessSolutionArtifact and
+ // the ProcessSolutionConnector
+ action.execute(this, artifact, parameters);
+ } else {
+ // execute non-ProcessSolutionActions using the underlying connector
+ repositoryService.executeParameterizedAction(artifact.wrappedNode.getConnectorId(), artifact.wrappedNode.getNodeId(), actionId, parameters);
+ }
+ }
+
+ public boolean isLoggedIn() {
+ return false;
+ }
+
+ public ContentRepresentation getDefaultContentRepresentation(String artifactId) throws RepositoryNodeNotFoundException {
+ ProcessSolutionArtifact artifact = (ProcessSolutionArtifact) getRepositoryNode(artifactId);
+ RuntimeConnectorList connectorList = CycleComponentFactory.getCycleComponentInstance(RuntimeConnectorList.class, RuntimeConnectorList.class);
+ RepositoryConnector connector = connectorList.getConnectorById(artifact.connectorId);
+ return connector.getDefaultContentRepresentation(artifact.wrappedNode.getNodeId());
+ }
+
+ public void startConfiguration() {
+ }
+
+ public void addConfiguration(Map configurationValues) {
+ }
+
+ public void addConfigurationEntry(String key, Object value) {
+ }
+
+ public void configurationFinished() {
+ }
+
+ public String[] getConfigurationKeys() {
+ return null;
+ }
+
+ public void setId(String connectorId) {
+ }
+
+ public String getId() {
+ return "ps-" + processSolutionId;
+ }
+
+ public String getName() {
+ try {
+ return processSolutionService.getProcessSolutionById(processSolutionId).getLabel();
+ } catch (Exception e) {
+ return "Deleted processSolution - " + processSolutionId;
+ }
+ }
+
+ public void setName(String name) {
+ }
+
+ public String concatenateNodeId(String prefix, String suffix) {
+ return null;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionFolder.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionFolder.java
new file mode 100644
index 0000000000..af5ac07e7a
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionFolder.java
@@ -0,0 +1,19 @@
+package org.activiti.cycle.impl.processsolution.connector;
+
+import org.activiti.cycle.RepositoryFolder;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+
+/**
+ * A {@link RepositoryFolder}-implementation for virtual folders
+ *
+ * @author Daniel Meyer
+ */
+public class ProcessSolutionFolder extends ProcessSolutionRepositoryNode implements RepositoryFolder {
+
+ public ProcessSolutionFolder(String connectorId, String nodeId, VirtualRepositoryFolder virtualFolder, ProcessSolution processSolution,
+ RepositoryFolder wrappedFolder) {
+ super(connectorId, nodeId, virtualFolder, processSolution, wrappedFolder);
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionRepositoryNode.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionRepositoryNode.java
new file mode 100644
index 0000000000..ce4f74885c
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/connector/ProcessSolutionRepositoryNode.java
@@ -0,0 +1,119 @@
+package org.activiti.cycle.impl.processsolution.connector;
+
+import org.activiti.cycle.RepositoryFolder;
+import org.activiti.cycle.RepositoryNode;
+import org.activiti.cycle.RepositoryNodeMetadata;
+import org.activiti.cycle.impl.RepositoryNodeMetadataImpl;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+
+/**
+ * A {@link RepositoryFolder}-implementation for virtual folders
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class ProcessSolutionRepositoryNode implements RepositoryNode {
+
+ protected VirtualRepositoryFolder virtualRepositoryFolder;
+
+ protected ProcessSolution processSolution;
+
+ protected RepositoryNode wrappedNode;
+
+ protected String connectorId;
+
+ protected String nodeId;
+
+ public ProcessSolutionRepositoryNode(String connectorId, String nodeId, VirtualRepositoryFolder virtualFolder, ProcessSolution processSolution,
+ RepositoryNode wrappedNode) {
+ this.virtualRepositoryFolder = virtualFolder;
+ this.connectorId = connectorId;
+ this.nodeId = nodeId;
+ this.processSolution = processSolution;
+ this.wrappedNode = wrappedNode;
+ }
+
+ public String getConnectorId() {
+ if (wrappedNode != null) {
+ return wrappedNode.getConnectorId();
+ }
+ return connectorId;
+ }
+
+ public String getNodeId() {
+ if (wrappedNode != null) {
+ return wrappedNode.getNodeId();
+ }
+ return nodeId;
+ }
+
+ public String getGlobalUniqueId() {
+ if (wrappedNode != null) {
+ return wrappedNode.getGlobalUniqueId();
+ }
+ return nodeId;
+ }
+
+ public RepositoryNodeMetadata getMetadata() {
+ if (wrappedNode != null) {
+ if (!wrappedNode.getMetadata().getParentFolderId().equals(virtualRepositoryFolder.getReferencedNodeId())) {
+ return wrappedNode.getMetadata();
+ }
+ }
+
+ return new RepositoryNodeMetadataImpl() {
+
+ public String getName() {
+ if (wrappedNode != null) {
+ return wrappedNode.getMetadata().getName();
+ }
+ if (virtualRepositoryFolder != null) {
+ return virtualRepositoryFolder.getLabel();
+ }
+ if (nodeId.endsWith(ProcessSolutionConnector.PS_HOME_NAME)) {
+ return "Home";
+ }
+ return processSolution.getLabel();
+ }
+ public String getParentFolderId() {
+ if (wrappedNode != null) {
+ return processSolution.getId() + "/" + virtualRepositoryFolder.getId();
+ }
+ return "/";
+ }
+ };
+ }
+
+ public VirtualRepositoryFolder getVirtualRepositoryFolder() {
+ return virtualRepositoryFolder;
+ }
+
+ public void setVirtualRepositoryFolder(VirtualRepositoryFolder virtualRepositoryFolder) {
+ this.virtualRepositoryFolder = virtualRepositoryFolder;
+ }
+
+ public ProcessSolution getProcessSolution() {
+ return processSolution;
+ }
+
+ public void setProcessSolution(ProcessSolution processSolution) {
+ this.processSolution = processSolution;
+ }
+
+ public RepositoryNode getWrappedNode() {
+ return wrappedNode;
+ }
+
+ public void setWrappedNode(RepositoryNode wrappedNode) {
+ this.wrappedNode = wrappedNode;
+ }
+
+ public void setConnectorId(String connectorId) {
+ this.connectorId = connectorId;
+ }
+
+ public void setNodeId(String nodeId) {
+ this.nodeId = nodeId;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/ImplementationDoneEvent.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/ImplementationDoneEvent.java
new file mode 100644
index 0000000000..984f6a126e
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/ImplementationDoneEvent.java
@@ -0,0 +1,17 @@
+package org.activiti.cycle.impl.processsolution.event;
+
+import org.activiti.cycle.processsolution.ProcessSolution;
+
+/**
+ * Signifies that the implementation-phase is completed for a given
+ * {@link ProcessSolution}
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class ImplementationDoneEvent extends ProcessSolutionStateEvent {
+
+ public ImplementationDoneEvent(ProcessSolution processSolution) {
+ super(processSolution);
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/ProcessSolutionStateEvent.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/ProcessSolutionStateEvent.java
new file mode 100644
index 0000000000..d71939c763
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/ProcessSolutionStateEvent.java
@@ -0,0 +1,22 @@
+package org.activiti.cycle.impl.processsolution.event;
+
+import org.activiti.cycle.processsolution.ProcessSolution;
+
+/**
+ * Signifies that a {@link ProcessSolution} is transitioning to a new state.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class ProcessSolutionStateEvent {
+
+ private ProcessSolution processSolution;
+
+ public ProcessSolutionStateEvent(ProcessSolution processSolution) {
+ this.processSolution = processSolution;
+ }
+
+ public ProcessSolution getProcessSolution() {
+ return processSolution;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/SpecificationDoneEvent.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/SpecificationDoneEvent.java
new file mode 100644
index 0000000000..ca79d15ed7
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/SpecificationDoneEvent.java
@@ -0,0 +1,17 @@
+package org.activiti.cycle.impl.processsolution.event;
+
+import org.activiti.cycle.processsolution.ProcessSolution;
+
+/**
+ * Signifies that the specification-phase is completed for a given
+ * {@link ProcessSolution}
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class SpecificationDoneEvent extends ProcessSolutionStateEvent {
+
+ public SpecificationDoneEvent(ProcessSolution processSolution) {
+ super(processSolution);
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/TechnicalProjectCreatedEvent.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/TechnicalProjectCreatedEvent.java
new file mode 100644
index 0000000000..e440db85cf
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/TechnicalProjectCreatedEvent.java
@@ -0,0 +1,30 @@
+package org.activiti.cycle.impl.processsolution.event;
+
+import org.activiti.cycle.RepositoryFolder;
+import org.activiti.cycle.processsolution.ProcessSolution;
+
+/**
+ * Signifies that a new technical project has been generated.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class TechnicalProjectCreatedEvent {
+
+ private ProcessSolution processSolution;
+
+ private RepositoryFolder repositoryFolder;
+
+ public TechnicalProjectCreatedEvent(ProcessSolution ps, RepositoryFolder folder) {
+ this.processSolution = ps;
+ this.repositoryFolder = folder;
+ }
+
+ public ProcessSolution getProcessSolution() {
+ return processSolution;
+ }
+
+ public RepositoryFolder getRepositoryFolder() {
+ return repositoryFolder;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/TechnicalProjectUpdatedEvent.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/TechnicalProjectUpdatedEvent.java
new file mode 100644
index 0000000000..85ee1272bf
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/TechnicalProjectUpdatedEvent.java
@@ -0,0 +1,30 @@
+package org.activiti.cycle.impl.processsolution.event;
+
+import org.activiti.cycle.RepositoryFolder;
+import org.activiti.cycle.processsolution.ProcessSolution;
+
+/**
+ * Signifies that a new technical project has been updated.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class TechnicalProjectUpdatedEvent {
+
+ private ProcessSolution processSolution;
+
+ private RepositoryFolder repositoryFolder;
+
+ public TechnicalProjectUpdatedEvent(ProcessSolution ps, RepositoryFolder folder) {
+ this.processSolution = ps;
+ this.repositoryFolder = folder;
+ }
+
+ public ProcessSolution getProcessSolution() {
+ return processSolution;
+ }
+
+ public RepositoryFolder getRepositoryFolder() {
+ return repositoryFolder;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/TestingDoneEvent.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/TestingDoneEvent.java
new file mode 100644
index 0000000000..6ef76c8eeb
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/event/TestingDoneEvent.java
@@ -0,0 +1,17 @@
+package org.activiti.cycle.impl.processsolution.event;
+
+import org.activiti.cycle.processsolution.ProcessSolution;
+
+/**
+ * Signifies that the testing-phase is completed for a given
+ * {@link ProcessSolution}
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class TestingDoneEvent extends ProcessSolutionStateEvent {
+
+ public TestingDoneEvent(ProcessSolution processSolution) {
+ super(processSolution);
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/AbstractProcessSolutionStateEmailListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/AbstractProcessSolutionStateEmailListener.java
new file mode 100644
index 0000000000..73047c9abf
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/AbstractProcessSolutionStateEmailListener.java
@@ -0,0 +1,50 @@
+package org.activiti.cycle.impl.processsolution.listener;
+
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.event.CycleEventListener;
+import org.activiti.cycle.impl.components.CycleEmailDispatcher;
+import org.activiti.cycle.impl.processsolution.event.ProcessSolutionStateEvent;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.service.CycleServiceFactory;
+import org.activiti.engine.identity.User;
+
+/**
+ * Abstract base class for {@link ProcessSolutionStateEvent}-listeners which
+ * send a notification via email.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public abstract class AbstractProcessSolutionStateEmailListener implements CycleEventListener {
+
+ protected Logger logger = Logger.getLogger(getClass().getName());
+
+ /**
+ * TODO: Make configurable (it is important to have a valid address here, otherwise the sending fails with a 550 error)
+ */
+ private String fromEmailAddress = "activiti@camunda.com";
+
+ protected CycleEmailDispatcher cycleEmailDispatcher = CycleComponentFactory.getCycleComponentInstance(CycleEmailDispatcher.class, CycleEmailDispatcher.class);
+
+ public void onEvent(T event) {
+ try {
+ for (User user : getRecipients(event.getProcessSolution())) {
+ cycleEmailDispatcher.sendEmail(fromEmailAddress, user.getEmail(), getSubject(event), getMessage(event));
+ }
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Error while building email.", e);
+ throw new RuntimeException("Error while building email.", e);
+ }
+ }
+
+ protected abstract String getSubject(T event);
+ protected abstract String getMessage(T event);
+
+ protected List getRecipients(ProcessSolution processSolution) {
+ return CycleServiceFactory.getProcessSolutionService().getProcessSolutionCollaborators(processSolution.getId(), null);
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/AbstractProcessSolutionStateListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/AbstractProcessSolutionStateListener.java
new file mode 100644
index 0000000000..96ac900af8
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/AbstractProcessSolutionStateListener.java
@@ -0,0 +1,27 @@
+package org.activiti.cycle.impl.processsolution.listener;
+
+import org.activiti.cycle.event.CycleCompensatingEventListener;
+import org.activiti.cycle.impl.db.entity.ProcessSolutionEntity;
+import org.activiti.cycle.impl.processsolution.event.ProcessSolutionStateEvent;
+import org.activiti.cycle.processsolution.ProcessSolutionState;
+import org.activiti.cycle.service.CycleProcessSolutionService;
+import org.activiti.cycle.service.CycleServiceFactory;
+
+public abstract class AbstractProcessSolutionStateListener implements CycleCompensatingEventListener {
+
+ private CycleProcessSolutionService processService = CycleServiceFactory.getProcessSolutionService();
+
+ public void compensateEvent(T event) {
+ ProcessSolutionEntity ps = (ProcessSolutionEntity) processService.getProcessSolutionById(event.getProcessSolution().getId());
+ ps.setState(getCurrentState());
+ processService.updateProcessSolution(ps);
+ }
+ public void onEvent(T event) {
+ ProcessSolutionEntity ps = (ProcessSolutionEntity) processService.getProcessSolutionById(event.getProcessSolution().getId());
+ ps.setState(getNextState());
+ processService.updateProcessSolution(ps);
+ }
+ protected abstract ProcessSolutionState getCurrentState();
+ protected abstract ProcessSolutionState getNextState();
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/ImplementationDoneEmailNotificationListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/ImplementationDoneEmailNotificationListener.java
new file mode 100644
index 0000000000..8b99d24949
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/ImplementationDoneEmailNotificationListener.java
@@ -0,0 +1,42 @@
+package org.activiti.cycle.impl.processsolution.listener;
+
+import java.io.StringWriter;
+
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.event.CycleEventListener;
+import org.activiti.cycle.impl.processsolution.event.ImplementationDoneEvent;
+
+/**
+ * Listener sending an email when the implementation for a given process
+ * solution is completed.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class ImplementationDoneEmailNotificationListener extends AbstractProcessSolutionStateEmailListener implements
+ CycleEventListener {
+
+ protected String getSubject(ImplementationDoneEvent event) {
+ return "Implementation done in " + event.getProcessSolution().getLabel();
+ }
+
+ protected String getMessage(ImplementationDoneEvent event) {
+ StringWriter writer = new StringWriter();
+ writer.append("Dear collaborator in project " + event.getProcessSolution().getLabel() + ".");
+ writer.append(" ");
+ writer.append(" ");
+ writer.append("The project has completed the implementation phase and is now in testing.");
+ writer.append(" ");
+ writer.append(" ");
+
+ String psConnectorId = "ps-" + event.getProcessSolution().getId();
+ String psLabel = event.getProcessSolution().getLabel();
+ writer.append("Go to Cycle Process Solution Homepage");
+
+ writer.append(" ");
+ writer.append(" ");
+ writer.append("With best regards from your Activiti Cycle.");
+ return writer.toString();
+ }
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/ImplementationDoneStateListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/ImplementationDoneStateListener.java
new file mode 100644
index 0000000000..28afeda7aa
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/ImplementationDoneStateListener.java
@@ -0,0 +1,21 @@
+package org.activiti.cycle.impl.processsolution.listener;
+
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.event.CycleCompensatingEventListener;
+import org.activiti.cycle.impl.processsolution.event.ImplementationDoneEvent;
+import org.activiti.cycle.processsolution.ProcessSolutionState;
+
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class ImplementationDoneStateListener extends AbstractProcessSolutionStateListener implements
+ CycleCompensatingEventListener {
+
+ protected ProcessSolutionState getCurrentState() {
+ return ProcessSolutionState.IN_IMPLEMENTATION;
+ }
+
+ protected ProcessSolutionState getNextState() {
+ return ProcessSolutionState.IN_TESTING;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/ImplementationDoneUpdateOperationalProcessModels.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/ImplementationDoneUpdateOperationalProcessModels.java
new file mode 100644
index 0000000000..fc58b1aae7
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/ImplementationDoneUpdateOperationalProcessModels.java
@@ -0,0 +1,141 @@
+package org.activiti.cycle.impl.processsolution.listener;
+
+import java.io.StringWriter;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.activiti.cycle.Content;
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.RepositoryArtifact;
+import org.activiti.cycle.RepositoryArtifactLink;
+import org.activiti.cycle.RepositoryConnector;
+import org.activiti.cycle.RepositoryFolder;
+import org.activiti.cycle.RepositoryNodeCollection;
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.event.CycleEventListener;
+import org.activiti.cycle.impl.components.CycleEmailDispatcher;
+import org.activiti.cycle.impl.components.RuntimeConnectorList;
+import org.activiti.cycle.impl.connector.signavio.SignavioConnectorInterface;
+import org.activiti.cycle.impl.connector.signavio.provider.ActivitiCompliantBpmn20Provider;
+import org.activiti.cycle.impl.db.entity.RepositoryArtifactLinkEntity;
+import org.activiti.cycle.impl.processsolution.ProcessSolutionUtils;
+import org.activiti.cycle.impl.processsolution.event.ImplementationDoneEvent;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+import org.activiti.cycle.service.CycleProcessSolutionService;
+import org.activiti.cycle.service.CycleRepositoryService;
+import org.activiti.cycle.service.CycleServiceFactory;
+import org.activiti.engine.identity.User;
+
+/**
+ * {@link CycleEventListener} for {@link ImplementationDoneEvent}s.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class ImplementationDoneUpdateOperationalProcessModels implements CycleEventListener {
+
+ private CycleProcessSolutionService processSolutionService = CycleServiceFactory.getProcessSolutionService();
+ private CycleRepositoryService repositoryService = CycleServiceFactory.getRepositoryService();
+
+ public void onEvent(ImplementationDoneEvent event) {
+ List updatedOperationalModels = new ArrayList();
+ VirtualRepositoryFolder processesFolder = getProcessSolutionUtils().getProcessesFolder(event.getProcessSolution());
+ VirtualRepositoryFolder implementationFolder = getProcessSolutionUtils().getImplementationFolder(event.getProcessSolution());
+ List processModels = getProcessSolutionUtils().getProcessModels(processesFolder);
+ for (RepositoryArtifact operationalProcessModel : processModels) {
+ List links = repositoryService.getArtifactLinks(operationalProcessModel.getConnectorId(), operationalProcessModel.getNodeId());
+ for (RepositoryArtifactLink repositoryArtifactLink : links) {
+ if (!RepositoryArtifactLinkEntity.TYPE_IMPLEMENTS.equals(repositoryArtifactLink.getLinkType())) {
+ continue;
+ }
+ RepositoryArtifact implementedProcessModel = repositoryArtifactLink.getTargetArtifact();
+ // check whether the implemented process model is last changed after the
+ // operational process model:
+ RepositoryConnector operationalProcessModelConnector = RuntimeConnectorList.getMyConnectorById(operationalProcessModel.getConnectorId());
+ if (!isEeSignavio(operationalProcessModelConnector)) {
+ continue;
+ }
+ RepositoryConnector implementedProcessModelConnector = RuntimeConnectorList.getMyConnectorById(implementedProcessModel.getConnectorId());
+ String operationalProcessModelBpmn = ActivitiCompliantBpmn20Provider.createBpmnXml(operationalProcessModelConnector, operationalProcessModel);
+ Content implementedProcessModelBpmnContent = implementedProcessModelConnector.getContent(implementedProcessModel.getNodeId());
+ String implementedProcessModelBpmn = implementedProcessModelBpmnContent.asString();
+ if (implementedProcessModelBpmn.equals(operationalProcessModelBpmn)) {
+ // no need to update
+ continue;
+ }
+ // backup operational process model:
+ RepositoryFolder backupFolder = null;
+ RepositoryNodeCollection childnodes = operationalProcessModelConnector.getChildren(operationalProcessModel.getMetadata().getParentFolderId());
+ for (RepositoryFolder potentialBackupFolder : childnodes.getFolderList()) {
+ if ("backup".equals(potentialBackupFolder.getMetadata().getName())) {
+ backupFolder = potentialBackupFolder;
+ break;
+ }
+ }
+ if (backupFolder == null) {
+ backupFolder = operationalProcessModelConnector.createFolder(operationalProcessModel.getMetadata().getParentFolderId(), "backup");
+ }
+ Content content = operationalProcessModelConnector.getContent(operationalProcessModel.getNodeId());
+ String backupModelName = operationalProcessModel.getMetadata().getName() + "_" + DateFormat.getDateTimeInstance().format(new Date());
+ operationalProcessModelConnector.createArtifact(backupFolder.getNodeId(), backupModelName, null, content);
+ // FIXME: limitation of the SignavioConnector: ATM we cannot update the
+ // content of an artifact. This is why we delete the artifact and create
+ // a new artifact instead.
+ operationalProcessModelConnector.deleteArtifact(operationalProcessModel.getNodeId());
+ // create the new model:
+ SignavioConnectorInterface signavioConnectorInterface = (SignavioConnectorInterface) operationalProcessModelConnector;
+ String json = signavioConnectorInterface.transformBpmn20XmltoJson(implementedProcessModelBpmn);
+ Content jsoncontent = new Content();
+ jsoncontent.setValue(json);
+ String parentfolderId = operationalProcessModel.getMetadata().getParentFolderId();
+ RepositoryFolder parentFolder = operationalProcessModelConnector.getRepositoryFolder(parentfolderId);
+ RepositoryArtifact newArtifact = operationalProcessModelConnector.createArtifact(parentFolder.getNodeId(), operationalProcessModel.getMetadata()
+ .getName(), null, jsoncontent);
+ // delete the link to the deleted artifact
+ repositoryService.deleteLink(repositoryArtifactLink.getId());
+ // create new link
+ RepositoryArtifactLinkEntity newLink = new RepositoryArtifactLinkEntity();
+ newLink.setSourceArtifact(newArtifact);
+ newLink.setTargetArtifact(implementedProcessModel);
+ newLink.setLinkType(RepositoryArtifactLinkEntity.TYPE_IMPLEMENTS);
+ repositoryService.addArtifactLink(newLink);
+ updatedOperationalModels.add(newArtifact);
+
+ }
+ }
+ if (updatedOperationalModels.size() > 0) {
+ sendEmailnotification(event.getProcessSolution(), updatedOperationalModels);
+ }
+ }
+
+ private boolean isEeSignavio(RepositoryConnector connector) {
+ SignavioConnectorInterface connectorInterface = (SignavioConnectorInterface) connector;
+ return !connectorInterface.getConfiguration().getSignavioUrl().endsWith("activiti-modeler/")
+ && !connectorInterface.getConfiguration().getSignavioUrl().endsWith("activiti-modeler");
+ }
+
+ private void sendEmailnotification(ProcessSolution processSolution, List updatedOperationalModels) {
+ CycleEmailDispatcher cycleEmailDispatcher = CycleComponentFactory.getCycleComponentInstance(CycleEmailDispatcher.class);
+ for (User user : processSolutionService.getProcessSolutionCollaborators(processSolution.getId(), null)) {
+ StringWriter writer = new StringWriter();
+ writer.append("Hi " + user.getFirstName() + " " + user.getLastName() + ",
");
+ writer.append("The following operational models for the process solution " + processSolution.getLabel() + " have been updated: ");
+ writer.append("
");
+ for (RepositoryArtifact updatedModel : updatedOperationalModels) {
+ writer.append("
");
+ cycleEmailDispatcher.sendEmail("activiti-cycle@localhost", user.getEmail(), "Operational models updated", writer.toString());
+ }
+ }
+
+ protected ProcessSolutionUtils getProcessSolutionUtils() {
+ return CycleComponentFactory.getCycleComponentInstance(ProcessSolutionUtils.class);
+ }
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneAddHudsonJobListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneAddHudsonJobListener.java
new file mode 100644
index 0000000000..afa428ddd7
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneAddHudsonJobListener.java
@@ -0,0 +1,80 @@
+package org.activiti.cycle.impl.processsolution.listener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.activiti.cycle.RepositoryFolder;
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.event.CycleEventListener;
+import org.activiti.cycle.impl.connector.ci.hudson.action.CreateHudsonJob;
+import org.activiti.cycle.impl.processsolution.event.TechnicalProjectCreatedEvent;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.service.CycleServiceFactory;
+import org.activiti.engine.identity.User;
+
+/**
+ * {@link CycleEventListener} adding the technical project to the Hudson CI
+ * server (in the first iteration) and doing nothing in subsequent iterations.
+ *
+ * EXPERIMENTAL
+ *
+ * TODO: Remove hacky implementation extension for reusing stuff of
+ * {@link SpecificationDoneGenerateProjectListener}
+ *
+ * @author bernd.ruecker@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class SpecificationDoneAddHudsonJobListener implements CycleEventListener {
+
+ public final String METADATA_SVN_URL = "svnUrl";
+
+ protected static Logger log = Logger.getLogger(SpecificationDoneAddHudsonJobListener.class.getName());
+
+ public final String HUDSON_URL = "http://localhost:8080/hudson/";
+
+ public void onEvent(TechnicalProjectCreatedEvent event) {
+ ProcessSolution processSolution = event.getProcessSolution();
+
+ // it should work like this:
+ RepositoryFolder underlyingFolder = event.getRepositoryFolder();
+
+ if (isSvnFolder(underlyingFolder)) {
+ try {
+ String psConnectorId = "ps-" + processSolution.getId();
+ String jobDescription = "This job was generated by Activiti-Cycle, see "
+ + "Cycle Process Solution Homepage";
+
+ new CreateHudsonJob().createCIJobForProject(HUDSON_URL, getSvnUrl(underlyingFolder), processSolution.getLabel(), jobDescription, getDeveloperEmails(processSolution));
+ }
+ catch (Exception ex) {
+ log.log(Level.SEVERE, "Couldn't create Hudson job for Cycle Process Solution " + processSolution.getLabel() + ", see nested exception for details. Ignoring and proceed with specification done events.", ex);
+ }
+ } else {
+ log.warning("Using not supported repository for Hudson in implementation folder: " + underlyingFolder + ". Currently supported are: SVN");
+ }
+ }
+
+ private List getDeveloperEmails(ProcessSolution processSolution) {
+ ArrayList list = new ArrayList();
+
+ for (User user : CycleServiceFactory.getProcessSolutionService().getProcessSolutionCollaborators(processSolution.getId(), null)) {
+ // TODO: Select role developer
+ list.add(user.getEmail());
+ }
+
+ return list;
+ }
+
+ private boolean isSvnFolder(RepositoryFolder underlyingFolder) {
+ return getSvnUrl(underlyingFolder) != null;
+ }
+
+ private String getSvnUrl(RepositoryFolder underlyingFolder) {
+ return underlyingFolder.getMetadata().getMetadata(METADATA_SVN_URL);
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneEmailNotificationListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneEmailNotificationListener.java
new file mode 100644
index 0000000000..a9fcbcb084
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneEmailNotificationListener.java
@@ -0,0 +1,45 @@
+package org.activiti.cycle.impl.processsolution.listener;
+
+import java.io.StringWriter;
+
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.event.CycleEventListener;
+import org.activiti.cycle.impl.processsolution.event.SpecificationDoneEvent;
+
+/**
+ * Listener sending an email when the specification for a given process solution
+ * is completed.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class SpecificationDoneEmailNotificationListener extends AbstractProcessSolutionStateEmailListener implements
+ CycleEventListener {
+
+ protected String getSubject(SpecificationDoneEvent event) {
+ return "Specification done in " + event.getProcessSolution().getLabel();
+ }
+
+ protected String getMessage(SpecificationDoneEvent event) {
+ StringWriter writer = new StringWriter();
+ writer.append("Dear collaborator in project " + event.getProcessSolution().getLabel() + ".");
+ writer.append(" ");
+ writer.append(" ");
+ writer.append("The project has completed the specification phase and is now in implementation.");
+ writer.append(" ");
+ writer.append(" ");
+
+ String psConnectorId = "ps-" + event.getProcessSolution().getId();
+ String psLabel = event.getProcessSolution().getLabel();
+ writer.append("Go to Cycle Process Solution Homepage");
+
+ writer.append(" ");
+ writer.append(" ");
+ writer.append("A technical project was created in the folder Implementation.");
+ writer.append(" ");
+ writer.append(" ");
+ writer.append("With best regards from your Activiti Cycle.");
+ return writer.toString();
+ }
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneGenerateProjectListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneGenerateProjectListener.java
new file mode 100644
index 0000000000..e068f93800
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneGenerateProjectListener.java
@@ -0,0 +1,203 @@
+package org.activiti.cycle.impl.processsolution.listener;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.activiti.cycle.Content;
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.RepositoryArtifact;
+import org.activiti.cycle.RepositoryArtifactLink;
+import org.activiti.cycle.RepositoryConnector;
+import org.activiti.cycle.RepositoryFolder;
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.event.CycleCompensatingEventListener;
+import org.activiti.cycle.impl.components.CycleEmailDispatcher;
+import org.activiti.cycle.impl.components.RuntimeConnectorList;
+import org.activiti.cycle.impl.connector.signavio.action.CreateMavenProjectAction;
+import org.activiti.cycle.impl.connector.signavio.provider.ActivitiCompliantBpmn20Provider;
+import org.activiti.cycle.impl.db.entity.RepositoryArtifactLinkEntity;
+import org.activiti.cycle.impl.processsolution.ProcessSolutionUtils;
+import org.activiti.cycle.impl.processsolution.event.SpecificationDoneEvent;
+import org.activiti.cycle.impl.processsolution.event.TechnicalProjectCreatedEvent;
+import org.activiti.cycle.impl.processsolution.event.TechnicalProjectUpdatedEvent;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+import org.activiti.cycle.service.CycleEventService;
+import org.activiti.cycle.service.CycleProcessSolutionService;
+import org.activiti.cycle.service.CycleRepositoryService;
+import org.activiti.cycle.service.CycleServiceFactory;
+import org.activiti.engine.identity.User;
+
+/**
+ * {@link CycleCompensatingEventListener} generating a technical project-stub
+ * (in the first iteration) and updating an existing technical project in
+ * subsequent iterations.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class SpecificationDoneGenerateProjectListener implements CycleCompensatingEventListener {
+
+ private CycleProcessSolutionService processSolutionService = CycleServiceFactory.getProcessSolutionService();
+ private CycleRepositoryService repositoryservice = CycleServiceFactory.getRepositoryService();
+ private CycleEventService eventService = CycleServiceFactory.getEventService();
+ private ProcessSolutionUtils processSolutionUtils = CycleComponentFactory.getCycleComponentInstance(ProcessSolutionUtils.class);
+
+ public void onEvent(SpecificationDoneEvent event) {
+ ProcessSolution processSolution = event.getProcessSolution();
+ VirtualRepositoryFolder implementationFolder = processSolutionUtils.getImplementationFolder(processSolution);
+ if (implementationFolder == null) {
+ return;
+ }
+ RepositoryFolder underlyingFolder = repositoryservice
+ .getRepositoryFolder(implementationFolder.getConnectorId(), implementationFolder.getReferencedNodeId());
+
+ if (repositoryservice.getChildren(underlyingFolder.getConnectorId(), underlyingFolder.getNodeId()).asList().size() > 0) {
+ Map processesMappedToBpmnXml = updateProject(processSolution, underlyingFolder);
+ sendEmailUpdated(processSolution, processesMappedToBpmnXml);
+ // fire an event signifying that a new technical project has been
+ // updated:
+ eventService.fireEvent(new TechnicalProjectUpdatedEvent(processSolution, underlyingFolder));
+ } else {
+ Map processesMappedToBpmnXml = createProject(processSolution, underlyingFolder);
+ sendEmailCreated(processSolution, processesMappedToBpmnXml);
+ // fire an event signifying that a new technical project has been
+ // generated:
+ eventService.fireEvent(new TechnicalProjectCreatedEvent(processSolution, underlyingFolder));
+ }
+
+ }
+
+ protected Map updateProject(ProcessSolution processSolution, RepositoryFolder underlyingTechnicalFolder) {
+ VirtualRepositoryFolder processes = processSolutionUtils.getProcessesFolder(processSolution);
+ // get all processmodels in the processes folder
+ List processModels = processSolutionUtils.getProcessModels(processes);
+ return updateProcessModels(underlyingTechnicalFolder, processModels);
+ }
+
+ protected Map updateProcessModels(RepositoryFolder underlyingTechnicalFolder, List processModels) {
+ List newModels = new ArrayList();
+ Map resultMap = new HashMap();
+ // update existing models:
+ for (RepositoryArtifact processModel : processModels) {
+ List links = repositoryservice.getArtifactLinks(processModel.getConnectorId(), processModel.getNodeId());
+ if (links.size() == 0) {
+ newModels.add(processModel);
+ }
+ for (RepositoryArtifactLink link : links) {
+ if (!RepositoryArtifactLinkEntity.TYPE_IMPLEMENTS.equals(link.getLinkType())) {
+ continue;
+ }
+ RepositoryArtifact implementationArtifact = link.getTargetArtifact();
+ if (!implementationArtifact.getConnectorId().equals(underlyingTechnicalFolder.getConnectorId())) {
+ continue;
+ }
+ if (!implementationArtifact.getNodeId().startsWith(underlyingTechnicalFolder.getNodeId())) {
+ continue;
+ }
+ // only update technical model if the ProcessModel is last changed after
+ // the implementation
+ Date processModelUpdated = processModel.getMetadata().getLastChanged();
+ Date implementationUpdated = implementationArtifact.getMetadata().getLastChanged();
+ if (processModelUpdated == null || implementationUpdated == null || processModelUpdated.after(implementationUpdated)) {
+ RepositoryConnector processModelConnector = RuntimeConnectorList.getMyConnectorById(processModel.getConnectorId());
+ Content newContent = new Content();
+ newContent.setValue(ActivitiCompliantBpmn20Provider.createBpmnXml(processModelConnector, processModel));
+ repositoryservice.updateContent(implementationArtifact.getConnectorId(), implementationArtifact.getNodeId(), newContent);
+ resultMap.put(processModel, implementationArtifact);
+ }
+ }
+ }
+ // add new models:
+ RepositoryConnector targetConnector = RuntimeConnectorList.getMyConnectorById(underlyingTechnicalFolder.getConnectorId());
+ // get folder for processModels:
+ // TODO: hard-coding this ATM:
+ RepositoryFolder processesFolder = targetConnector.getRepositoryFolder(underlyingTechnicalFolder.getNodeId() + "/src/main/resources/diagrams");
+ for (RepositoryArtifact processModel : newModels) {
+ RepositoryConnector processModelConnector = RuntimeConnectorList.getMyConnectorById(processModel.getConnectorId());
+ // do not create artifact if this model is contained in a "backup" folder:
+ RepositoryFolder parentFolder = processModelConnector.getRepositoryFolder(processModel.getMetadata().getParentFolderId());
+ if ("backup".equals(parentFolder.getMetadata().getName())) {
+ continue;
+ }
+ Content newContent = new Content();
+ newContent.setValue(ActivitiCompliantBpmn20Provider.createBpmnXml(processModelConnector, processModel));
+ String artifactName = processModel.getMetadata().getName() + ".bpmn20.xml";
+ RepositoryArtifact implementationArtifact = repositoryservice.createArtifact(processesFolder.getConnectorId(), processesFolder.getNodeId(), artifactName,
+ null, newContent);
+ // create link:
+ RepositoryArtifactLinkEntity link = new RepositoryArtifactLinkEntity();
+ link.setLinkType(RepositoryArtifactLinkEntity.TYPE_IMPLEMENTS);
+ link.setSourceArtifact(processModel);
+ link.setTargetArtifact(implementationArtifact);
+ repositoryservice.addArtifactLink(link);
+ resultMap.put(processModel, implementationArtifact);
+ }
+ return resultMap;
+ }
+ protected Map createProject(ProcessSolution processSolution, RepositoryFolder underlyingTechnicalFolder) {
+ VirtualRepositoryFolder processes = processSolutionUtils.getProcessesFolder(processSolution);
+ // get all processmodels in the processes folder
+ List processModels = processSolutionUtils.getProcessModels(processes);
+ // configure parameters for CreateMavenProjectAction
+ CreateMavenProjectAction createMavenProjectAction = new CreateMavenProjectAction();
+ RepositoryConnector targetConnector = RuntimeConnectorList.getMyConnectorById(underlyingTechnicalFolder.getConnectorId());
+ String targetFolderId = underlyingTechnicalFolder.getNodeId();
+ String targetName = processSolution.getLabel();
+ String comment = "";
+ boolean createLink = true;
+ // create the technical project
+ return createMavenProjectAction.createMavenProject(targetFolderId, targetName, comment, targetConnector, createLink, processModels);
+ }
+
+ protected void sendEmailCreated(ProcessSolution processSolution, Map processesMappedToBpmnXml) {
+ CycleEmailDispatcher cycleEmailDispatcher = CycleComponentFactory.getCycleComponentInstance(CycleEmailDispatcher.class, CycleEmailDispatcher.class);
+ for (User user : processSolutionService.getProcessSolutionCollaborators(processSolution.getId(), null)) {
+ StringWriter writer = new StringWriter();
+ writer.append("Hi " + user.getFirstName() + " " + user.getLastName() + ",
");
+ writer.append("Technical implementation models for the process solution " + processSolution.getLabel() + " have been created: ");
+ writer.append("
");
+ for (Entry processMappedToBpmnXml : processesMappedToBpmnXml.entrySet()) {
+ writer.append("
");
+ writer.append("The bpmn20.xml file for the process ");
+ writer.append(processMappedToBpmnXml.getKey().getMetadata().getName());
+ writer.append(" is located in ");
+ writer.append(processMappedToBpmnXml.getValue().getNodeId() + ".");
+ writer.append("
");
+ writer.append("Technical implementation models for the process solution " + processSolution.getLabel() + " have been updated/created: ");
+ writer.append("
");
+ for (Entry processMappedToBpmnXml : processesMappedToBpmnXml.entrySet()) {
+ writer.append("
");
+ writer.append("The bpmn20.xml file for the process ");
+ writer.append(processMappedToBpmnXml.getKey().getMetadata().getName());
+ writer.append(" is located in ");
+ writer.append(processMappedToBpmnXml.getValue().getNodeId() + ".");
+ writer.append("
");
+ }
+ writer.append("
");
+ cycleEmailDispatcher.sendEmail("activiti-cycle@localhost", user.getEmail(), "Technical Models updated/created", writer.toString());
+ }
+ }
+
+ public void compensateEvent(SpecificationDoneEvent event) {
+ // TODO: delete technical implementation project in first iteration
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneStateListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneStateListener.java
new file mode 100644
index 0000000000..e3c3fc5f84
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/SpecificationDoneStateListener.java
@@ -0,0 +1,26 @@
+package org.activiti.cycle.impl.processsolution.listener;
+
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.event.CycleCompensatingEventListener;
+import org.activiti.cycle.impl.processsolution.event.SpecificationDoneEvent;
+import org.activiti.cycle.processsolution.ProcessSolutionState;
+
+/**
+ * Listener for {@link ProcessSolutionState} Events.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class SpecificationDoneStateListener extends AbstractProcessSolutionStateListener implements
+ CycleCompensatingEventListener {
+
+ protected ProcessSolutionState getCurrentState() {
+ return ProcessSolutionState.IN_SPECIFICATION;
+ }
+
+ protected ProcessSolutionState getNextState() {
+ return ProcessSolutionState.IN_IMPLEMENTATION;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/TestingDoneEmailNotificationListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/TestingDoneEmailNotificationListener.java
new file mode 100644
index 0000000000..2f754365eb
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/TestingDoneEmailNotificationListener.java
@@ -0,0 +1,43 @@
+package org.activiti.cycle.impl.processsolution.listener;
+
+import java.io.StringWriter;
+
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.event.CycleEventListener;
+import org.activiti.cycle.impl.processsolution.event.TestingDoneEvent;
+
+/**
+ * Listener sending an email when the implementation for a given process
+ * solution is completed.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class TestingDoneEmailNotificationListener extends AbstractProcessSolutionStateEmailListener implements
+ CycleEventListener {
+
+ protected String getSubject(TestingDoneEvent event) {
+ return "Testing done in " + event.getProcessSolution().getLabel();
+ }
+
+ protected String getMessage(TestingDoneEvent event) {
+ StringWriter writer = new StringWriter();
+ writer.append("Dear collaborator in project " + event.getProcessSolution().getLabel() + ".");
+ writer.append(" ");
+ writer.append(" ");
+ writer.append("The project has completed the testing phase and the generated product/prototype is ready for rollout/demonstration.");
+ writer.append("Further requirements can now be specified (the project is back in specificcation)");
+ writer.append(" ");
+ writer.append(" ");
+
+ String psConnectorId = "ps-" + event.getProcessSolution().getId();
+ String psLabel = event.getProcessSolution().getLabel();
+ writer.append("Go to Cycle Process Solution Homepage");
+
+ writer.append(" ");
+ writer.append(" ");
+ writer.append("With best regards from your Activiti Cycle.");
+ return writer.toString();
+ }
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/TestingDoneStateListener.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/TestingDoneStateListener.java
new file mode 100644
index 0000000000..2f89254ec4
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/listener/TestingDoneStateListener.java
@@ -0,0 +1,22 @@
+package org.activiti.cycle.impl.processsolution.listener;
+
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.event.CycleCompensatingEventListener;
+import org.activiti.cycle.impl.processsolution.event.ImplementationDoneEvent;
+import org.activiti.cycle.impl.processsolution.event.TestingDoneEvent;
+import org.activiti.cycle.processsolution.ProcessSolutionState;
+
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class TestingDoneStateListener extends AbstractProcessSolutionStateListener implements
+ CycleCompensatingEventListener {
+
+ protected ProcessSolutionState getCurrentState() {
+ return ProcessSolutionState.IN_TESTING;
+ }
+
+ protected ProcessSolutionState getNextState() {
+ return ProcessSolutionState.IN_SPECIFICATION;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/representation/ProcessSolutionHomeContentRepresentation.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/representation/ProcessSolutionHomeContentRepresentation.java
new file mode 100644
index 0000000000..fd7fb07762
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/processsolution/representation/ProcessSolutionHomeContentRepresentation.java
@@ -0,0 +1,56 @@
+package org.activiti.cycle.impl.processsolution.representation;
+
+import org.activiti.cycle.Content;
+import org.activiti.cycle.ContentRepresentation;
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.MimeType;
+import org.activiti.cycle.RenderInfo;
+import org.activiti.cycle.RepositoryArtifact;
+import org.activiti.cycle.RepositoryArtifactType;
+import org.activiti.cycle.annotations.CycleComponent;
+import org.activiti.cycle.context.CycleContextType;
+import org.activiti.cycle.impl.mimetype.HtmlMimeType;
+import org.activiti.cycle.impl.processsolution.artifacttype.ProcessSolutionHomeArtifactType;
+import org.activiti.cycle.impl.util.IoUtils;
+
+/**
+ * Default {@link ContentRepresentation} for the "Home" screen
+ *
+ * @author daniel.meyer@camunda.com
+ */
+@CycleComponent(context = CycleContextType.APPLICATION)
+public class ProcessSolutionHomeContentRepresentation implements ContentRepresentation {
+
+ private static final long serialVersionUID = 1L;
+
+ public String getId() {
+ return "Home";
+ }
+
+ public RenderInfo getRenderInfo() {
+ return RenderInfo.HTML;
+ }
+
+ public Content getContent(RepositoryArtifact artifact) {
+ try {
+ String parsedForm = IoUtils.readText(getClass().getResourceAsStream("ProcessSolutionHomeContentRepresentation.html"));
+ Content content = new Content();
+ content.setValue(parsedForm);
+ return content;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ public MimeType getRepresentationMimeType() {
+ return CycleComponentFactory.getCycleComponentInstance(HtmlMimeType.class, HtmlMimeType.class);
+ }
+
+ public RepositoryArtifactType getRepositoryArtifactType() {
+ return CycleComponentFactory.getCycleComponentInstance(ProcessSolutionHomeArtifactType.class, ProcessSolutionHomeArtifactType.class);
+ }
+
+ public boolean isForDownload() {
+ return false;
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleConfigurationServiceImpl.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleConfigurationServiceImpl.java
index a490b4dcf5..df1e41ef90 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleConfigurationServiceImpl.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleConfigurationServiceImpl.java
@@ -204,8 +204,10 @@ public class CycleConfigurationServiceImpl implements CycleConfigurationService
CycleConfigEntity entity = cycleConfigurationDao.selectCycleConfigByGroupAndKey(groupId, key);
if (entity == null) {
entity = new CycleConfigEntity();
+ entity.setGroupName(groupId);
+ entity.setKey(key);
}
- entity.setValue(value);
+ entity.setValue(value);
cycleConfigurationDao.saveCycleConfig(entity);
}
@@ -230,4 +232,8 @@ public class CycleConfigurationServiceImpl implements CycleConfigurationService
}
return resultMap;
}
+
+ public CycleConfigurationDao getCycleConfigurationDao() {
+ return cycleConfigurationDao;
+ }
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleEventServiceImpl.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleEventServiceImpl.java
new file mode 100644
index 0000000000..4c2f09486b
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleEventServiceImpl.java
@@ -0,0 +1,58 @@
+package org.activiti.cycle.impl.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.event.CycleCompensatingEventListener;
+import org.activiti.cycle.event.CycleEventListener;
+import org.activiti.cycle.impl.event.CycleEvents;
+import org.activiti.cycle.service.CycleEventService;
+
+/**
+ * Default implementation of the {@link CycleEventService} interface
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class CycleEventServiceImpl implements CycleEventService {
+
+ Logger log = Logger.getLogger(CycleEventService.class.getName());
+
+ public void fireEvent(T event) {
+ Exception exception = null;
+ List> successfulEventListeners = new ArrayList>();
+ CycleEvents cycleEvents = CycleComponentFactory.getCycleComponentInstance(CycleEvents.class, CycleEvents.class);
+ for (CycleEventListener eventListener : cycleEvents.getEventListeners((Class) event.getClass())) {
+ try {
+ eventListener.onEvent(event);
+ successfulEventListeners.add(eventListener);
+ } catch (Exception e) {
+ log.log(Level.SEVERE, "Error while invoking EventListener '" + eventListener.getClass().getName() + "' with event '" + event + "': " + e.getMessage(),
+ e);
+ exception = e;
+ break;
+ }
+ }
+ // try to compensate
+ if (exception != null) {
+ for (CycleEventListener cycleEventListener : successfulEventListeners) {
+ if (cycleEventListener instanceof CycleCompensatingEventListener) {
+ CycleCompensatingEventListener compensatingListener = (CycleCompensatingEventListener) cycleEventListener;
+ try {
+ compensatingListener.compensateEvent(event);
+ } catch (Exception e) {
+ log.log(Level.SEVERE,
+ "Error while compensating EventListener '" + compensatingListener.getClass().getName() + "' with event '" + event + "': " + e.getMessage(),
+ e);
+ // let this one pass, error during compensation
+
+ }
+ }
+ throw new RuntimeException("Error on" + event + ": " + exception.getMessage(), exception);
+ }
+ }
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CyclePluginServiceImpl.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CyclePluginServiceImpl.java
index b09f00ed65..bc8865d1d8 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CyclePluginServiceImpl.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CyclePluginServiceImpl.java
@@ -110,6 +110,7 @@ public class CyclePluginServiceImpl implements CyclePluginService {
for (CreateUrlAction action : getCreateUrlActions(artifact)) {
// TODO: Think about id
RepositoryArtifactOpenLinkAction link = new RepositoryArtifactOpenLinkAction("Open " + action.getId(), action.getUrl(connector, artifact));
+ link.setWarning(action.getWarning(connector, artifact));
list.add(link);
}
return list;
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleProcessSolutionServiceImpl.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleProcessSolutionServiceImpl.java
new file mode 100644
index 0000000000..388a4a1f5b
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleProcessSolutionServiceImpl.java
@@ -0,0 +1,75 @@
+package org.activiti.cycle.impl.service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.activiti.cycle.CycleComponentFactory;
+import org.activiti.cycle.impl.components.RuntimeConnectorList;
+import org.activiti.cycle.impl.db.CycleProcessSolutionDao;
+import org.activiti.cycle.impl.db.entity.ProcessSolutionEntity;
+import org.activiti.cycle.impl.processsolution.DefaultProcessSolutionTemplate;
+import org.activiti.cycle.impl.processsolution.ProcessSolutionCreate;
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.processsolution.ProcessSolutionTemplate;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+import org.activiti.cycle.service.CycleProcessSolutionService;
+import org.activiti.engine.ProcessEngines;
+import org.activiti.engine.identity.User;
+import org.activiti.engine.impl.identity.UserEntity;
+
+/**
+ * Default implementation of the {@link CycleProcessSolutionService}-interface.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public class CycleProcessSolutionServiceImpl implements CycleProcessSolutionService {
+
+ protected CycleProcessSolutionDao dao;
+
+ public ProcessSolution getProcessSolutionById(String id) {
+ return dao.getProcessSolutionById(id);
+ }
+
+ public List getProcessSolutions() {
+ return new ArrayList(dao.getProcessSolutionList());
+ }
+
+ public String createNewProcessSolution(String name) {
+ ProcessSolutionCreate processSolutionCreate = CycleComponentFactory.getCycleComponentInstance("processSolutionCreate", ProcessSolutionCreate.class);
+ processSolutionCreate.setName(name);
+ String processSolutionId = processSolutionCreate.createNewProcessSolution();
+ RuntimeConnectorList runtimeConnectorList = CycleComponentFactory.getCycleComponentInstance(RuntimeConnectorList.class, RuntimeConnectorList.class);
+ runtimeConnectorList.discardConnectors();
+ return processSolutionId;
+ }
+
+ public List getFoldersForProcessSolution(String id) {
+ return new ArrayList(dao.getVirtualForldersByProcessSolutionId(id));
+ }
+
+ public void setDao(CycleProcessSolutionDao dao) {
+ this.dao = dao;
+ }
+
+ public VirtualRepositoryFolder getVirtualRepositoryFolderById(String id) {
+ return dao.getVirtualRepositoryFolderById(id);
+ }
+
+ public ProcessSolutionTemplate getDefaultProcessSolutionTemplate() {
+ return CycleComponentFactory.getCycleComponentInstance(DefaultProcessSolutionTemplate.class, DefaultProcessSolutionTemplate.class);
+ }
+
+ public CycleProcessSolutionDao getDao() {
+ return dao;
+ }
+
+ public ProcessSolution updateProcessSolution(ProcessSolution processSolution) {
+ return dao.saveProcessSolution((ProcessSolutionEntity) processSolution);
+ }
+
+ public List getProcessSolutionCollaborators(String processSolutionId, String processSolutionCollaboratorRole) {
+ // for the moment, do nothing:
+ return ProcessEngines.getDefaultProcessEngine().getIdentityService().createUserQuery().list();
+ }
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleRepositoryServiceImpl.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleRepositoryServiceImpl.java
index b596a4aee9..0f39298166 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleRepositoryServiceImpl.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleRepositoryServiceImpl.java
@@ -129,6 +129,10 @@ public class CycleRepositoryServiceImpl implements CycleRepositoryService {
return getRepositoryConnector(connectorId).createArtifact(parentFolderId, artifactName, artifactType, artifactContent);
}
+ public RepositoryArtifact createEmptyArtifact(String connectorId, String parentFolderId, String artifactName, String artifactType) {
+ return getRepositoryConnector(connectorId).createEmptyArtifact(parentFolderId, artifactName, artifactType);
+ }
+
public RepositoryArtifact createArtifactFromContentRepresentation(String connectorId, String parentFolderId, String artifactName, String artifactType,
String contentRepresentationName, Content artifactContent) throws RepositoryNodeNotFoundException {
return getRepositoryConnector(connectorId).createArtifactFromContentRepresentation(parentFolderId, artifactName, artifactType, contentRepresentationName,
@@ -163,7 +167,7 @@ public class CycleRepositoryServiceImpl implements CycleRepositoryService {
public void executeParameterizedAction(String connectorId, String artifactId, String actionId, Map parameters) throws Exception {
- ParameterizedAction action = cycleServiceConfiguration.getCyclePluginService().getParameterizedActionById(actionId);
+ ParameterizedAction action = cycleServiceConfiguration.getPluginService().getParameterizedActionById(actionId);
String form = action.getFormAsHtml();
FormHandler formHandler = CycleComponentFactory.getCycleComponentInstance(FormHandler.class, FormHandler.class);
formHandler.setValues(form, parameters);
@@ -173,8 +177,10 @@ public class CycleRepositoryServiceImpl implements CycleRepositoryService {
// TODO: (Nils Preusker, 20.10.2010), find a better way to solve this!
for (String key : parameters.keySet()) {
if (key.equals("targetConnectorId")) {
- RepositoryConnector targetConnector = getRepositoryConnector((String) parameters.get(key));
- parameters.put(key, targetConnector);
+ if (parameters.get(key) != null && parameters.get(key) instanceof String) {
+ RepositoryConnector targetConnector = getRepositoryConnector((String) parameters.get(key));
+ parameters.put(key, targetConnector);
+ }
}
}
@@ -187,7 +193,7 @@ public class CycleRepositoryServiceImpl implements CycleRepositoryService {
// Retrieve the action and its form
String form = null;
- for (ParameterizedAction action : cycleServiceConfiguration.getCyclePluginService().getParameterizedActions(artifact)) {
+ for (ParameterizedAction action : cycleServiceConfiguration.getPluginService().getParameterizedActions(artifact)) {
if (action.getId().equals(actionId)) {
form = action.getFormAsHtml();
break;
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleServiceConfiguration.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleServiceConfiguration.java
index b6205c3ca2..30241637bd 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleServiceConfiguration.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/impl/service/CycleServiceConfiguration.java
@@ -2,7 +2,10 @@ package org.activiti.cycle.impl.service;
import org.activiti.cycle.impl.db.impl.CycleDaoMyBatisImpl;
import org.activiti.cycle.service.CycleConfigurationService;
+import org.activiti.cycle.service.CycleContentService;
+import org.activiti.cycle.service.CycleEventService;
import org.activiti.cycle.service.CyclePluginService;
+import org.activiti.cycle.service.CycleProcessSolutionService;
import org.activiti.cycle.service.CycleRepositoryService;
import org.activiti.cycle.service.CycleTagService;
@@ -17,7 +20,7 @@ public class CycleServiceConfiguration {
private CycleRepositoryServiceImpl repositoryService;
private CycleTagServiceImpl tagService;
-
+
private CycleCommentServiceImpl commentService;
private CycleConfigurationServiceImpl configurationService;
@@ -26,6 +29,10 @@ public class CycleServiceConfiguration {
private CycleContentServiceImpl cycleContentServiceImpl;
+ private CycleProcessSolutionServiceImpl cycleProcessSolutionServiceImpl;
+
+ private CycleEventServiceImpl cycleEventServiceImpl;
+
private CycleServiceConfiguration() {
wireServices();
initializeServices();
@@ -40,6 +47,8 @@ public class CycleServiceConfiguration {
commentService = new CycleCommentServiceImpl();
cyclePluginServiceImpl = new CyclePluginServiceImpl();
cycleContentServiceImpl = new CycleContentServiceImpl();
+ cycleProcessSolutionServiceImpl = new CycleProcessSolutionServiceImpl();
+ cycleEventServiceImpl = new CycleEventServiceImpl();
// wire-up
repositoryService.setLinkDao(dao);
@@ -47,10 +56,11 @@ public class CycleServiceConfiguration {
configurationService.setCycleRepositoryConnectorConfigurationDao(dao);
tagService.setTagDao(dao);
commentService.setTagDao(dao);
+ cycleProcessSolutionServiceImpl.setDao(dao);
repositoryService.setCycleServiceConfiguration(this);
tagService.setCycleServiceConfiguration(this);
configurationService.setCycleServiceConfiguration(this);
-
+
}
private void initializeServices() {
@@ -61,30 +71,38 @@ public class CycleServiceConfiguration {
configurationService.initialize();
}
- public CycleTagService getTagService() {
+ public CycleTagServiceImpl getTagService() {
return tagService;
}
public CycleCommentServiceImpl getCommentService() {
return commentService;
}
-
- public CycleRepositoryService getRepositoryService() {
+
+ public CycleRepositoryServiceImpl getRepositoryService() {
return repositoryService;
}
- public CycleConfigurationService getConfigurationService() {
+ public CycleConfigurationServiceImpl getConfigurationService() {
return configurationService;
}
- public CyclePluginService getCyclePluginService() {
+ public CyclePluginServiceImpl getPluginService() {
return cyclePluginServiceImpl;
}
- public CycleContentServiceImpl getCycleContentServiceImpl() {
+ public CycleContentServiceImpl getContentService() {
return cycleContentServiceImpl;
}
+ public CycleProcessSolutionServiceImpl getProcessSolutionService() {
+ return cycleProcessSolutionServiceImpl;
+ }
+
+ public CycleEventServiceImpl getCycleEventService() {
+ return cycleEventServiceImpl;
+ }
+
public static CycleServiceConfiguration getInstance() {
if (INSTANCE == null) {
synchronized (CycleServiceConfiguration.class) {
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/ProcessSolution.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/ProcessSolution.java
new file mode 100644
index 0000000000..2817e8aa49
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/ProcessSolution.java
@@ -0,0 +1,28 @@
+package org.activiti.cycle.processsolution;
+
+/**
+ * A {@link ProcessSolution} is a container for {@link VirtualRepositoryFolder}
+ * s. A {@link ProcessSolution} is in a certain {@link ProcessSolutionState}.
+ *
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public interface ProcessSolution {
+
+ /**
+ * The id of this {@link ProcessSolution}
+ */
+ public String getId();
+
+ /**
+ * @return the label for this {@link ProcessSolution}.
+ */
+ public String getLabel();
+
+ /**
+ * @return the {@link ProcessSolutionState} this {@link ProcessSolution} is
+ * currently in.
+ */
+ public ProcessSolutionState getState();
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/ProcessSolutionState.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/ProcessSolutionState.java
new file mode 100644
index 0000000000..92c08084ea
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/ProcessSolutionState.java
@@ -0,0 +1,10 @@
+package org.activiti.cycle.processsolution;
+
+/**
+ * The state a {@link ProcessSolution} is currently in.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public enum ProcessSolutionState {
+ IN_SPECIFICATION, IN_IMPLEMENTATION, IN_TESTING, IN_OPERATIONS;
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/ProcessSolutionTemplate.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/ProcessSolutionTemplate.java
new file mode 100644
index 0000000000..fd552b01db
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/ProcessSolutionTemplate.java
@@ -0,0 +1,16 @@
+package org.activiti.cycle.processsolution;
+
+import java.util.List;
+
+/**
+ * A template for creating new {@link ProcessSolution}s
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public interface ProcessSolutionTemplate {
+
+ public List getVirtualRepositoryFolders();
+
+ public ProcessSolutionState getInitialState();
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/VirtualRepositoryFolder.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/VirtualRepositoryFolder.java
new file mode 100644
index 0000000000..3350606229
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/processsolution/VirtualRepositoryFolder.java
@@ -0,0 +1,32 @@
+package org.activiti.cycle.processsolution;
+
+import org.activiti.cycle.RepositoryFolder;
+
+/**
+ * A {@link VirtualRepositoryFolder} is a virtual folder, referencing a concrete
+ * {@link RepositoryFolder}.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public interface VirtualRepositoryFolder {
+
+ /**
+ * @return the id of the {@link ProcessSolution}, this folder is associated
+ * to, as returned by {@link ProcessSolution#getId()}
+ */
+ public String getProcessSolutionId();
+
+ /**
+ * @return the type of a repository folder.
+ */
+ public String getType();
+
+ public String getReferencedNodeId();
+
+ public String getConnectorId();
+
+ public String getLabel();
+
+ public String getId();
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleEventService.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleEventService.java
new file mode 100644
index 0000000000..d7534c0152
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleEventService.java
@@ -0,0 +1,15 @@
+package org.activiti.cycle.service;
+
+/**
+ * Cycle event service
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public interface CycleEventService {
+
+ /**
+ * Fire a cycle event
+ */
+ public void fireEvent(T event);
+
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleProcessSolutionService.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleProcessSolutionService.java
new file mode 100644
index 0000000000..066bcd2a87
--- /dev/null
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleProcessSolutionService.java
@@ -0,0 +1,67 @@
+package org.activiti.cycle.service;
+
+import java.util.List;
+
+import org.activiti.cycle.processsolution.ProcessSolution;
+import org.activiti.cycle.processsolution.ProcessSolutionTemplate;
+import org.activiti.cycle.processsolution.VirtualRepositoryFolder;
+import org.activiti.engine.identity.User;
+
+/**
+ * Cycle-serivce for managing {@link ProcessSolution}s and
+ * {@link VirtualRepositoryFolder}s.
+ *
+ * @author daniel.meyer@camunda.com
+ */
+public interface CycleProcessSolutionService {
+
+ /**
+ * @param the
+ * id of the {@link ProcessSolution} to be returned.
+ * @return the {@link ProcessSolution} with the given id.
+ */
+ public ProcessSolution getProcessSolutionById(String id);
+
+ /**
+ *
+ * @return a {@link List} of all available {@link ProcessSolution}s
+ */
+ public List getProcessSolutions();
+
+ /**
+ * Create a new process solution based on the default configuration
+ *
+ * @param the
+ * name of the new process solution
+ * @return the id of the new processSolution
+ */
+ public String createNewProcessSolution(String name);
+
+ /**
+ * @param the
+ * id of the {@link ProcessSolution} to return the list of
+ * {@link VirtualRepositoryFolder}s for.
+ *
+ * @return a {@link List} of {@link VirtualRepositoryFolder}s for the given
+ * {@link ProcessSolution}.
+ */
+ public List getFoldersForProcessSolution(String id);
+
+ /**
+ * @return the {@link VirtualRepositoryFolder} corresponding to the provided
+ * id.
+ */
+ public VirtualRepositoryFolder getVirtualRepositoryFolderById(String id);
+
+ /**
+ * @return the default {@link ProcessSolutionTemplate}.
+ */
+ public ProcessSolutionTemplate getDefaultProcessSolutionTemplate();
+
+ /**
+ * Update a {@link ProcessSolution}
+ */
+ public ProcessSolution updateProcessSolution(ProcessSolution processSolution);
+
+ public List getProcessSolutionCollaborators(String processSolutionId, String processSolutionCollaboratorRole);
+}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleRepositoryService.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleRepositoryService.java
index 36b943fc55..f03560b1a1 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleRepositoryService.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleRepositoryService.java
@@ -76,9 +76,9 @@ public interface CycleRepositoryService {
public Content getContent(String connectorId, String artifactId) throws RepositoryNodeNotFoundException;
public List getIncomingArtifactLinks(String targetConnectorId, String targetArtifactId);
-
+
public RepositoryNode getRepositoryNode(String connectorId, String nodeId);
-
+
/**
* load the {@link RepositoryArtifact} including details
*/
@@ -93,12 +93,13 @@ public interface CycleRepositoryService {
public RepositoryFolder getRepositoryFolder(String connectorId, String folderId) throws RepositoryNodeNotFoundException;
-// /**
-// * return the list of supported {@link ArtifactType}s of this
-// * {@link RepositoryConnector} for the given folder. Most conenctors doesn't
-// * make any difference between the folders, but some may do.
-// */
-// public List getSupportedArtifactTypes(String connectorId, String folderId);
+ // /**
+ // * return the list of supported {@link ArtifactType}s of this
+ // * {@link RepositoryConnector} for the given folder. Most conenctors doesn't
+ // * make any difference between the folders, but some may do.
+ // */
+ // public List getSupportedArtifactTypes(String connectorId,
+ // String folderId);
/**
* update artifact content with default {@link ContentRepresentation}
@@ -132,6 +133,9 @@ public interface CycleRepositoryService {
public String getActionFormTemplate(String connectorId, String artifactId, String actionId);
-
+ /**
+ * Create a new (empty) artifact as a childNode of the provided parentFodlerId
+ */
+ public RepositoryArtifact createEmptyArtifact(String connectorId, String parentFolderId, String artifactName, String artifactType);
}
diff --git a/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleServiceFactory.java b/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleServiceFactory.java
index 99470fbd1e..bc12e34e77 100644
--- a/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleServiceFactory.java
+++ b/modules/activiti-cycle/src/main/java/org/activiti/cycle/service/CycleServiceFactory.java
@@ -19,6 +19,7 @@ import org.activiti.cycle.impl.service.CycleServiceConfiguration;
* {@link CycleRepositoryService}, the {@link CycleTagService} and the
* {@link CycleConfigurationService}.
*
+ * @author Daniel Meyer
*/
public class CycleServiceFactory {
@@ -35,15 +36,23 @@ public class CycleServiceFactory {
}
public static CyclePluginService getCyclePluginService() {
- return CycleServiceConfiguration.getInstance().getCyclePluginService();
+ return CycleServiceConfiguration.getInstance().getPluginService();
}
public static CycleContentService getContentService() {
- return CycleServiceConfiguration.getInstance().getCycleContentServiceImpl();
+ return CycleServiceConfiguration.getInstance().getContentService();
}
-
+
public static CycleCommentService getCommentService() {
return CycleServiceConfiguration.getInstance().getCommentService();
}
+ public static CycleProcessSolutionService getProcessSolutionService() {
+ return CycleServiceConfiguration.getInstance().getProcessSolutionService();
+ }
+
+ public static CycleEventService getEventService() {
+ return CycleServiceConfiguration.getInstance().getCycleEventService();
+ }
+
}
diff --git a/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/connector/ci/hudson/action/template.config.xml b/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/connector/ci/hudson/action/template.config.xml
new file mode 100644
index 0000000000..a0d0daab59
--- /dev/null
+++ b/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/connector/ci/hudson/action/template.config.xml
@@ -0,0 +1,50 @@
+
+
+
+ @JOB_DESCRIPTION@
+ false
+
+
+
+
+ @SVN_LOCATION@
+ .
+
+
+ true
+ true
+
+
+
+
+
+
+ true
+ false
+ false
+ false
+
+
+ @hourly
+
+
+ false
+ clean install
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ -1
+
+
+ @EMAIL_ADDRESSES@
+ false
+ true
+
+
+
+
+
diff --git a/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/connector/signavio/emptyProcessModelTemplate.json b/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/connector/signavio/emptyProcessModelTemplate.json
new file mode 100644
index 0000000000..9e0a624957
--- /dev/null
+++ b/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/connector/signavio/emptyProcessModelTemplate.json
@@ -0,0 +1,39 @@
+{
+ "bounds": {
+ "lowerRight": {
+ "y": 0,
+ "x": 0
+ },
+ "upperLeft": {
+ "y": 0,
+ "x": 0
+ }
+ },
+ "resourceId": "canvas",
+ "childShapes": [],
+ "properties": {
+ "monitoring": "",
+ "modificationdate": "",
+ "namespaces": "",
+ "creationdate": "",
+ "version": "",
+ "author": "",
+ "targetnamespace": "http://www.signavio.com/bpmn20",
+ "name": "",
+ "documentation": "",
+ "language": "English",
+ "auditing": "",
+ "expressionlanguage": "http://www.w3.org/1999/XPath",
+ "typelanguage": "http://www.w3.org/2001/XMLSchema"
+ },
+ "stencilset": {
+ "url": "/activiti-modeler/editor/stencilsets//bpmn2.0/bpmn2.0.json",
+ "namespace": "http://b3mn.org/stencilset/bpmn2.0#"
+ },
+ "ssextensions": [
+
+ ],
+ "stencil": {
+ "id": "BPMNDiagram"
+ }
+}
\ No newline at end of file
diff --git a/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/processsolution/LinkRequirementWithProcessDiagramAction.html b/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/processsolution/LinkRequirementWithProcessDiagramAction.html
new file mode 100644
index 0000000000..9fc3c33164
--- /dev/null
+++ b/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/processsolution/LinkRequirementWithProcessDiagramAction.html
@@ -0,0 +1,20 @@
+
+
Link Requirement to Process Diagram
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/processsolution/representation/ProcessSolutionHomeContentRepresentation.html b/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/processsolution/representation/ProcessSolutionHomeContentRepresentation.html
new file mode 100644
index 0000000000..02d6aa3ad4
--- /dev/null
+++ b/modules/activiti-cycle/src/main/resources/org/activiti/cycle/impl/processsolution/representation/ProcessSolutionHomeContentRepresentation.html
@@ -0,0 +1,3 @@
+