From a8aef62578226e50551a92ae1c092d38285b998c Mon Sep 17 00:00:00 2001 From: Tihomir Surdilovic Date: Tue, 8 Apr 2014 18:14:09 -0400 Subject: [PATCH] BZ 1074694 - Unable to reopen process definition containing incorrect I/O task assignments --- .../DesignerNotificationEvent.java | 37 ++++- .../org/jbpm/designer/jBPMDesignerAPI.gwt.xml | 1 + .../bpmn2/impl/Bpmn2JsonMarshaller.java | 2 +- .../JBPMBpmn2ResourceFactoryImpl.java | 1 + .../bpmn2/resource/JBPMBpmn2ResourceImpl.java | 144 +++++++++++++++++- .../web/profile/impl/DefaultProfileImpl.java | 6 +- .../web/profile/impl/JbpmProfileImpl.java | 34 +++-- .../web/server/TransformerServlet.java | 4 + .../designer/client/DesignerPresenter.java | 1 - .../DesignerNotificationPopupsManager.java | 110 ++++++++++++- 10 files changed, 318 insertions(+), 22 deletions(-) diff --git a/jbpm-designer-api/src/main/java/org/jbpm/designer/notification/DesignerNotificationEvent.java b/jbpm-designer-api/src/main/java/org/jbpm/designer/notification/DesignerNotificationEvent.java index 2fc6ec1276..99d117e8be 100644 --- a/jbpm-designer-api/src/main/java/org/jbpm/designer/notification/DesignerNotificationEvent.java +++ b/jbpm-designer-api/src/main/java/org/jbpm/designer/notification/DesignerNotificationEvent.java @@ -1,4 +1,39 @@ package org.jbpm.designer.notification; -public class DesignerNotificationEvent { +import org.jboss.errai.common.client.api.annotations.Portable; +import org.uberfire.workbench.events.NotificationEvent; +import org.uberfire.workbench.events.UberFireEvent; + +@Portable +public class DesignerNotificationEvent extends UberFireEvent { + + private final String notification; + private final NotificationEvent.NotificationType type; + + public DesignerNotificationEvent() { + this("Designernotification", "message"); + } + public DesignerNotificationEvent( final String notification, final String message ) { + this( notification, + NotificationEvent.NotificationType.DEFAULT ); + } + + public DesignerNotificationEvent( final String notification, + final NotificationEvent.NotificationType type ) { + this.notification = notification; + this.type = type; + } + + public String getNotification() { + return this.notification; + } + + public NotificationEvent.NotificationType getType() { + return type; + } + + @Override + public String toString() { + return "DesignerNotificationEvent [notification=" + notification + ", type=" + type + "]"; + } } diff --git a/jbpm-designer-api/src/main/resources/org/jbpm/designer/jBPMDesignerAPI.gwt.xml b/jbpm-designer-api/src/main/resources/org/jbpm/designer/jBPMDesignerAPI.gwt.xml index acfd7e7449..13ae5a8b69 100644 --- a/jbpm-designer-api/src/main/resources/org/jbpm/designer/jBPMDesignerAPI.gwt.xml +++ b/jbpm-designer-api/src/main/resources/org/jbpm/designer/jBPMDesignerAPI.gwt.xml @@ -8,6 +8,7 @@ + \ No newline at end of file diff --git a/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/impl/Bpmn2JsonMarshaller.java b/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/impl/Bpmn2JsonMarshaller.java index b36e32b010..19a4751d09 100644 --- a/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/impl/Bpmn2JsonMarshaller.java +++ b/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/impl/Bpmn2JsonMarshaller.java @@ -1472,7 +1472,7 @@ protected void marshallTask(Task task, BPMNPlane plane, JsonGenerator generator, if(din.getName() != null && din.getName().equals("TaskName")) { List taskDataInputAssociations = task.getDataInputAssociations(); for(DataInputAssociation dia : taskDataInputAssociations) { - if(dia.getTargetRef().getId().equals(din.getId())) { + if(dia.getTargetRef() != null && dia.getTargetRef().getId().equals(din.getId())) { properties.put("taskname", ((FormalExpression) dia.getAssignment().get(0).getFrom()).getBody()); } } diff --git a/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/resource/JBPMBpmn2ResourceFactoryImpl.java b/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/resource/JBPMBpmn2ResourceFactoryImpl.java index bbef858dcf..a25e72051b 100644 --- a/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/resource/JBPMBpmn2ResourceFactoryImpl.java +++ b/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/resource/JBPMBpmn2ResourceFactoryImpl.java @@ -58,6 +58,7 @@ public Resource createResource(URI uri) { result.getDefaultSaveOptions().put(XMLResource.OPTION_ENCODING, "UTF-8"); result.getDefaultSaveOptions().put(XMLResource.OPTION_USE_CACHED_LOOKUP_TABLE, new ArrayList()); result.getDefaultSaveOptions().put(XMLResource.OPTION_DEFER_IDREF_RESOLUTION, true); + result.getDefaultSaveOptions().put(XMLResource.OPTION_PROCESS_DANGLING_HREF, XMLResource.OPTION_PROCESS_DANGLING_HREF_RECORD); return result; } diff --git a/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/resource/JBPMBpmn2ResourceImpl.java b/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/resource/JBPMBpmn2ResourceImpl.java index 835a0f2a6b..e30b296c06 100644 --- a/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/resource/JBPMBpmn2ResourceImpl.java +++ b/jbpm-designer-backend/src/main/java/org/jbpm/designer/bpmn2/resource/JBPMBpmn2ResourceImpl.java @@ -1,17 +1,38 @@ package org.jbpm.designer.bpmn2.resource; -import org.eclipse.bpmn2.Bpmn2Package; +import org.eclipse.bpmn2.*; import org.eclipse.bpmn2.util.Bpmn2ResourceImpl; import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.common.util.WrappedException; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.ecore.impl.DynamicEObjectImpl; +import org.eclipse.emf.ecore.xmi.XMLHelper; +import org.eclipse.emf.ecore.xmi.XMLLoad; +import org.eclipse.emf.ecore.xmi.XMLResource; import org.eclipse.emf.ecore.xmi.XMLSave; +import org.eclipse.emf.ecore.xmi.impl.XMLLoadImpl; +import org.xml.sax.helpers.DefaultHandler; +import org.eclipse.emf.ecore.resource.Resource; + +import java.io.IOException; +import java.io.InputStream; +import java.util.*; public class JBPMBpmn2ResourceImpl extends Bpmn2ResourceImpl { + public HashMap xmlNameToFeatureMap = new HashMap(); + public JBPMBpmn2ResourceImpl(URI uri) { super(uri); + this.getDefaultLoadOptions().put(XMLResource.OPTION_DEFER_IDREF_RESOLUTION, true); + this.getDefaultLoadOptions().put(XMLResource.OPTION_DISABLE_NOTIFY, true); + this.getDefaultLoadOptions().put(XMLResource.OPTION_USE_XML_NAME_TO_FEATURE_MAP, xmlNameToFeatureMap); + + this.getDefaultSaveOptions().put(XMLResource.OPTION_ENCODING, "UTF-8"); + this.getDefaultSaveOptions().put(XMLResource.OPTION_PROCESS_DANGLING_HREF, XMLResource.OPTION_PROCESS_DANGLING_HREF_DISCARD); } @Override @@ -28,4 +49,125 @@ protected boolean shouldSaveFeature(EObject o, EStructuralFeature f) { } }; } + + /** + * Override this method to hook in our own XmlHandler + */ + @Override + protected XMLLoad createXMLLoad() { + return new XMLLoadImpl(createXMLHelper()) { + Bpmn2ModelerXmlHandler handler; + + @Override + protected DefaultHandler makeDefaultHandler() { + handler = new Bpmn2ModelerXmlHandler(resource, helper, options); + return handler; + } + + @Override + public void load(XMLResource resource, InputStream inputStream, Map options) throws IOException { + try { + super.load(resource, inputStream, options); + } + catch (Exception e) { + DiagnosticWrappedException error = new DiagnosticWrappedException(e); + error.setLine(handler.getLineNumber()); + error.setColumn(handler.getColumnNumber()); + error.setLocation(handler.getLocation()); + resource.getErrors().add(error); + } + } + }; + } + + class DiagnosticWrappedException extends WrappedException implements Resource.Diagnostic { + private static final long serialVersionUID = 1L; + private String location; + private int column; + private int line; + + public DiagnosticWrappedException(Exception exception) { + super(exception); + } + + public void setLocation(String location) { + this.location = location; + } + + public String getLocation() { + return location; + } + + public void setColumn(int column) { + this.column = column;; + } + + public int getColumn() { + return column; + } + + public void setLine(int line) { + this.line = line; + } + + public int getLine() { + return line; + } + } + + /** + * We need to extend the standard SAXXMLHandler to hook into the handling of + * attribute references which may be either simple ID Strings or QNames. + * We'll search through all of the objects' IDs first to find the one we're + * looking for. If not, we'll try a QName search. + */ + protected static class Bpmn2ModelerXmlHandler extends BpmnXmlHandler { + public Bpmn2ModelerXmlHandler(XMLResource xmiResource, XMLHelper helper, Map options) { + super(xmiResource, helper, options); + } + + public int getLineNumber() { + return super.getLineNumber(); + } + + public int getColumnNumber() { + return super.getColumnNumber(); + } + + public String getLocation() { + return super.getLocation(); + } + + } + + public static EObject createStringWrapper(String value) { + DynamicEObjectImpl de = new DynamicEObjectImpl() { + // prevent owners from trying to resolve this thing - it's just a string! + public boolean eIsProxy() { + return false; + } + + @Override + public boolean equals(Object object) { + if (object instanceof DynamicEObjectImpl) { + DynamicEObjectImpl that = (DynamicEObjectImpl) object; + if (eProxyURI()==null) { + return that.eProxyURI()==null; + } + String thisString = eProxyURI().toString(); + String thatString = that.eProxyURI() == null ? null : that.eProxyURI().toString(); + return thisString.equals(thatString); + } + else if (object instanceof String) { + String thisString = eProxyURI().toString(); + return thisString.equals(object); + } + return super.equals(object); + } + + }; + de.eSetClass(EcorePackage.eINSTANCE.getEObject()); + de.eSetProxyURI(URI.createURI(value)); + return de; + } } \ No newline at end of file diff --git a/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/profile/impl/DefaultProfileImpl.java b/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/profile/impl/DefaultProfileImpl.java index 1e6863651c..258e58dc76 100644 --- a/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/profile/impl/DefaultProfileImpl.java +++ b/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/profile/impl/DefaultProfileImpl.java @@ -229,8 +229,10 @@ public String parseModel(String jsonModel, String preProcessingData) { res = unmarshaller.unmarshall(jsonModel, preProcessingData); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Map saveMap = new HashMap(); - saveMap.put(XMLResource.OPTION_ENCODING, "UTF-8"); - saveMap.put(XMLResource.OPTION_DEFER_IDREF_RESOLUTION, true); + saveMap.put( XMLResource.OPTION_ENCODING, "UTF-8" ); + saveMap.put( XMLResource.OPTION_DEFER_IDREF_RESOLUTION, true ); + saveMap.put( XMLResource.OPTION_DISABLE_NOTIFY, true ); + saveMap.put( XMLResource.OPTION_PROCESS_DANGLING_HREF, XMLResource.OPTION_PROCESS_DANGLING_HREF_RECORD ); res.save(outputStream, saveMap); return outputStream.toString(); } catch (JsonParseException e) { diff --git a/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/profile/impl/JbpmProfileImpl.java b/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/profile/impl/JbpmProfileImpl.java index 119dfad178..7188d33d86 100644 --- a/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/profile/impl/JbpmProfileImpl.java +++ b/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/profile/impl/JbpmProfileImpl.java @@ -18,6 +18,7 @@ import org.jbpm.designer.bpmn2.impl.Bpmn2JsonUnmarshaller; import org.jbpm.designer.bpmn2.resource.JBPMBpmn2ResourceFactoryImpl; import org.jbpm.designer.bpmn2.resource.JBPMBpmn2ResourceImpl; +import org.jbpm.designer.notification.DesignerNotificationEvent; import org.jbpm.designer.repository.Repository; import org.jbpm.designer.repository.UriUtils; import org.jbpm.designer.util.ConfigurationProvider; @@ -28,8 +29,10 @@ import org.slf4j.LoggerFactory; import org.uberfire.backend.vfs.Path; import org.uberfire.backend.vfs.VFSService; +import org.uberfire.workbench.events.NotificationEvent; import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Event; import javax.inject.Inject; import javax.servlet.ServletContext; import javax.xml.stream.XMLInputFactory; @@ -62,6 +65,9 @@ public class JbpmProfileImpl implements IDiagramProfile { @Inject private VFSService vfsServices; + @Inject + private Event notification; + public JbpmProfileImpl(ServletContext servletContext) { this(servletContext, true, false); } @@ -294,27 +300,37 @@ public Definitions getDefinitions(String xml) throws Exception { Map options = new HashMap(); options.put( JBPMBpmn2ResourceImpl.OPTION_ENCODING, "UTF-8" ); options.put( JBPMBpmn2ResourceImpl.OPTION_DEFER_IDREF_RESOLUTION, true ); + options.put(JBPMBpmn2ResourceImpl.OPTION_DISABLE_NOTIFY, true); + options.put( JBPMBpmn2ResourceImpl.OPTION_PROCESS_DANGLING_HREF, JBPMBpmn2ResourceImpl.OPTION_PROCESS_DANGLING_HREF_RECORD ); InputStream is = new ByteArrayInputStream(xml.getBytes("UTF-8")); resource.load(is, options); + if(!resource.getErrors().isEmpty()) { + String errorMessages = ""; + for (Resource.Diagnostic error : resource.getErrors()) { + errorMessages += error.getMessage() + "\n"; + } + notification.fire( new DesignerNotificationEvent( errorMessages, NotificationEvent.NotificationType.ERROR ) ); + } + + if(!resource.getWarnings().isEmpty()) { + String warningMessages = ""; + for(Resource.Diagnostic warning : resource.getWarnings()) { + warningMessages += warning.getMessage() + "\n"; + } + notification.fire( new DesignerNotificationEvent( warningMessages, NotificationEvent.NotificationType.WARNING ) ); + } EList warnings = resource.getWarnings(); if (warnings != null && !warnings.isEmpty()){ for (Diagnostic diagnostic : warnings) { - _logger.info("Warning: "+diagnostic.getMessage()); - } - } - - EList errors = resource.getErrors(); - if (errors != null && !errors.isEmpty()){ - for (Diagnostic diagnostic : errors) { - _logger.info("Error: "+diagnostic.getMessage()); + _logger.info("Warning: " + diagnostic.getMessage()); } - throw new Exception("Error parsing process definition"); } return ((DocumentRoot) resource.getContents().get(0)).getDefinitions(); } catch(Exception e) { + _logger.error(e.getMessage()); throw new Exception(e); } } diff --git a/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/server/TransformerServlet.java b/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/server/TransformerServlet.java index 20131f9172..bdc62a53c0 100644 --- a/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/server/TransformerServlet.java +++ b/jbpm-designer-backend/src/main/java/org/jbpm/designer/web/server/TransformerServlet.java @@ -64,6 +64,7 @@ import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.util.FeatureMap; +import org.eclipse.emf.ecore.xmi.XMLResource; import org.jboss.drools.impl.DroolsFactoryImpl; import org.jbpm.designer.bpmn2.resource.JBPMBpmn2ResourceFactoryImpl; import org.jbpm.designer.bpmn2.resource.JBPMBpmn2ResourceImpl; @@ -263,6 +264,8 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) JBPMBpmn2ResourceImpl bpmn2resource = (JBPMBpmn2ResourceImpl) rSet.createResource(URI.createURI("virtual.bpmn2")); bpmn2resource.getDefaultLoadOptions().put(JBPMBpmn2ResourceImpl.OPTION_ENCODING, "UTF-8"); bpmn2resource.getDefaultLoadOptions().put(JBPMBpmn2ResourceImpl.OPTION_DEFER_IDREF_RESOLUTION, true); + bpmn2resource.getDefaultLoadOptions().put( JBPMBpmn2ResourceImpl.OPTION_DISABLE_NOTIFY, true ); + bpmn2resource.getDefaultLoadOptions().put(JBPMBpmn2ResourceImpl.OPTION_PROCESS_DANGLING_HREF, JBPMBpmn2ResourceImpl.OPTION_PROCESS_DANGLING_HREF_RECORD); bpmn2resource.setEncoding("UTF-8"); rSet.getResources().add(bpmn2resource); bpmn2resource.getContents().add(def); @@ -273,6 +276,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) resp.setContentType("application/json"); resp.getWriter().print(json); } catch(Exception e) { + e.printStackTrace(); _logger.error(e.getMessage()); resp.setContentType("application/json"); resp.getWriter().print("{}"); diff --git a/jbpm-designer-client/src/main/java/org/jbpm/designer/client/DesignerPresenter.java b/jbpm-designer-client/src/main/java/org/jbpm/designer/client/DesignerPresenter.java index 0fa01aee1e..ab95cf5844 100644 --- a/jbpm-designer-client/src/main/java/org/jbpm/designer/client/DesignerPresenter.java +++ b/jbpm-designer-client/src/main/java/org/jbpm/designer/client/DesignerPresenter.java @@ -7,7 +7,6 @@ import javax.inject.Inject; import com.google.gwt.core.client.GWT; -import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.IsWidget; import org.guvnor.common.services.shared.file.CopyService; import org.guvnor.common.services.shared.file.DeleteService; diff --git a/jbpm-designer-client/src/main/java/org/jbpm/designer/client/notification/DesignerNotificationPopupsManager.java b/jbpm-designer-client/src/main/java/org/jbpm/designer/client/notification/DesignerNotificationPopupsManager.java index 28227c1281..f144689bc5 100644 --- a/jbpm-designer-client/src/main/java/org/jbpm/designer/client/notification/DesignerNotificationPopupsManager.java +++ b/jbpm-designer-client/src/main/java/org/jbpm/designer/client/notification/DesignerNotificationPopupsManager.java @@ -1,11 +1,107 @@ package org.jbpm.designer.client.notification; -/** - * Created with IntelliJ IDEA. - * User: tihomirsurdilovic - * Date: 4/8/14 - * Time: 10:21 AM - * To change this template use File | Settings | File Templates. - */ +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.Window; +import org.jboss.errai.ioc.client.container.SyncBeanManager; +import org.jbpm.designer.notification.DesignerNotificationEvent; +import org.uberfire.client.workbench.widgets.notifications.NotificationPopupView; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +import org.uberfire.client.workbench.widgets.animations.LinearFadeOutAnimation; + +@ApplicationScoped public class DesignerNotificationPopupsManager { + + @Inject + private SyncBeanManager iocManager; + + //When true we are in the process of removing a notification message + private boolean removing = false; + + private final int SPACING = 48; + + private List activeNotifications = new ArrayList(); + private List deactiveNotifications = new ArrayList(); + + + /** + * Display a Notification message + * @param event + */ + public void addNotification( @Observes final DesignerNotificationEvent event ) { + + //Create a Notification pop-up. Because it is instantiated with CDI we need to manually destroy it when finished + final NotificationPopupView view = iocManager.lookupBean( NotificationPopupView.class ).getInstance(); + activeNotifications.add( view ); + view.setPopupPosition( getMargin(), + activeNotifications.size() * SPACING ); + view.setNotification( event.getNotification() ); + view.setType( event.getType() ); + view.setNotificationWidth( getWidth() + "px" ); + view.show( new Command() { + + @Override + public void execute() { + //The notification has been shown and can now be removed + deactiveNotifications.add( view ); + remove(); + } + + } ); + } + + //80% of screen width + private int getWidth() { + return (int) ( Window.getClientWidth() * 0.8 ); + } + + //10% of screen width + private int getMargin() { + return (int) ( ( Window.getClientWidth() - getWidth() ) / 2 ); + } + + //Remove a notification message. Recursive until all pending removals have been completed. + private void remove() { + if ( removing ) { + return; + } + if ( deactiveNotifications.size() == 0 ) { + return; + } + removing = true; + final NotificationPopupView view = deactiveNotifications.get( 0 ); + final LinearFadeOutAnimation fadeOutAnimation = new LinearFadeOutAnimation( view ) { + + @Override + public void onUpdate( double progress ) { + super.onUpdate( progress ); + for ( int i = 0; i < activeNotifications.size(); i++ ) { + NotificationPopupView v = activeNotifications.get( i ); + final int left = v.getPopupLeft(); + final int top = (int) ( ( ( i + 1 ) * SPACING ) - ( progress * SPACING ) ); + v.setPopupPosition( left, + top ); + } + } + + @Override + public void onComplete() { + super.onComplete(); + view.hide(); + deactiveNotifications.remove( view ); + activeNotifications.remove( view ); + iocManager.destroyBean( view ); + removing = false; + remove(); + } + + }; + fadeOutAnimation.run( 500 ); + } + }