This post is a guest post by Activiti co-founder and community member Joram Barrez (@jbarrez) who works for Alfresco. Thanks Joram! I’d like to see more of these community guest posts, so - as usual - don’t hesitate to ping me (@starbuxman) with ideas and contributions! -Josh Introduction Activiti is an Apache-licensed business process management (BPM) engine. Such an engine has as core goal to take a process definition comprised of human tasks and service calls and execute those in a certain order, while exposing various API’s to start, manage and query data about process instances for that definition. Contrary to many of its competitors, Activiti is lightweight and integrates easily with any Java technology or project. All that, and it works at any scale - from just a few dozen to many thousands or even millions of process executions. The source code of Activiti can be found on Github. The project was founded and is sponsored by Alfresco, but enjoys contributions from all across the globe and industries. A process definition is typically visualized as a flow-chart-like diagram. In recent years, the BPMN 2.0 standard (an OMG standard, like UML) has become the de-facto ‘language’ of these diagrams. This standard defines how a certain shape on the diagram should be interpreted, both technically and business-wise and how it is stored, as a not-so-hipster XML file.. but luckily most of the tooling hides this for you. This is a standard, and you can use any number of compliant tools to design (and even run) your BPMN processes. That said, if you’re asking me, there is no better choice than Activiti! Spring Boot integration Activiti and Spring play nicely together. The convention-over-configuration approach in Spring Boot works nicely with Activiti’s process engine is setup and use. Out of the box, you only need a database, as process executions can span anywhere from a few seconds to a couple of years. Obviously, as an intrinsic part of a process definition is calling and consuming data to and from various systems with all kinds of technologies. The simplicity of adding the needed dependencies and integrating various pieces of (boiler-plate) logic with Spring Boot really makes this child’s play. Using Spring Boot and Activiti in a microservice approach also makes a lot of sense. Spring Boot makes it easy to get a production-ready service up and running in no time and - in a distributed microservice architecture - Activiti processes can glue together various microservices while also weaving in human workflow (tasks and forms) to achieve a certain goal. The Spring Boot integration in Activiti was created by Spring expert Josh Long. Josh and I did a webinar a couple of months ago that should give you a good insight into the basics of the Activiti integration for Spring Boot. The Activiti user guide section on Spring Boot is also a great starting place to get more information. Getting Started The code for this example can be found in my Github repository. The process we’ll implement here is a hiring process for a developer. It’s simplified of course (as it needs to fit on this web page), but you should get the core concepts. Here’s the diagram: As said in the introduction, all shapes here have a very specific interpretation thanks to the BPMN 2.0 standard. But even without knowledge of BPMN, the process is pretty easy to understand: When the process starts, the resume of the job applicant is stored in an external system. The process then waits until a telephone interview has been conducted. This is done by a user (see the little icon of a person in the corner). If the telephone interview wasn’t all that, a polite rejection email is sent. Otherwise, both a tech interview and financial negotiation should happen. Note that at any point, the applicant can cancel. That’s shown in the diagram as the event on the boundary of the big rectangle. When the event happens, everything inside will be killed and the process halts. If all goes well, a welcome email is sent. This is the BPMN for this process Let’s create a new Maven project, and add the dependencies needed to get Spring Boot, Activiti and a database. We’ll use an in memory database to keep things simple. org.activiti spring-boot-starter-basic ${activiti.version} com.h2database h2 1.4.185 So only two dependencies is what is needed to create a very first Spring Boot + Activiti application: @SpringBootApplication public class MyApp { public static void main(String[] args) { SpringApplication.run(MyApp.class, args); } } You could already run this application, it won’t do anything functionally but behind the scenes it already creates an in-memory H2 database creates an Activiti process engine using that database exposes all Activiti services as Spring Beans configures tidbits here and there such as the Activiti async job executor, mail server, etc. Let’s get something running. Drop the BPMN 2.0 process definition into thesrc/main/resources/processes folder. All processes placed here will automatically be deployed (ie. parsed and made to be executable) to the Activiti engine. Let’s keep things simple to start, and create a CommandLineRunner that will be executed when the app boots up: @Bean CommandLineRunner init( final RepositoryService repositoryService, final RuntimeService runtimeService, final TaskService taskService) { return new CommandLineRunner() { public void run(String... strings) throws Exception { Map variables = new HashMap(); variables.put("applicantName", "John Doe"); variables.put("email", "
[email protected]"); variables.put("phoneNumber", "123456789"); runtimeService.startProcessInstanceByKey("hireProcess", variables); } }; } So what’s happening here is that we create a map of all the variables needed to run the process and pass it when starting process. If you’d check the process definition you’ll see we reference those variables using ${variableName} in many places (such as the task description). The first step of the process is an automatic step (see the little cogwheel icon), implemented using an expression that uses a Spring Bean: which is implemented with activiti:expression="${resumeService.storeResume()}" Of course, we need that bean or the process would not start. So let’s create it: @Component public class ResumeService { public void storeResume() { System.out.println("Storing resume ..."); } } When running the application now, you’ll see that the bean is called: . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.2.0.RELEASE) 2015-02-16 11:55:11.129 INFO 304 --- [ main] MyApp : Starting MyApp on The-Activiti-Machine.local with PID 304 ... Storing resume ... 2015-02-16 11:55:13.662 INFO 304 --- [ main] MyApp : Started MyApp in 2.788 seconds (JVM running for 3.067) And that’s it! Congrats with running your first process instance using Activiti in Spring Boot! Let’s spice things up a bit, and add following dependency to our pom.xml: org.activiti spring-boot-starter-rest-api ${activiti.version} Having this on the classpath does a nifty thing: it takes the Activiti REST API (which is written in Spring MVC) and exposes this fully in your application. The REST API of Activiti is fully documented in the Activiti User Guide. The REST API is secured by basic auth, and won’t have any users by default. Let’s add an admin user to the system as shown below (add this to the MyApp class). Don’t do this in a production system of course, there you’ll want to hook in the authentication to LDAP or something else. @Bean InitializingBean usersAndGroupsInitializer(final IdentityService identityService) { return new InitializingBean() { public void afterPropertiesSet() throws Exception { Group group = identityService.newGroup("user"); group.setName("users"); group.setType("security-role"); identityService.saveGroup(group); User admin = identityService.newUser("admin"); admin.setPassword("admin"); identityService.saveUser(admin); } }; } Start the application. We can now start a process instance as we did in the CommandLineRunner, but now using REST: curl -u admin:admin -H "Content-Type: application/json" -d '{"processDefinitionKey":"hireProcess", "variables": [ {"name":"applicantName", "value":"John Doe"}, {"name":"email", "value":"
[email protected]"}, {"name":"phoneNumber", "value":"1234567"} ]}' http://localhost:8080/runtime/process-instances Which returns us the json representation of the process instance: { "tenantId": "", "url": "http://localhost:8080/runtime/process-instances/5", "activityId": "sid-42BAE58A-8FFB-4B02-AAED-E0D8EA5A7E39", "id": "5", "processDefinitionUrl": "http://localhost:8080/repository/process-definitions/hireProcess:1:4", "suspended": false, "completed": false, "ended": false, "businessKey": null, "variables": [], "processDefinitionId": "hireProcess:1:4" } I just want to stand still for a moment how cool this is. Just by adding one dependency, you’re getting the whole Activiti REST API embedded in your application! Let’s make it even cooler, and add following dependency org.activiti spring-boot-starter-actuator ${activiti.version} This adds a Spring Boot actuator endpoint for Activiti. If we restart the application, and hithttp://localhost:8080/activiti/, we get some basic stats about our processes. With some imagination that in a live system you’ve got many more process definitions deployed and executing, you can see how this is useful. The same actuator is also registered as a JMX bean exposing similar information. { completedTaskCountToday: 0, deployedProcessDefinitions: [ "hireProcess (v1)" ], processDefinitionCount: 1, cachedProcessDefinitionCount: 1, runningProcessInstanceCount: { hireProcess (v1): 0 }, completedTaskCount: 0, completedActivities: 0, completedProcessInstanceCount: { hireProcess (v1): 0 }, openTaskCount: 0 } To finish our coding, let’s create a dedicated REST endpoint for our hire process, that could be consumed by for example a javascript web application (out of scope for this article). So most likely, we’ll have a form for the applicant to fill in the details we’ve been passing programmatically above. And while we’re at it, let’s store the applicant information as a JPA entity. In that case, the data won’t be stored in Activiti anymore, but in a separate table and referenced by Activiti when needed. You probably guessed it by now, JPA support is enabled by adding a dependency: org.activiti spring-boot-starter-jpa ${activiti.version} and add the entity to the MyApp class: @Entity class Applicant { @Id @GeneratedValue private Long id; private String name; private String email; private String phoneNumber; // Getters and setters We’ll also need a Repository for this Entity (put this in a separate file or also in MyApp). No need for any methods, the Repository magic from Spring will generate the methods we need for us. public interface ApplicantRepository extends JpaRepository { // .. } And now we can create the dedicated REST endpoint: @RestController public class MyRestController { @Autowired private RuntimeService runtimeService; @Autowired private ApplicantRepository applicantRepository; @RequestMapping(value="/start-hire-process", method= RequestMethod.POST, produces= MediaType.APPLICATION_JSON_VALUE) public void startHireProcess(@RequestBody Map data) { Applicant applicant = new Applicant(data.get("name"), data.get("email"), data.get("phoneNumber")); applicantRepository.save(applicant); Map variables = new HashMap(); variables.put("applicant", applicant); runtimeService.startProcessInstanceByKey("hireProcessWithJpa", variables); } } Note we’re now using a slightly different process called ‘hireProcessWithJpa’, which has a few tweaks in it to cope with the fact the data is now in a JPA entity. So for example, we can’t use ${applicantName} anymore, but we now have to use ${applicant.name}. Let’s restart the application and start a new process instance: curl -u admin:admin -H "Content-Type: application/json" -d '{"name":"John Doe", "email": "
[email protected]", "phoneNumber":"123456789"}' http://localhost:8080/start-hire-process We can now go through our process. You could create a custom endpoints for this too, exposing different task queries with different forms … but I’ll leave this to your imagination and use the default Activiti REST end points to walk through the process. Let’s see which task the process instance currently is at (you could pass in more detailed parameters here, for example the ‘processInstanceId’ for better filtering): curl -u admin:admin -H "Content-Type: application/json" http://localhost:8080/runtime/tasks which returns { "order": "asc", "size": 1, "sort": "id", "total": 1, "data": [{ "id": "14", "processInstanceId": "8", "createTime": "2015-02-16T13:11:26.078+01:00", "description": "Conduct a telephone interview with John Doe. Phone number = 123456789", "name": "Telephone interview" ... }], "start": 0 } So, our process is now at the Telephone interview. In a realistic application, there would be a task list and a form that could be filled in to complete this task. Let’s complete this task (we have to set the telephoneInterviewOutcome variable as the exclusive gateway uses it to route the execution): curl -u admin:admin -H "Content-Type: application/json" -d '{"action" : "complete", "variables": [ {"name":"telephoneInterviewOutcome", "value":true} ]}' http://localhost:8080/runtime/tasks/14 When we get the tasks again now, the process instance will have moved on to the two tasks in parallel in the subprocess (big rectangle): { "order": "asc", "size": 2, "sort": "id", "total": 2, "data": [ { ... "name": "Tech interview" }, { ... "name": "Financial negotiation" } ], "start": 0 } We can now continue the rest of the process in a similar fashion, but I’ll leave that to you to play around with. Testing One of the strengths of using Activiti for creating business processes is that everything is simply Java. As a consequence, processes can be tested as regular Java code with unit tests. Spring Boot makes writing such test a breeze. Here’s how the unit test for the “happy path” looks like (while omitting @Autowired fields and test e-mail server setup). The code also shows the use of the Activiti API’s for querying tasks for a given group and process instance. @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = {MyApp.class}) @WebAppConfiguration @IntegrationTest public class HireProcessTest { @Test public void testHappyPath() { // Create test applicant Applicant applicant = new Applicant("John Doe", "
[email protected]", "12344"); applicantRepository.save(applicant); // Start process instance Map variables = new HashMap(); variables.put("applicant", applicant); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("hireProcessWithJpa", variables); // First, the 'phone interview' should be active Task task = taskService.createTaskQuery() .processInstanceId(processInstance.getId()) .taskCandidateGroup("dev-managers") .singleResult(); Assert.assertEquals("Telephone interview", task.getName()); // Completing the phone interview with success should trigger two new tasks Map taskVariables = new HashMap(); taskVariables.put("telephoneInterviewOutcome", true); taskService.complete(task.getId(), taskVariables); List tasks = taskService.createTaskQuery() .processInstanceId(processInstance.getId()) .orderByTaskName().asc() .list(); Assert.assertEquals(2, tasks.size()); Assert.assertEquals("Financial negotiation", tasks.get(0).getName()); Assert.assertEquals("Tech interview", tasks.get(1).getName()); // Completing both should wrap up the subprocess, send out the 'welcome mail' and end the process instance taskVariables = new HashMap(); taskVariables.put("techOk", true); taskService.complete(tasks.get(0).getId(), taskVariables); taskVariables = new HashMap(); taskVariables.put("financialOk", true); taskService.complete(tasks.get(1).getId(), taskVariables); // Verify email Assert.assertEquals(1, wiser.getMessages().size()); // Verify process completed Assert.assertEquals(1, historyService.createHistoricProcessInstanceQuery().finished().count()); } Next steps We haven’t touched any of the tooling around Activiti. There is a bunch more than just the engine, like the Eclipse plugin to design processes, a free web editor in the cloud (also included in the .zip download you can get from Activiti's site, a web application that showcases many of the features of the engine, … The current release of Activiti (version 5.17.0) has integration with Spring Boot 1.1.6. However, the current master version is compatible with 1.2.1. Using Spring Boot 1.2.0 brings us sweet stuff like support for XA transactions with JTA. This means you can hook up your processes easily with JMS, JPA and Activiti logic all in the same transaction! ..Which brings us to the next point … In this example, we’ve focussed heavily on human interactions (and barely touched it). But there’s many things you can do around orchestrating systems too. The Spring Boot integration also has Spring Integration support you could leverage to do just that in a very neat way! And of course there is much much more about the BPMN 2.0 standard. Read more about itin the Activiti docs.
Comments
Jan 12, 2018 · Arran Glen
Hi folks, this ACM Queue paper is about $15.00 USD and well worth the read IMO.
https://dl.acm.org/citation.cfm?id=3093908
Sep 20, 2017 · Tim Spann
I think you'll have better luck on StackOverflow with this :)
Sep 06, 2017 · Mohit Sinha
my old friends at ORCL claim they are working on a reactive JDBC driver... FYI. Spring Data will have reactive drivers for Mongo, Cassandra, Redis at GA on Sept 21.
Sep 06, 2017 · Thomas Martin
When Spring Boot 2.0 comes out this winter, you won't have to choose - JAX-RS will be supported natively
Aug 03, 2017 · Serhii Povisenko
Gaspar below is correct. this whole article has less to do with docker than it does about understanding how Java memory regions work. Another reason to use cloud foundry buildpacks to create containers, as the memory calculator helps a lot here
Jul 25, 2017 · Serhii Povisenko
also, see https://github.com/cloudfoundry/jvmkill
Jul 25, 2017 · Serhii Povisenko
YourKit is the way to go on analyzing memory usage. Also, try using a Cloud Foundry Java buildpack to automate container construction with intelligent, automatic initial memory settings! read more about the memory calculator here:
https://www.cloudfoundry.org/just-released-java-buildpack-4-0/
Jul 14, 2017 · Tim Spann
that's funny! good one
May 09, 2017 · Arran Glen
that's a great point. I have a few other corrections I'm going to make, I'm getting more feedback from Kafka users. I'll add that to the list. TY!
May 02, 2017 · Tim Spann
Looking for more detail about how spring boot really works?
Check out the short version:
https://www.youtube.com/watch?v=u1QnlAbCFys
the medium length version:
https://content.pivotal.io/webinars/spring-boot-under-the-hood
the long in depth version:
https://www.youtube.com/watch?v=uof5h-j0IeE
Apr 14, 2017 · Piotr Mińkowski
Try Spring Cloud Sleuth on cloud foundry it removes a lot of the pain here check out http://run.pivotal.io/spring
Dec 27, 2016 · Duncan Brown
this article is comparing apples and oranges - the entire premise is strange. Spring Cloud is a development stack. Kubernetes is a cloud platform. You even sort of acknlowedge this in the article when you say "The two platforms, Spring Cloud and Kubernetes, are very different and there is no direct feature parity between them". Your mistake is calling Spring Cloud a platform, it is not. If you wanted an meaningful comparison, I would compare running Spring Cloud on Cloud Foundry vs. Spring Cloud on Kubernetes.
Oct 28, 2016 · Dave Fecak
I think many would agree that Spring is 1st and foremost an integration framework - and puts effort into curating & testing a huge subset of those 1000s of options you mention. I don't think that Java EE tests with anywhere near the same amount of the Java landscape outside Java EE, it's a much smaller box.
http://docs.spring.io/platform/docs/current/reference/htmlsingle/#appendix-dependency-versions
Oct 28, 2016 · Dave Fecak
Some companies are stuck on last decade's tech, and there are jobs to be had sure, similar to mainframe market dynamics. But that runway is even more finite now - priority for digital business is increasing, and the pace of innovation has increased dramatically with cloud. Java EE's glacial pace of update will force those companies to abandon those stacks for cloud native ones faster than ever before, typically at least 3 years from finialization of a EE spec to arrival of supported-in-production commerical servers from the few remaining vendors. And that will only be to deliver what a small part of what Spring has already been shipping since 2015.
http://www.theregister.co.uk/2016/09/20/java_ee_8_delayed_new_projects_focus/
The good news is that much of what you learn about JPA, JAX-RS, JMX, Servlets are highly transferrable and are worth learning. The rest has become fairly irrelevant to modern Java development.
Oct 28, 2016 · Dave Fecak
I suggest that the Java EE commenters on this thread read these articles, and then comment, so they aren't publicly displaying their ignorance / bias and inviting non-productive discourse from both sides.
https://spring.io/blog/2015/11/29/how-not-to-hate-spring-in-2016
And this one from 2015 has much that is still relevant: https://dzone.com/articles/java-doesnt-suck-rockin-jvm
http://www.theregister.co.uk/2016/09/20/java_ee_8_delayed_new_projects_focus/
Sep 30, 2016 · Benjamin Ball
Yes, it sounds like some misconfigurations are present with your RabbitMQ instance used in this benchmark. To get the best possible performance from Rabbit, you do have to do some homework, both in config and in app/client code. A propertly configured Rabbit can run 1M/messages second on GCE:
https://blog.pivotal.io/pivotal/products/rabbitmq-hits-one-million-messages-per-second-on-google-compute-engine
https://cloudplatform.googleblog.com/2014/06/rabbitmq-on-google-compute-engine.html