I recently realized that I have this blog for five years already. More precisely, on March 31 2008, I did my first post here.
It was my project lead at that moment that encouraged me to write down some stuff I had done with jBPM. He is now a real good friend of mine, and it is funny to look back how different things where five years ago. In that same period of time I got a house, got married, two kids, a dog, was hired by JBoss to work on jBPM and later joined Alfresco to build Activiti.
I remember that my first posts on this blog caused quite a stir in the company I was working back then. Something about ‘company secrets’ or something. Yeah, it was five years ago, I told you. It’s good to see they changed their attitude towards blogging if I see my ex-colleagues freely blogging now.
Over the past five years, my blog grew really nicely. Below you can see an overview of the unique visitors / month. There’s a jump in June 2009, when I joined JBoss. For a long time, I’ve been floating around a steady 5K line but the last months traffic is sure on the rise as shown by the slope on the right (8850 unique visits last month). It really amazes me that a simple blog like this can attract this amount of people. Really humbling. As such, it really motivates me to continue posting on what I’m working on from day to day.
In those five years, I wrote 115 posts (that’s 23 posts on average each year), 223.211 people have visited this blog, of which 128.222 unique visitors. If you think about that number of people … that’ just amazing. That are more people than the amount of inhabitants of Bruges, one of the more famous cities in Belgium. But more importantly, over those five years the average time on the blog is 2:21 minutes. So people really take their time to write down what I write! In the age of twitter that means a lot.
The image below shows where all the visits are coming from.
The top 3 is no surprise, as it is where we also see most requests from in the Activiti community.
I can say nothing more than that I’m really humbled that you all take the time to read what I write down here.
Thank you. Thank all of the 128.222 of you!
One of the awesome things of having a big open source community is that it makes Activiti run on platforms and configuration which we don’t have in our automated QA environment. Regardless of how many people/time/money you have, there’s always going to be a funny configuration that makes the software behave just differently.
So one of the issues discovered and reported by Brian Showers in the Activiti forum, is that the test suite doesn’t run green on JDK 1.7.0._17. And indeed, my dev machine didn’t have the latest JDK installed nor had the QA at the time of writing.
So what is the problem? Well, in Activiti when using a scriptTask, you can write something like
<scriptTask id="myScript" scriptFormat="JavaScript">
<script><![CDATA[
var sum = a + b;
]]></script>
</scriptTask>
The variable sum will be automatically saved as a process variable (unless you set the property “activiti:autoStoreVariables=’false’” on the scriptTask). This has worked since whatever version of Activiti we introduced scripting. So it has worked for JDK 5, 6 and until recently 7. But on the latest JDK, at the time of writing 1.7.0_17, it doesn’t run. It throws an exception:
org.activiti.engine.ActivitiException: couldn't find a variable type that is able to serialize sun.org.mozilla.javascript.internal.Undefined@7216df
at org.activiti.engine.impl.variable.DefaultVariableTypes.findVariableType(DefaultVariableTypes.java:62)
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableInstance(VariableScopeImpl.java:359)
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableLocal(VariableScopeImpl.java:292)
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableLocal(VariableScopeImpl.java:279)
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:254)
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:237)
at org.activiti.engine.impl.scripting.ScriptBindings.put(ScriptBindings.java:83)
at javax.script.SimpleScriptContext.setAttribute(SimpleScriptContext.java:228)
at com.sun.script.javascript.ExternalScriptable.put(ExternalScriptable.java:182)
at sun.org.mozilla.javascript.internal.ScriptableObject.defineProperty(ScriptableObject.java:1394)
at sun.org.mozilla.javascript.internal.ScriptRuntime.initScript(ScriptRuntime.java:3239)
at sun.org.mozilla.javascript.internal.Interpreter.initFrame(Interpreter.java:2695)
at sun.org.mozilla.javascript.internal.Interpreter.interpret(Interpreter.java:844)
at sun.org.mozilla.javascript.internal.InterpretedFunction.call(InterpretedFunction.java:162)
at sun.org.mozilla.javascript.internal.ContextFactory.doTopCall(ContextFactory.java:429)
at com.sun.script.javascript.RhinoScriptEngine$1.superDoTopCall(RhinoScriptEngine.java:116)
at com.sun.script.javascript.RhinoScriptEngine$1.doTopCall(RhinoScriptEngine.java:109)
at sun.org.mozilla.javascript.internal.ScriptRuntime.doTopCall(ScriptRuntime.java:3161)
at sun.org.mozilla.javascript.internal.InterpretedFunction.exec(InterpretedFunction.java:173)
at sun.org.mozilla.javascript.internal.Context.evaluateReader(Context.java:1159)
...
So it seems that in the latest JDK 7 version, somehow the class that is used to represent a scripting variable in the JDK isn't Serializable anymore. However, if you switch your script to
<scriptTask id="myScript" scriptFormat="JavaScript">
<script><![CDATA[
var sum = a + b;
execution.setVariable("sum", sum);
]]></script>
</scriptTask>
It just works. When I debugged, an object of the class 'Double' was passed instead of 'Undefined' in the first example. So when calling a Java object from a script, some auto-coercion/conversion is happening ... which is not happening in the first example. Unfortunately all the classes involved are internal classes (eg sun.javascript.xxxxx) so we can't just force the conversion somehow as this would break on some systems.
So what options do we have
So the current thinking is that we wait a bit until next release. In the mean time, I'l try to figure out how to report a JDK bug.
People that are depending on the auto storage of variable will have to use the workaround with execution.setVariable(name, value); for the moment. If anybody has any input regarding this issue, please let us know!
In the Activiti 5.12 release, we added reporting capabilities on top of the Activiti engine, demonstrating the concepts through the Activiti Explorer web application (but of course usable everywhere).
Now, don’t be fooled: since a very long time , the Activiti engine has the capability of gathering historical or audit data when you execute business processes. All this data is stored in the historical database tables and can thus be easily queried. Which means that any reporting tool such as JasperReports, Birt, Crystal Reports etc. can just use the historical tables as a datasource to produce reports in any format you’d like (Word, PDF, …) to get insight how your business is executing its business processes. I’ll probably blog such an example pretty soon.
But the thing where I’d like to focus on today is the web side of things: web charts/reports which can be combined into a dashboard for example. The first thing we must be able to do is to expose the historical data in a way we can use it to create these charts. But where do you put the logic (the queries and data manipulation) to generate a dataset for the chart and/or the report? Do you embed the SQL in your UI-layer? Of course not. What if multiple applications want to use the data? What if we want to store the generated dataset to get a snapshot of the data at a certain point in time.
When we thought about this problem we first though about it in the traditional way. A new service with reporting capabilities, probably using some sort of DSL to define the dataset generation which are stored in some kind of data store. Anyway, a whole new things and concepts to learn and master. Not to mention extra implementation and maintenance.
But then it hit us. Everything we needed is already available in the Activiti engine. If we use a process to define the logic to create the dataset for the report, we can leverage all the facilities of the engine. The only requirement for this process is that it generates the JSON data which follows a fixed format. Some benefits
Since the generation of the dataset is done by a process, everything possible in a process can be used. So you can use Java delegate classes or whatever you fancy.
But since the kool kids nowadays are using Javascript, we added some example process to the demo data of Activiti Explorer that use the scripting functionality in the engine. The nice thing about Javascript is that JSON is native to it, and creating a JSON object is really easy. As said above, the only requirement for such a process is that it must generate the JSON dataset following the predefined format.
For example to generate an overview of all past process instances we could have a process like this:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="activiti-report">
<process id="process-instance-overview-report" name="Process Instance Overview" isExecutable="true">
<startEvent id="startevent1" name="Start" />
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="generateDataset" />
<scriptTask id="generateDataset" name="Execute script" scriptFormat="JavaScript" activiti:autoStoreVariables="false">
<script><![CDATA[
importPackage(java.sql);
importPackage(java.lang);
importPackage(org.activiti.explorer.reporting);
var result = ReportingUtil.executeSelectSqlQuery("SELECT PD.NAME_, PD.VERSION_ , count(*) FROM ACT_HI_PROCINST PI
inner join ACT_RE_PROCDEF PD on PI.PROC_DEF_ID_ = PD.ID_ group by PROC_DEF_ID_");
var reportData = {};
reportData.datasets = [];
// Native json usage
var dataset = {};
dataset.type = "pieChart";
dataset.description = "Process instance overview (" + new java.util.Date() + ")";
dataset.data = {};
while (result.next()) { // process results one row at a time
var name = result.getString(1);
var version = result.getLong(2)
var count = result.getLong(3);
dataset.data[name + " (v" + version + ")"] = count;
}
reportData.datasets.push(dataset);
// Storing the json as process variable
execution.setVariable("reportData", new java.lang.String(JSON.stringify(reportData)).getBytes("UTF-8"));
]]></script>
</scriptTask>
<sequenceFlow id="flow3" sourceRef="generateDataset" targetRef="theEnd" />
<endEvent id="theEnd" />
</process></em>
</definitions>
The script is pretty easy to understand. All it does is querying the database, creating a json object with the data and storing it as process variable. Since it is stored as a variable, it is basically a snapshot of the dataset. So at any time in the future you can just fetch the json data and look at the report as it was at that point in time.
The json produced by the process above can then easily be used to generate charts, as demonstrated by the Activiti Explorer application:
It is also very easy to see how easy it is to create a dashboard app with the same approach. But that’ll be for a next blogpost.
Thanks for reading!
I wasn’t going to give attention to this.
In my opinion, bad publicity is also free publicity. And that’s not deserved.
I really don’t care that people use Activiti, tweak and patch it and not share it. Many companies do that. But when they publicly shout out that they fork Activiti to create an own engine, they hurt the Activiti community. They hurt the community because the confusion they cause. And when you touch the Activiti community, you touch me. And so I decided to publicly talk about it.
The whole open source ecosystem is based on trust. It is based on people discussing problems and find solutions together, and working for a common cause to make the world better. Many companies work on open source and build own products on top of open source software and contributing certain pieces back. That’s how open source works.
I know the people in Camunda for at least 5 years. Even before we worked together on jBPM I discussed software and bpm with them in the pub. I really enjoy(ed) working with the people from Camunda on Activiti, especially Daniel whom I connected with on a technical level very few people can. So imagine my surprise when Daniel called us to let us know that they decided to fork Activiti and start their own bpm platform. Open source and apache licensed. No discussion up front. We didn’t have any arguments. They basically claim our visions don’t match anymore. In open source, or in any collaboration between people you first bring issues you have to the table. You discuss the problems. You try to find a solution. And when that fails … maybe then you can decide to pull the plug and fork.
But nothing of that happened. Just the message out of the blue that the fork was going to happen. Hell, we organized the Activiti Community Day of past November together with them. Just a few weeks ago I proposed a get-together meetup to rocket-strap the roadmap of Activiti. Was all of this already decided then?
I wouldn’t have reacted if they just announced their own platform. But a fork of Activiti is really damaging for our community. Our CTO and their CEO discussed this via email and we got the following message from him (copied verbatim):
That’s said be assured that we do not place a message like “we fork Activiti”
Let me just post a screenshot of the title of the article of them. Enough said.
I really trusted them. I trusted that they would do the honorable. I really did.
So I’m not only let down on the community-aspect of things, but also on a personal level.
Open source is build on trust. Forking an open source project is introducing distrust. It confuses people. It makes them wonder ‘why would these guys fork’ or ‘is there something wrong with Activiti that we don’t know?’. I don’t know the answer to the why-question. I don’t understand it and I don’t know why. Maybe I would have if we would have discussed things.
There is nothing wrong with Activiti. The last couple of months were our most productive ever. We released version 5.12 and it was *packed* with features and fixes.
There is nothing wrong with Activiti. Hell, the ‘migration guide’ from Activiti to Camunda is basically just a jar rename. Some docs are copied exactly from the Activiti docs. Where is the difference in vision between the engines? Why not work together and contribute the missing pieces to Activiti? I think you yourself can be the judge on this.
Was it about branding? Do they want their own sticker on the bits and pieces? While Alfresco is putting a lot of money in Activiti, I don’t believe Alfresco has ever tried to mark the project. There isn’t a mention of Alfresco on the Activiti home page (and we’re proud on that Alfresco treats Activiti as such!). Again, all these things could have been discussed and solved. But none of that happened.
But things are what they are. Camunda have forked Activiti and that’s how it is. It is sad on different levels, but let’s focus on the future now. We will build your trust. We will prove that this fork has no impact on Activiti.
I’ve been chatting with a lot of people of the community in the past days and they all share our disbelief about the fork. Activiti has never been more alive than today.
At Alfresco we have a new CEO since a few months and workflow/bpm is in the list of top priorities of our management. Together with our many other contributors we will keep pushing Activiti, and maintain it’s status as the number one fully open-source platform for BPM and workflow.
I’m sorry for the confusion being caused. I’d like to see it different, too.
Believe me on that Activiti will keep to it’s core open source values and we will continue to strive to make Activiti even better than it is. Each and every single day. You can trust me on that.
Joram
PS: feel free to leave any happy Activiti comments to this post. It will cheer me up. I need that now
.
This morning when I was at the breakfast table I read the news that Google is shutting down Google Reader in July. I was completely flabbergasted. Every day, sipping my morning coffee, I go to Google Reader to see what has happened in the software world the past day. That has been my routine since 2008, regardless of which employer I had or which project I was doing. I can’t think of any service I use for that long and for that amount of time per day. Or maybe I can … GMail. And it got me thinking.
But let me take a step back first. Google proclaims it has seen a decline in its usage. Yet, when I see my twitter feeds and the rss feeds (in Reader) all I can see is the Google Reader news. Yes, I’m probably biased since I’m a software developer and I tend to talk with and follow fellow geeks. This is how Mashable.com puts it:
Hear that clunking sound? That’s thousands of jaws dropping at the news that Google Reader is going to be retired come July 1, 2013. That whooshing sound is “Google Reader” shooting to the top of Twitter’s worldwide trends, even on a day when a new pope was picked.
And that giant “NOOOOOOOO” sound is the Internet’s reaction to Google’s most unpopular decision in — well, as far back as I can remember.
I would gladly pay to use Google Reader. According my my stats I’ve read about 35.000 blogs through Reader since I started using it. It is my single source to keep up to date with the industry and Twitter or any other social media by no means comes close. The noise on there is just to large. Somewhere I read “Google Reader to Twitter is like a filing cabinet to a bag of cats”.
I also hope that Google thought very well about the people that they piss off now. A vocal and influential group (if only it were that they install and maintain the other family members computers
) Kyle Wild, CEO at Keen_IO states it clearly:
Google Reader had few users, but think about who they were:media mavens, tech press, and Google fanboys/fangirls.Bad segment to upset…
— Kyle Wild (@dorkitude) March 14, 2013
Why Open Source Matters
![]()
But let’s quit whining. We all knew this could happen one day, right? After all, Reader is in the hands of one colossal company and is publicly traded on the stock market. They aren’t doing this for charity.
But me, and many others, rely on Google every day. It runs my life: e-mail, calendar, navigation, … heck, when my internet is down I check www.google.com because it is always there. And in the back of our heads we know that is one company behind all these things and yes, we know that this is a bad thing….. but Google is not evil, right?
The facts are however plain and simple: if you don’t control it, the company owning it might pull the plug any day. They have any right to do so. And that brings me the title of this post (by the way, I wonder what the impact on my users visits will be once Reader goes down). I’m an open source guy. And this move by Google really made me reinforce my believe in open source software (again).
You see, if we would decide to pull the plug on Activiti or Alfresco today, it will be bad news for sure. But because the software we write is open source, it only means the people who are currently writing code for the Activiti and Alfresco project are gone. Activiti and Alfresco will still exist. The code, the documentation will be there. You can still open the code and patch it. Other committers will still be there. Somebody or some other company could take leadership and continue.
The point is: you’re not at the mercy of one single company. And in this era, where everything is becoming cloud-based and closed, we should really think about what brought us to open source software in the first place. Do we want to put our businesses in the hands of other mega-corporations wo care very little about us? Do we want a vendor/service-lock-in for our critical businesses?
I’m very happy that the company I work for, Alfresco, has the right mindset on this. Not only for letting me work on open source software. Yes, we do have Alfresco Cloud. But it is build on the same codebase as the one we’re shipping as community edition. If for some reason the plug is pulled, anybody can take the code, tweak it and run it. Even build their own cloud version. With Alfresco and Activiti, we’ve got nothing to hide. Our code is right there. That is our strength. It’s not only about a kick-ass product. It’s about openness and freedom to be in control of your own path, regardless of what happens.
Don’t get me wrong though, I’m not saying everything should be open-source. Companies still need to make money. But the core, the foundations needs to be. Take GitHub for example: it offers services on top of an open source version control system (Git). If GitHub pulls the plug, I can happily still code away. Sure it will hurt a bit in the beginning, but I’m not locked in.
That being said … anybody has decent alternative for Google Reader? Preferably open source.
Activiti has many ways to integrate with your environment and technologies. Forms is typically an area where there are many different solutions needed. Activiti offers out of the box two options: using a form key (that is how for example Alfresco forms are correlated with process tasks) or using form properties. The details of forms and form properties are described in the userguide, but basically it boils down to
For example, this is how the form properties can be defined on a user task:
<userTask id="provideNewSalesLead" name="Provide new sales lead" activiti:assignee="${initiator}">
<extensionElements>
<activiti:formProperty id="customerName" name="Customer name" type="string" required="true"/>
<activiti:formProperty id="potentialProfit" name="Potential profit" type="long" />
<activiti:formProperty id="details" name="Details" type="string"/>
</extensionElements>
</userTask>
To fetch this information at runtime, you have to use something like:
TaskFormData taskFormData = formService.getTaskFormData(myTaskId); List<FormProperty> formProperties = taskFormData.getFormProperties();
When using Activiti Explorer, all of the supported form properties have specific renderes that know how to render that form property type. Eg. a date form property will be visualized using a date picker, a number property must be checked on validity, etc.
The xml example of above looks as follows in Explorer:
A question we often hear on the Activiti Forum is how to add new form property types and render them in Explorer. And actually doing that is very easy.
Let’s start with a simple example: we want to add a form property type that allows the user to select a month. Visually, we want to use a dropdown menu for that on the UI side of things.
Create a new class that subclasses AbstractFormType and implement the two abstract methods:
public class MonthFormType extends AbstractFormType {
public static final String TYPE_NAME = "month";
public String getName() {
return TYPE_NAME;
}
public Object convertFormValueToModelValue(String propertyValue) {
Integer month = Integer.valueOf(propertyValue);
return month;
}
public String convertModelValueToFormValue(Object modelValue) {
if (modelValue == null) {
return null;
}
return modelValue.toString();
}
}
To understand this code, you just have to keep in mind that for the FormService, everyhing is a string. This is done so it is very easy to include in your own UI technology. The FormType class itself, like the one above will do the actual conversion to real objects.
The convertFormValueToModelValue method implements the conversion from the form to the internal object. Here it’s very simple, we assume the value stored in the form is an integer which is the number of the month. The other method, convertModelValueToFormValue is the other way around. Since we just use the month index as model value here, that’s also pretty easy.
The form type must now be plugged into the process engine config:
<bean id="processEngineConfiguration" ... >
...
<property name="customFormTypes">
<list>
...
<bean class="org.activiti.explorer.form.MonthFormType"/>
</list>
</property>
</bean>
For the renderer in Activiti Explorer, we need to write the dropdown of months. We also know from the MonthFormType above that we must send an integer (as a string) back and forth indicating the index of the month:
public class MonthFormPropertyRenderer extends AbstractFormPropertyRenderer {
private static final long serialVersionUID = 1L;
public MonthFormPropertyRenderer() {
super(MonthFormType.class);
}
public Field getPropertyField(FormProperty formProperty) {
ComboBox comboBox = new MonthCombobox(getPropertyLabel(formProperty));
comboBox.setRequired(formProperty.isRequired());
comboBox.setRequiredError(getMessage(Messages.FORM_FIELD_REQUIRED, getPropertyLabel(formProperty)));
comboBox.setEnabled(formProperty.isWritable());
// Fill combobox
I18nManager i18nManager = ExplorerApp.get().getI18nManager();
for (int i=0; i<12; i++) {
comboBox.addItem(i);
comboBox.setItemCaption(i, i18nManager.getMessage(Messages.MONTH_PREFIX + i));
}
return comboBox;
}
}
In the code it is easy to see how that is done: we simple set the caption to the month name and store the index as value. In our messages.properties file, we do need to have entries like ‘month.0=January’ to make this work.
The renderer must now be plugged into the FormPropertyRenderManager bean. You can find this bean in the activiti-ui-context.xml file:
<bean id="formPropertyRendererManager" class="org.activiti.explorer.ui.form.FormPropertyRendererManager" lazy-init="true">
...
<property name="propertyRenderers">
<list>
...
<bean class="org.activiti.explorer.ui.form.MonthFormPropertyRenderer" />
</list>
</property>
</bean>
The custom form type and renderer are now known to the system. If you use it in your BPMN 2.0 processes like this
<activiti:formProperty id="month" name="Month" type="month" required="true" />
It will be rendered nicely to this:
And that’s all there is to it!
The previous example was of course pretty easy. Let me show you how you can create a more complex form type. In the following example, we want the user to select a process definition from a drop-down.
The FormType implementation is a bit more involved now. Again, the value that is being sent back and forth is a string. So for this case, we use the process definition id and convert it to a ProcessDefinition object when needed:
public class ProcessDefinitionFormType extends AbstractFormType {
public static final String TYPE_NAME = "processDefinition";
public String getName() {
return TYPE_NAME;
}
public Object convertFormValueToModelValue(String propertyValue) {
if(propertyValue != null) {
ProcessDefinition processDefinition = ProcessEngines.getDefaultProcessEngine()
.getRepositoryService()
.createProcessDefinitionQuery()
.processDefinitionId(propertyValue)
.singleResult();
if(processDefinition == null) {
throw new ActivitiObjectNotFoundException("Process definition with id " + propertyValue + " does not exist", ProcessDefinitionEntity.class);
}
return processDefinition;
}
return null;
}
public String convertModelValueToFormValue(Object modelValue) {
if (modelValue == null) {
return null;
}
if (!(modelValue instanceof ProcessDefinition)) {
throw new ActivitiIllegalArgumentException("This form type only support process definitions, but is " + modelValue.getClass());
}
return ((ProcessDefinition) modelValue).getId();
}
}
We must now make the user select a process definition from the dropdown, and send the process definition back and forth. Keep in mind that the renderer only keeps the process definition id’s as values, not the process definition objects. The object is only retrieved by the ProcessDefinitionFormType class above when the user actually submits the form.
public class ProcessDefinitionFormPropertyRenderer extends AbstractFormPropertyRenderer {
private static final long serialVersionUID = 1L;
public ProcessDefinitionFormPropertyRenderer() {
super(ProcessDefinitionFormType.class);
}
public Field getPropertyField(FormProperty formProperty) {
ComboBox comboBox = new ComboBox(getPropertyLabel(formProperty));
comboBox.setRequired(formProperty.isRequired());
comboBox.setRequiredError(getMessage(Messages.FORM_FIELD_REQUIRED, getPropertyLabel(formProperty)));
comboBox.setEnabled(formProperty.isWritable());
List<ProcessDefinition> processDefinitions = ProcessEngines.getDefaultProcessEngine()
.getRepositoryService()
.createProcessDefinitionQuery()
.orderByProcessDefinitionName().asc()
.orderByProcessDefinitionVersion().asc()
.list();
for (ProcessDefinition processDefinition : processDefinitions) {
comboBox.addItem(processDefinition.getId());
String name = processDefinition.getName() + " (v" + processDefinition.getVersion() + ")";
comboBox.setItemCaption(processDefinition.getId(), name);
}
return comboBox;
}
}
And of course both must be configured in the configuration files as described in the previous example. The result looks as follows:
That’s all there is to it to create custom form properties. Happy Coding!
After two and a half year of peer pressure, my Activiti-Co-Core-Developer Frederik has given in and started blogging (again)!
As he pledged to write about Activiti on a regular basis, I think it’s fair I point you guys to his blog so you can bookmark/rss/whatever him and give him some well-earned traffic. As a matter of facts, he just posted a blog on dynamically creating processes and deploying them in 100 lines, demonstrating some of the new features of the fresh 5.12 release!
Running Activiti on the cloud is really easy. All you need is a simple database and a web container if you’d like to run the Activiti Explorer UI. Since Activiti uses very little memory and scales/clusters horizontally out-of-the-box, it is a perfect fit for a cloud deployment.
Doing such a deployment on the cloud gets easier every day. Of course you can set up your own stack on Amazon, but nowadays there are many dedicated PaaS solutions that make your life much easier by offering dedicated platforms. At least from a click-and-run point of view these are much easier to work with. CloudBees is such a PaaS specifically tailored to developers and Java applications.
For framework builders like us, CloudBees offers a very interesting concept called a ‘clickstart’. If you’re interested, here are the full details. But basically, you just have to put a simple json file online (I chose to use GitHub) that tells where to find your war file and what kind of configuration you want. If you want, you can also specify a repository and CloudBees will build and deploy it for you (they hired the lead developer of Jenkins, after all). The CloudBees platform then allows you to go to a special url, passing the url to your json as parameter, which boots up a cloud instance with your app.
So, as we released Activiti 5.12 two days ago (at midnight at the bar, nonetheless) I thought it was a good idea to create a CloudBees Clickstart for Activiti Explorer 5.12. Simply click on the button below (it goes to that special url). You will have to create a CloudBees account (don’t worry, it’s free and you don’t need to provide a credit card like on Amazon) to run your own personal instance of Activiti Explorer 5.12 on the CloudBees cloud.
If that is too much hassle (trust me, it isn’t), you can also try out my demo instance. I’m assuming you’ll see some delays once people start hitting it … so it’s best to try it on your own account.
Update: some funny people think it is funny to change the user passwords (I do too, in some way
)… so if you can’t login to my instance you’ll have to wait until I restart it … or run your own instance :-0
By coincidence I stumbled upon the following slideware this weekend: http://portal.sliderocket.com/CJAKM/xml-attacks
In those slides, the author proclaims that you can make your XML so that it can attack your server. As this sounded pretty fishy, I decided to give it a try with Activiti. I created a ‘Billion Laugh Attack’ as described in the link above:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE lols [ <!ENTITY lol "lol"> <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" targetNamespace="Examples"> <process id="oneTaskProcess" name="The One Task Process"> <documentation>This is a process for testing purposes</documentation> <startEvent id="theStart" /> <sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" /> <userTask id="theTask" name="my task" /> <sequenceFlow id="flow2" sourceRef="theTask" targetRef="theEnd" /> <endEvent id="theEnd" /> </process> </definitions>
This simple process (without the doctype and entity stuff) normally parses like nothing. But when I added those malicious snippets of XML to my process … the engine started parsing the process … and kept parsing … and parsing …. and after 15 minutes I killed the process, not having finished the parsing of this simple process.
Of course, normally the XML’s that are uploaded to the Activiti process engine are under control of the developers. So typically it is a non-issue. But still. Suppose someone actually offers to upload any process BPMN 2.0 XML to end users …. Just fire off a few of these processes and you’ll see your server go down very easily.
I must say, I was completely flabbergasted by this. I assumed I could trust the JDK xml classes to do the right thing. But the fact is … it is pretty hard to know what the ‘right thing’ is here. I think many frameworks that allow xml that are using JDK xml parsing will have a problem with this.
Anyway, we quickly patched the Activiti engine to not parse any doctype or resolve any entity references (and some other fixes as mentioned in the link above) (see commit) and all is unicorns and rainbows again.
But it learned me a valuable lesson (again): never assume your code is safe. There is always someone out there with more time and creativity and bad intentions.
Open source software exists by the virtue of its community members. Without them using, discussing or cursing the software, it would simple cease to exist. So I’m always very happy when somebody from the community blogs about something they did with Activiti. As such, I very happily try to get as much people to their respective blogs as a service back.
Without further ado, I’m very happy to link to Saeid’s blog, where he writes down his integration experiences with Activiti, Apache Shiro and Tapestry : http://www.saeidmirzaei.com/techblog/?p=100
Enjoy! And Saeid: thank you!