Spring webflow 1.0 - Downloading a file - spring-webflow

I am using spring webflow 1.0. I am uploading a csv file, parsing it, and displying results before proceeding. The user has an option to download a csv file that contains the records that did not pass validation. When I click the link in a JSP to download this file, webflow invokes a form Action. The form action writes out a file via getting the output stream off the response:
HttpServletResponse response = ((ServletExternalContext) context.getExternalContext()).getResponse();
I do not want to leave the jsp I'm currently on. I just want to download the file. In other words, I do not want to transition to another state. I just want to serve the dynamically rendered file. Everything works (I don't leave the page, and I download the file), but I'm getting the following error in my console:
_pEncydKfggPHJo8=org.springframework.webflow.engine.NoMatchingTransitionException: No transition was matched on the event(s) signaled by the [1] action(s) that executed in this action state 'downloadErrorReportAction' of flow 'myFlow'; transitions must be defined to handle action result outcomes -- possible flow configuration error? Note: the eventIds signaled were: 'array<String>[[null]]', while the supported set of transitional criteria for this action state is 'array<TransitionCriteria>[[empty]]'
at org.springframework.webflow.engine.ActionState.doEnter(ActionState.java:187)
at org.springframework.webflow.engine.State.enter(State.java:191)
at org.springframework.webflow.engine.Transition.execute(Transition.java:212)
at org.springframework.webflow.engine.TransitionableState.onEvent(TransitionableState.java:107)
at org.springframework.webflow.engine.Flow.onEvent(Flow.java:534)
Truncated. see log file for complete stacktrace
Here is the relevant portion of my webflow config.
<view-state id="showUploadResults" view="UploadResults3.0">
<render-actions>
<action bean="UploadResultsAction" method="transitionToWebflow"/>
<action bean="UploadResultsAction" method="setupData"/>
</render-actions>
<transition on="submit" to="proceed"/>
<transition on="downloadErrorReport" to="downloadErrorReportAction"/>
</view-state>
<action-state id="downloadErrorReportAction">
<action bean="UploadResultsAction" method="downloadErrorReport" name="downloadErrorReport"/>
</action-state>

I was able to do this by defining a view-state and specifying a render action.
<view-state id="downloadErrorReportAction">
<render-actions>
<action bean="uploadResultsAction" method="downloadErrorReport" name="downloadErrorReport"/>
</render-actions>
</view-state>
I also had to add the same transitions from the previous action, because once you click the download link, you transition to another state. So the final config looks like the following:
<view-state id="showUploadResults" view="UploadResults3.0">
<render-actions>
<action bean="UploadResultsAction" method="transitionToWebflow"/>
<action bean="UploadResultsAction" method="setupData"/>
</render-actions>
<transition on="submit" to="proceed"/>
<transition on="downloadErrorReport" to="downloadErrorReportAction"/>
</view-state>
<view-state id="downloadErrorReportAction">
<render-actions>
<action bean="uploadResultsAction" method="downloadErrorReport" name="downloadErrorReport"/>
</render-actions>
<transition on="submit" to="proceed"/>
</view-state>

Related

Workflow: Setting default value for bpm:assignee

When I start my workflow, I want the option to assign it to somebody. If nobody is chosen, I want to default to assign it to the initiator.
Is this possible to do without creating a new model that extends bpm:assignee? If not, how would that extension be accomplished?
I believe this answer from Jeff Potts is relevant: https://stackoverflow.com/a/9418066/4542428
Note: I am using Community edition 4.2
EDIT: Stefan's answer got me the vast majority of the way to the answer, but it seems that I am somehow referencing the value of the association incorrectly. Context: I've never used associations, and this is likely just my failure to understand their difference from types and aspects.
From my model:
<type name="deliveryTicketWorkflow:start">
<parent>bpm:startTask</parent>
<properties>
</properties>
<associations />
<overrides />
<mandatory-aspects>
<aspect>deliveryTicketWorkflow:pmAspect</aspect>
<aspect>deliveryTicketWorkflow:requestDetailsAspect</aspect>
</mandatory-aspects>
</type>
<aspect name="deliveryTicketWorkflow:pmAspect">
<associations>
<association name="deliveryTicketWorkflow:assignedPM">
<source>
<mandatory>false</mandatory>
<many>false</many>
</source>
<target>
<class>cm:person</class>
<mandatory>false</mandatory>
<many>true</many>
</target>
</association>
</associations>
</aspect>
Which is used by my config as:
<config condition="activiti$deliveryTicketWorkflow" evaluator="string-compare">
<forms>
<form>
<field-visibility>
...
<show id="deliveryTicketWorkflow:assignedPM" />
...
</field-visibility>
<appearance>
...
<field id="deliveryTicketWorkflow:assignedPM" label-id="Project Manager" />
...
</appearance>
</form>
</forms>
</config>
My config for deliveryTicketworkflow:start is identical. This successfully displays the person selector, without making it mandatory, exactly 100% as Stefan said it would.
In my bpmn workflow definition, I then have these snippets in an execution listener for the start event:
if(!execution.getVariable("deliveryTicketWorkflow_assignedPM")){
execution.setVariable("deliveryTicketWorkflow_assignedPM", initiator);
}
...
deliveryTicket.properties["dtdlm:projectManager"] = execution.getVariable("deliveryTicketWorkflow_assignedPM").properties.firstName + " " + execution.getVariable("deliveryTicketWorkflow_assignedPM").properties.lastName;
When the workflow is run and I select somebody as the PM, that final line (where the first and last name of the PM is grabbed) returns a value of undefined for "deliveryTicketWorkflow_assignedPM". When it is left blank, everything works swimmingly but the General Info section of the Workflow description still lists the Project Manager as (None).
You could indeed customize the people picker, as jeff describes, but it requires quite some coding effort.
Alternatively you could use a workflow executionlistener for event start, and use it to set the bpm_assignee variable to the initiator in case it was empty on the form:
Add listener to you bpmn20:
<activiti:executionListener event="start" class="com.mycomp.Executionlistener"></activiti:executionListener>
In your first usertask, define the assignee to the association property in your workflow start form.
<userTask id="firsttask" name="firsttask" activiti:assignee="${mymodel.myassoc.properties.userName}" >
Also add this association to your start task content model.
Code in com.mycomp.Executionlistener will look like this:
public void notify(DelegateExecution execution) throws Exception {
if (execution.getVariable("mymodel_myassoc") == null ){
ActivitiScriptNode userScriptNode= (ActivitiScriptNode) execution.getVariable("initiator");
execution.setVariable("mymodel_myassoc",userScriptNode);
}
}

Problems exporting basic pure JavaScript charts using Asp.Net handler

I am having difficulty exporting a pure JavaScript chart from the basic examples provided in the C# solution (demo package from fusion charts). My sample is from the evaluation code provided on the FusionChart website.
I have added
exportenabled='1' exportHandler='http://localhost:64162/CS/Export_Handler/FCExporter.aspx'
to the existing Data.xml file in order to enable export using the local handler
Chart definition for the chart BasicExample/MultiChartsJS.aspx:
<chart caption='Monthly Unit Sales' xAxisName='Month' yAxisName='Units' showValues='0'
formatNumberScale='0' showBorder='1' exportenabled='1' exportHandler='http://localhost:64162/CS/Export_Handler/FCExporter.aspx'>
<set label='Jan' value='462' />
<set label='Feb' value='857' />
<set label='Mar' value='671' />
<set label='Apr' value='494' />
<set label='May' value='761' />
<set label='Jun' value='960' />
<set label='Jul' value='629' />
<set label='Aug' value='622' />
<set label='Sep' value='376' />
<set label='Oct' value='494' />
<set label='Nov' value='761' />
<set label='Dec' value='960' />
</chart>
I am pointing the exportHandler to the C# export handler (FCExporter) found in the same solution.
When invoking the export, the handler fires, but I am encountering two different runtime errors in the handler:
Failures inside FCExporter.cs
1)Jpeg conversion:
gr.Clear(ColorTranslator.FromHtml("#" + rawImageData["bgcolor"].ToString()));
is failing because rawImageData["bgcolor"] already contains a #
2)PDF conversion
int fRepeat = int.Parse(split_data[1]);
is failing with an out of range exception
It almost seems like the handler is out of sync with the charts that are being exported. It works if I point to the external fusion site for the export. Is there a new version of the handler available? I am using version 3.3.1-sr2.19840 of FusionCharts.js
It seems that you are using the export handler that is designed for exporting Flash charts only. For exporting JavaScript charts, you would need to setup export separately.
The .NET export handler is not yet equipped to handle JavaScript charts. Having said that, you are not out of luck yet. There are two options you still have:
Use pure server-side rendering to generate chart image - http://blog.fusioncharts.com/2013/03/how-to-save-charts-as-images-at-server-side/
Use the CodePlex SVG rendering engine for .NET after sending the chart's SVG data to server - http://svg.codeplex.com (You can get a chart's SVG string, even on IE, using chartInstance.toSVGString())
Note that if you are using PHP export handler, it can handle both flash and javascript export using the same export handler as mentioned in the documentation article - http://docs.fusioncharts.com/charts/contents/exporting-image/ECPureJS.html
Try adding following 2 attributes to your chart definition:
exportAtClient='0' exportAction='download'
This should make sure that export handler is being used.

Alfresco FormService Fundamental WRONG

Alfresco Forms Service does not work properly.
Looks like a fundamental Form functionality does not pass form configs (control-param name="nameHere") to webscripts.
I reproduce it step-by-step.
Create the Freemarker template and register it according to the documentation under /share project. The component renders and shows successfully. Everything works well.
Create a form according to the documentation on wiki Forms page.
Register it in <TOMCAT_INST/shared/classes/alfresco/web-
extension/myclok-form-config.xml> directory and pass it for load in such manner:
<bean id="sampleShareConfig" class="org.springframework.extensions.config.ConfigBootstrap" init-method="register">
<property name="configService" ref="web.config" />
<property name="configs">
<list>
<value>classpath:alfresco/web-extension/myclok-form-config.xml</value>
</list>
</property>
</bean>
<!-- ... share/WEB-INF/classes/org/springframework/extensions/surf/bootstrap/forms-bootstrap-context.xml -->
Form inst:
<config>
<forms>
<form id="myclok">
<view-form template="/org/alfresco/components/myclok/myclok.get.html.ftl" />
<edit-form template="/org/alfresco/components/myclok/myclok.get.html.ftl" />
<create-form template="/org/alfresco/components/myclok/myclok.get.html.ftl" />
<field-visibility>
<show id="currentPath" />
</field-visibility>
<appearance>
<field id="currentPath">
<control name="currentPath" template="/org/alfresco/components/myclok/myclok.get.html.ftl">
<control-param name="currentPath">sampleData1</control-param>
</control>
</field>
<control name="currentPath" template="/org/alfresco/components/myclok/myclok.get.html.ftl">
<control-param name="currentPath">sampleData2</control-param>
</control>
</appearance>
</form>
</forms>
</config>
In official documentation is written:
If the form element exists within a config element without an
evaluator and condition the form is always found, this is useful if
you want a certain field to appear on EVERY form in your application.
So, I specified it in myclok-form-config.xml file, but this approach does not give any result. Thus when the component with such reference to param is loaded by the following URL <http://localhost:8080/share/page/site/wcmqs/myclok> the Alfresco fails with the Exception:
Exception: freemarker.core.InvalidReferenceException - Expression field is undefined on line 6, column 6 in org/alfresco/components/myclok/myclok.get.html.ftl.
freemarker.core.TemplateObject.assertNonNull(TemplateObject.java:125)
freemarker.core.TemplateObject.invalidTypeException(TemplateObject.java:135)
freemarker.core.Dot._getAsTemplateModel(Dot.java:78)
In other words it's impossible to receive the value of the parameter of currentPath that is defined in FormConfigs.
So, the minimal functionality of FormsService / ConfigService does not work.
<#if field.control.params.currentPath??>
<#assign path=field.control.params.currentPath>
<#else>
<#assign path="someOtherDataValue">
</#if>
Does anyone know how to resolve it Or can demonstrate a working sample?
PS: All above described configurations of the form of FormService and the webscript component are attached.
The .AMP file for quick installation is required just to invoke the following command:
java -jar alfresco-mmt.jar install myclokStubFormComponent.amp ../tomcat/webapps/share.war
AMP file.
Config file.
I think you have mis-understood the difference betweent a form template and a field template. You are specifying the same file /org/alfresco/components/myclok/myclok.get.html.ftl to control the layout of the form as well as to render your fields.
When you use a field template to render the form you are getting an error that says the field object has not been populated. This is quite correct as the framework has not yet started to render the individual fields. You are rendering the form.
I'd suggest you try some more basic examples such as those that come with the Forms Development Kit (FDK) before you start implementing your own more complex forms, and especially before you begin to claim that the framework does not work as documented.

Spring-Webflow Using a HTML link to signal an event

I am playing around with Spring-Webflow (2.3), ZK (5.0.7.1) and ZK Spring (3.0).
Actually I'm trying to signal an event with a HTML link as described at Spring-Webflow.
<a href="${flowExecutionUrl}&_eventId=go2ProjectRoomView" >2 Project</a>
Part of my flow definition file looks like:
<view-state id="mainView">
<transition on="go2ProjectRoomView" to="projectRoomView" bind="false"/>
</view-state>
<view-state id="projectRoomView">
<transition on="go2MainView" to="mainView" bind="false"/>
</view-state>
If I deploy my web project and navigate to the main view following error appears:
The reference to entity "_eventId" must end with the ';' delimiter
Same error happens if I replace _eventId=go2ProjectRoomView by _eventId_go2ProjectRoomView.
Link to full stack trace.
The error you are receiving is actually an HTML/XML parsing error. Ampersand (&) is used to reference special characters/entities (see here). Change your link to:
<a href="${flowExecutionUrl}&_eventId=go2ProjectRoomView" >2 Project</a>
and you should be ok.

Spring webflow: how to keep track of persisted entity ids?

I'm working on a webflow (SWF2). The entities are generated using Roo. One of the webflow views, multi-instance.jspx, may be called multiple times to allow for multiple, persisted instances of the same entity (MyClass).
I'd like to keep a list of those persisted entities so that I can reference them on a later point in the flow. So far, I've tried the following.
A simplified version of my flow.xml looks like this:
<on-start>
<evaluate expression="new java.util.ArrayList()" result="flowScope.myList" result-type="java.io.Serializable"/>
</on-start>
<view-state id="multi-instance" view="multi-instance" model="myClass">
<binder>
<binding property="field1"/>
<binding property="field2"/>
</binder>
<on-entry>
<evaluate expression="new com.test.MyClass()" result="flowScope.myClass" />
</on-entry>
<transition on="another_instance" to="multi-instance"/>
<transition on="success" to="confirm"/>
<transition on="cancel" to="abort"/>
<on-exit>
<evaluate expression="myClass.persist()"/>
<evaluate expression="flowScope.myList.add(myClass)"/>
</on-exit>
</view-state>
The confirm and abort view-states are defined in flow.xml as well. The confirm.jspx looks like this:
<div xmlns:spring="http://www.springframework.org/tags" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:fn="http://java.sun.com/jsp/jstl/functions" xmlns:form="http://www.springframework.org/tags/form" xmlns:util="urn:jsptagdir:/WEB-INF/tags/util" xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
<jsp:directive.page contentType="text/html;charset=UTF-8" />
<jsp:output omit-xml-declaration="yes" />
<form:form>
<c:forEach items="${myList}" var="instance">
<li>${instance.getField1()} ${instance.getField2()}</li>
</c:forEach>
<div class="submit">
<input type="submit" id="success" name="_eventId_success" value="success"/>
<input type="submit" id="cancel" name="_eventId_cancel" value="cancel" />
</div>
</form:form>
</div>
So to the question:
Whenever I hit confirm.jspx, the web return says that there's an exception thrown at org.springframework.webflow.engine.impl.FlowExecutionImpl.wrap(FlowExecutionImpl.java:569).
EDIT: The Apache log is a little more enlightening. The following is a snippet of the top of the call stack:
SEVERE: Servlet.service() for servlet jsp threw exception org.apache.jasper.JasperException:
/WEB-INF/views/myflow/confirmation.jspx(6,7)
The function getField1 must be used with a prefix when a default namespace is not specified
at org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:40)
I'm not sure if the ArrayList-approach is possible; I believe I've read somewhere that the flowScope.myClass instance, as it is defined in the multi-instance-state, is picked up by the GC or at least falls out of scope. I'm not sure. If anyone can shed some light on that particular topic, I'd be thrilled.
(And if you happen to know a better way to keep a list of these persisted entities, please feel free to let me know!) Thanks in advance! :)
Update:
I'm able to count the number of elements in my list like so:
<c:choose>
<c:when test="${myList != null}">myList exists, it contains <c:out value="${fn:length(myList)}" /> items!</c:when>
<c:otherwise>myList doesn't exist.</c:otherwise>
</c:choose>
It shows the same number of elements as I've inserted. However, when I do this:
<c:forEach items="${myList}" var="instance">
<c:if test="${instance != null}">
<li>${instance.field1} ${instance.field2}</li>
</c:if>
</c:forEach>
nothing is displayed. (I can confirm that there is a correct number of <li>-elements when the null-test is omitted. Note also that I'm trying to access the properties directly, as indicated here: jstl/jsp - iterating over a vector of beans) I don't know what to think, regarding the scope here, but it seems clear that I can't access my entities via an ArrayList.
First, with regards to your question(s) around variable scoping, I would suggest looking at section 4.4 of the SWF documentation, where it describes all of the different available scopes.
Flow scope variables live through the lifetime of the flow. So your myClass variable will not go away until the flow exits. However keep in mind that your <on-entry> expression is assigning a new instance every time that view state is entered.
Second, I think you are probably on the right track with your solution. I would note several things:
You are persisting and adding to your list in the <on-exit> element -- this means these two things will always happen when you leave the state, including when you are doing your cancel transition. This may not be what you want. Related:
Your <transition on="another_instance" to="multi-instance"/> is actually exiting the view-state and re-entering it, triggering the <on-exit> and <on-entry> logic. It is possible to remain in the same state, by simply doing <transition on="another_instance">. Doing this will execute any logic you have inside the transition, and then re-render the view without actually changing states.
You may want to consider using the <var> tag to initialize variables... what you are doing with <evaluate expression="new ..."/> works but using <var> may be cleaner. Also, it is not necessary to say result-type="java.io.Serializable". result-type should be used when you need to convert the return type to something elese.
Finally, the error you are getting looks like it is unrelated to webflow. JSTL/EL allow you to access bean properties but not methods, and you are trying to invoke a method. See this question for more info.

Resources