Exposing Activiti BPMN Engine events via websockets extending its REST application

The latest release of Activiti (5.15) introduced a new interesting mechanism – support for event handlers. You can now get notified just when something interesting happens inside process engine. (You can read more on events in Activiti HERE).

I instantly thought that would be even more interesting if external applications integrated with Activiti REST could also get easily notified about the process events. Using websockets as a medium for broadcasting these messages seemed quite natural.
In this article I will present step by step how I extended the default REST application to serve events via websockets. I also developed a client test consuming these events to prove the thing is working, it will also be described later on.

Prerequisites

You can git clone or download zip package with sources of this project here:

https://github.com/wrobelm/activiti-websockets

To run it just go to activiti-websockets-server directory and execute

mvn jetty:run

This will build the server application and run it in maven embedded jetty server.

Then to run automated client test go to activiti-websockets-client directory and execute

mvn test

You will need Maven running on JDK 8 to run the test project.

activiti-websockets server project

Broad view on main concepts :

  • The project is based on original Activiti REST application. It is included in the project as maven dependency and extended where needed.
  • The websockets server has been developed using standard Java EE7 web-api libraries. They are compatible with modern webapp containers like Jetty 9 and Tomcat 8.

Let’s go into some more details:

1. How websocket server is hooked to the Activiti REST webapp?

The standard web.xml file has been altered to use a custom ServletContextListener :

...
 <listener>
 <listener-class>pl.mwrobel.activiti.extensions.CustomActivitiServletContextListener</listener-class>
 </listener>
...

This listener extends default ActivitiServletContextListener.

Look at its source code now:

public class CustomActivitiServletContextListener extends ActivitiServletContextListener {
private static final Logger log = LoggerFactory.getLogger(CustomActivitiServletContextListener.class);
@Override
 public void contextInitialized(ServletContextEvent event) {
 super.contextInitialized(event);
 event.getServletContext().setAttribute("activiti-engine", ProcessEngines.getDefaultProcessEngine());
 addProcessEventsEndpoint(event);
 }
public void addProcessEventsEndpoint(ServletContextEvent ce) {
 log.info("Deploying process-engine-events websockets server endpoint");
 ServletContext sc = ce.getServletContext();
final ServerContainer server_container
 = (ServerContainer) ce.getServletContext().getAttribute("javax.websocket.server.ServerContainer");
try {
 ServerEndpointConfig config
 = ServerEndpointConfig.Builder.create(ProcessEngineEventsWebsocket.class,
 "/process-engine-events").build();
 config.getUserProperties().put("servlet.context", sc);
 server_container.addEndpoint(config);
 } catch (DeploymentException e) {
 throw new RuntimeException(e);
 }
 }
}

Two most important lines:

ServerEndpointConfig config
 = ServerEndpointConfig.Builder.create(ProcessEngineEventsWebsocket.class,
 "/process-engine-events").build();

New endpoint will be deployed to path “/process-engine-events“, it is implemented by websocket server class ProcessEngineEventsWebsocket.

event.getServletContext().setAttribute("activiti-engine", ProcessEngines.getDefaultProcessEngine());

Process engine reference is stored in ‘activiti-engine‘ servlet context attribute, it allows to be later acquired in ProcessEngineEventsWebsocket class.

2. Websocket server endpoint

public class ProcessEngineEventsWebsocket extends Endpoint {
private static final Logger log = LoggerFactory.getLogger(ProcessEngineEventsWebsocket.class);
private ServletContext servletContext;
 private ProcessEngine processEngine;
@Override
 public void onOpen(final Session session, EndpointConfig config) {
 log.info("Websockets connection opened");
this.servletContext = (ServletContext) config.getUserProperties().get("servlet.context");
 processEngine = (ProcessEngine) servletContext.getAttribute("activiti-engine");
 processEngine.getRuntimeService().addEventListener(new ActivitiProcessEventsWebsocketBroadcaster(session));
 }
}
processEngine = (ProcessEngine) servletContext.getAttribute("activiti-engine");

When a new websockets session is opened, processEngine is retrieved from the servlet context attribute stored in servlet context listener.

processEngine.getRuntimeService().addEventListener(new ActivitiProcessEventsWebsocketBroadcaster(session));

Registering ActivitiProcessEventsWebsocketBroadcaster as an event listener for all events published by the Activiti engine. Websockets session object is passed to the event handler to allow sending messages to the client.

3. ActivitiProcessEventsWebsocketBroadcaster – Activiti events handler

This class implements ActivitiEventListener interface. Every time new event occurs this method gets called:

@Override
 public void onEvent(ActivitiEvent event) {
 switch (event.getType()) {
 case ACTIVITY_STARTED: {
 broadcastEvent((ActivitiActivityEvent)event);
 break;
 }
 case ACTIVITY_COMPLETED: {
 broadcastEvent((ActivitiActivityEvent)event);
 break;
 }
 }
 }

I decided to publish two selected types of Activiti events. Every time they occur they are broadcasted to the client. Quite self-explanatory.

private void broadcastEvent(ActivitiActivityEvent e) {
 ProcessEventDTO dto = ProcessEventDTO.builder().activityId(e.getActivityId())
 .activityName(e.getActivityId())
 .activityType(e.getType().toString())
 .processId(e.getProcessInstanceId())
 .build();
 log.info("Activiti event received: " + e.getType());
 RemoteEndpoint.Basic remoteEndpoint = session.getBasicRemote();
 try {
 remoteEndpoint.sendText(om.writeValueAsString(dto));
 } catch (IOException ex) {
 throw new RuntimeException(ex);
 }
 }

Here the event properties are wrapped in transport class object (ProcessEventDTO), then serialized to JSON using Jackson ObjectMapper, and send to the client as string. The client endpoint is retrieved from the session object injected in ProcessEngineEventsWebsocket during session opening.

And that’s basically all about publishing Activiti events via websockets.

Example process and websockets client as test case

To demonstrate it working I developed a simple BPMN process which gets auto deployed during start of the REST webapp.
It is done in DemoDataGenerator class substituting the original class from REST webapp. The file ‘event-demo-process.bpmn20.xml‘ is deployed in method initDemoProcessDefinitions(). You can find this process in graphic notation below:

events-process

Notes:
- first-task and second-task just print some info to server log
- timer-catch waits 8 seconds, then process is resumed again

Let’s move to activiti-websockets-client project

I developed a client going automatically through this process in the form of unit test.

The main test method goes as follows:

@Test
 public void processShouldComplete() throws InterruptedException, Exception {
 final ProcessEventsWebSocket ws = con.getProcessEventsWebsocket();
String processInstanceId = createProcessInstance();
ws.addExpectedEventAndWait(15000, ProcessEventDTO.builder()
 .activityName("user-task").processId(processInstanceId).activityType("ACTIVITY_STARTED")
 .build());
completeUserTask(processInstanceId);
 checkIfProcessHasFinished(processInstanceId);
 }

Notes:
- A new process instance is created using Activiti REST services. You can read documentation of these services HERE. I used Jersey client for dealing with HTTP requests and again Jackson for deserialization of JSON objects representing server events.
- In class ProcessEventsWebSocket using eclipse jetty websocket client library and CountDownLatch (you can read more about it HERE) I implemented simple mechanisms to wait for an event with given field values set. For demo purposes every event incoming in onMessage() method is printed to standard output.
- In this particular test we wait for ACTIVITY_STARTED event of ‘user-task‘, then we can push process forward in completeUserTask() method, finally we check if process has finished. It is also achieved using Activiti REST API mentioned before.

The output of the test should be like:

Running pl.mwrobel.activiti.websockets.test.ActivitiWebsocketsIntegrationTest
 2014-04-28 12:54:52.081:INFO::main: Logging initialized @863ms

http://127.0.0.1:9080/service/runtime/process-instances

 {"processId":"287","activityId":"start-event","activityName":"start-event","activityType":"ACTIVITY_STARTED"}
 {"processId":"287","activityId":"start-event","activityName":"start-event","activityType":"ACTIVITY_COMPLETED"}
 {"processId":"287","activityId":"first-task","activityName":"first-task","activityType":"ACTIVITY_STARTED"}
 {"processId":"287","activityId":"first-task","activityName":"first-task","activityType":"ACTIVITY_COMPLETED"}
 {"processId":"287","activityId":"timer-catch","activityName":"timer-catch","activityType":"ACTIVITY_STARTED"}
 {"processId":"287","activityId":"timer-catch","activityName":"timer-catch","activityType":"ACTIVITY_COMPLETED"}
 {"processId":"287","activityId":"second-task","activityName":"second-task","activityType":"ACTIVITY_STARTED"}
 {"processId":"287","activityId":"second-task","activityName":"second-task","activityType":"ACTIVITY_COMPLETED"}
 {"processId":"287","activityId":"user-task","activityName":"user-task","activityType":"ACTIVITY_STARTED"}
 MATCHED ****ProcessEventDTO(processId=287, activityId=null, activityName=user-task, activityType=ACTIVITY_STARTED, customActivityType=null)
 {"processId":"287","activityId":"user-task","activityName":"user-task","activityType":"ACTIVITY_COMPLETED"}
 {"processId":"287","activityId":"end-event","activityName":"end-event","activityType":"ACTIVITY_STARTED"}
 {"processId":"287","activityId":"end-event","activityName":"end-event","activityType":"ACTIVITY_COMPLETED"}
 closed connection
 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 9.358 sec

And that is all on Proof of Concept integration of Activiti with websockets server. Hope it helped someone ;)

Postscript:

My thread on Activiti forums about somewhat more complicated example involving signals and problems regarding transactions and events in Activiti:
http://forums.activiti.org/content/possible-bug-eventing-activiti-not-ready-receive-signal-even-after-dispatching

So – in some cases this concept may still need some refining ;)

Resources :

Article on Java EE7 Websockets:
http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html
CountDownLatch:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html
Detailed Activiti user guide:
http://www.activiti.org/userguide/
Jackson JSON Processor
http://jackson.codehaus.org/

How to draw semicircle on Android Canvas?

I just resolved a problem which at first I thought is a matter of 5 min for finding appropriate Android API function, unfortunately it took me much longer..

Problem:
Given start and end points of a vector, draw left/right hand semicircle knowing, that this vector is diameter of this semicircle.

Solution:

Use addArc android function.

public void addArc (RectF oval, float startAngle, float sweepAngle)
http://developer.android.com/reference/android/graphics/Path.html#addArc(android.graphics.RectF, float, float)

As you can see we need an oval rect and startAngle, which are not obvious..
Hereunder you can find a handy method computing needed parameters.

/**
     *
     * @param xStart vector start point
     * @param yStart
     * @param xEnd vector end point
     * @param yEnd
     * @param ovalRectOUT RectF to store result
     * @param enum direction left/right
     * @return start angle
     */
    public static float getSemicircle(float xStart, float yStart, float xEnd,
            float yEnd, RectF ovalRectOUT, Side direction) {

        float centerX = xStart + ((xEnd - xStart) / 2);
        float centerY = yStart + ((yEnd - yStart) / 2);

        double xLen = (xEnd - xStart);
        double yLen = (yEnd - yStart);
        float radius = (float) (Math.sqrt(xLen * xLen + yLen * yLen) / 2);
    
        RectF oval = new RectF((float) (centerX - radius),
                (float) (centerY - radius), (float) (centerX + radius),
                (float) (centerY + radius));

        ovalRectOUT.set(oval);

        double radStartAngle = 0;
        if (direction == Side.LEFT) {
            radStartAngle = Math.atan2(yStart - centerY, xStart - centerX);
        } else {
            radStartAngle = Math.atan2(yEnd - centerY, xEnd - centerX);
        }
        float startAngle = (float) Math.toDegrees(radStartAngle);

        return startAngle;

    }

After that drawing semicircles is deadsimple with eg.

path.addArc(oval, startAngle, 180);

Hope that helps! ;)

If you need to manipulate circle pieces a bit more specifically I really recommend you this article:
http://www.tbray.org/ongoing/When/200x/2009/01/02/Android-Draw-a-Curved-Line

How to configure Eclipse to debug Alfresco java webscript code

How to configure Eclipse to debug Alfresco java webscript code

NOTE: It is assumed, that Eclipse environment has been configured to work with Alfresco SDK. If it’s not true the configuration process has been described here: http://wiki.alfresco.com/wiki/Alfresco_SDK_3.4#Set_Eclipse_Compiler_Compliance_Level_to_6.0.

1. Select: Run menu -> Debug configurations…
2. Create new configuration for ‘Remote Java Application’
3. In ‘Project’ field select ‘home’ project (presumably your webscript project), which you want to debug (but it is also possible to debug code from Alfresco core outside selected project)
4. In ‘Connection properties’ set Tomcat server IP address and debug port you defined earlier.

NOTE: It’s advised against to use ‘localhost’ instead of IP address on Windows because of hosts file issues.

5. Click Apply, Debug
6. If everything went correct you shouldn’t get any message.
7. Switch to debug perspective
8. You should see something like this:

Proper screen from eclipse debug perspective

Proper screen from eclipse debug perspective


In case of debug session disconnection (eg. Tomcat restart) you may reconnect to it using Relaunch option:
Reconnecting to debugger

Reconnecting to debugger


9. Now you can normally set breakpoints and trace code execution of your webscript or other Alfresco SDK projects. A good test is setting a breakpoint in org.alfresco.web.bean.LoginBean at line
“FacesContext context = FacesContext.getCurrentInstance();” – breakpoint should be caught at Alfresco Explorer login attemp.

NOTE: You may get following error message: “Unable to install breakpoint in org.evolpe.webscripts.XMLwebs due to missing line number attributes. Modify compiler options to generate line number attributes.
Reason:
Absent Line Number Information”

Missing line info window

Missing line info window

CAUSE&SOLUTION: To debug your own webscripts or other code it’s essential to enable code line number information in the compiler. If you use ant, set debug=”true’ parameter in javac task.

How to configure tomcat to debug instance of Alfresco on Windows and Linux will be described soon in my next articles.;)

How to perform form field validation in Alfresco Share?

It’s possible to implement your own field validation function in Alfresco Share in JS by overwriting Share mandatory constraint which uses
Alfresco.forms.validation.mandatory located in /opt/alfresco-3.4.d/tomcat/webapps/share/js/forms-runtime.js
The default configuration can be suppressed the way described here:http://wiki.alfresco.com/wiki/Forms#constraint-handlers
validation-handler parameter is the name of your own JS function.
Here you can find description of validation function parameters http://wiki.alfresco.com/wiki/Forms_Developer_Guide#Validation_Handlers

In the current version of Alfresco (3.4d) the validation ‘fails silently’ which means you can’t dynamically display error message. When validation fails it’s not possible to submit the form. There are foundations for enhancement of this mechanism so it’s quite possible the ‘full’ validation will be available in near future.
You can

download full eclipse ant project for my example here

. I will provide
instructions for manual installation as well.
Ant installation
1. Set paths to tomcat directory in build.properties
TOMCAT_HOME=/opt/alfresco-3.4.d/tomcatAPP_TOMCAT_HOME=/opt/alfresco-3.4.d/tomcat
2. Just run ‘ant’ in project directory.

So, let’s start the core of this tutorial.
1. I implemented my own simple data model for this example. As it is not complicated, nor directly connected with validation its self I just attach needed files with my project.If you’re not familiar with defining data models you can read about it for instance, here
tutorial:http://www.tribloom.com/blogs/michael/2011/04/18/alfresco-repository-webscript/

detailed documentation: http://wiki.alfresco.com/wiki/Data_Dictionary_Guide#The_Data_Dictionary
validationModel.xml goes to /opt/alfresco-3.4.d/tomcat/webapps/alfresco/WEB-INF/classes/alfresco/extension/model
validation-model-context.xml goes to /opt/alfresco-3.4.d/tomcat/webapps/alfresco/WEB-INF/classes/alfresco/extension/

2. share-config-validation-custom.xml goes to /opt/alfresco-3.4.d/tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension
This file contains definition of create and edit forms for the new data type.
Piece of code concerning validation:


<field id="ex:gross" label-id="example.price.gross" mandatory="true"
	help-id="example.price.gross.help">
	<constraint-handlers>
		<!-- validation-handler param: js function name -->
		<constraint type="MANDATORY"
			validation-handler="Alfresco.forms.validation.exampleGrossValidation"
			event="keyup" />
	</constraint-handlers>
</field>

 

Alfresco.forms.validation.exampleGrossValidation is the name of js validation
function.
3. exampleFormValidation.js and exampleFormValidation-min.js go to/opt/alfresco-3.4.d/tomcat/webapps/share/components/form

Alfresco.forms.validation.exampleGrossValidation = function exampleGrossValidation(field, args,
  event, form, silent, message) {
 

  var valid = true;

  valid = YAHOO.lang.trim(field.value).length !== 0;  // check if field is not empty

// referencing to form fields is possible by following convention:
// field object passed to function representing validated field has an object form representing the whole form  
// field object names are created by concating 'prop_'+{namespace from data model}_+{property name from data model}  
  var net = field.form.prop_ex_net.value;
  var gross = field.form.prop_ex_gross.value;
  var rate = field.form.prop_ex_rate.value;

  var check = ((rate/100) + 1) * net;

  if((Math.abs(gross - check)) > 0.01)  // check if gross has a valid value with given precision
{
valid = false;
}

  return valid;
};

 
4. exampleValidation_pl_PL.properties (labels for forms) goes to/opt/alfresco-3.4.d/tomcat/webapps/share/WEB-INF/classes/alfresco/messages


You have to modify following files:


1. slingshot-application-context.xml at /opt/alfresco-3.4.d/tomcat/webapps/share/WEB-INF/classes/alfresco/
Paths to share-config-validation-custom.xml and exampleValidation_pl_PL.properties go there
At xPath ‘/beans/bean/constructor-arg/list’ add:

<value>classpath:alfresco/web-extension/share-config-validation-custom.xml</value>
At xPath ‘/beans/bean/property/list’ add:

<value>alfresco.messages.exampleValidation</value>

2. form.get.head.ftl – link to .js fileat /opt/alfresco-3.4.d/tomcat/webapps/share/WEB-INF/classes/alfresco/site-webscripts/org/alfresco/components/form
Add: <@script type=”text/javascript” src=”${page.url.context}/res/components/form/exampleFormValidation.js”></@script>

3. toolbar.get.config.xml - In this file we can add a new option in menu ‘Create content’ in Alfresco Share Site.
at /opt/alfresco-3.4.d/tomcat/webapps/share/WEB-INF/classes/alfresco/site-webscripts/org/alfresco/components/documentlibrary/
add :
<content mimetype=”text/xml” icon=”xml” label=”example.price.menu” itemid=”ex:price”/>

Alfresco document preview doesn’t work for MS Office files (.doc, .docx, .ppt .pptx etc) although it works for pdf files. I get “This document can’t be previewed.” message.

For reasons which are not obvious for me the installation and starting of alfrescoOpenOffice service doesn’t
always work properly during automated Alfresco install/start script execution. If you have the same problem
here is the solution for windows environment.
1. Start – > run  - > services.msc Check if alfrescoOpenOffice service is listed. If not go to {alfresco install dir}\openoffice\scriptsand execute “openoffice_serviceinstall INSTALL” with administrative privileges.

2. Every time after starting alfresco by {alfresco install dir}\servicerun START execute also{alfresco install dir}\openoffice\scripts\openoffice_servicerun START.

3. If you’re not happy with auto starting openoffice service with Windows change alfrescoOpenOffice execution
type to “Manual”.

4. MS Office documents preview in Alfresco should be working just fine now :)

I get “Unable to locate the Javac Compiler” when trying to build using IAM Maven Eclipse plugin.

Problem:
When trying to build maven project using IAM under Eclipse I get an error like this:


"Unable to locate the Javac Compiler in:C:\\Program
Files\\Java\\jre6\..\\lib\\tools.jar
Please ensure you are using JDK 1.4 or above and
not a JRE (the com.sun.tools.javac.Main class is required).
In most cases you can change the location of your Java
installation by setting the JAVA_HOME environment variable"

,nevertheless I have proper installation of newest Java JDK and JAVA_HOME is properly set.
The problem never occured before.

Solution:

I know two possible causes of that problem:

1. Eclipse uses its own JRE to start, and IAM does the same. You’ve recently installed a new maven plugin and it requires JDK.
You can force Eclipse to start using directly given Java VM by starting it that way from command line:

“X:\\path\to\\eclipse\\eclipse.exe -vm JAVA_JDK_PATH”

2. There also cases when IAM brokes up without apparent cause, and that happend to me.
The reason may be IAM itself.
Try standard tricks checklist from my another post here:
Problems using Eclipse IAM and Maven

I get “The processing instruction target matching “[xX][mM][lL]” is not allowed.” error during start of Alfresco.

If you just defined a new content type in Alfresco the cause may be empty characters at the beginning of the model context file, nevertheless it’s grammatically correct the parser can’t get through.

Eg. it may result in a stack trace similiar to:


org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem:
Failed to import bean definitions from URL location [classpath:alfresco/application-context.xml]
Offending resource: ServletContext resource [/WEB-INF/web-application-context.xml];
nested exception is org.springframework.beans.factory.parsing.BeanDefinitionParsingException:
 Configuration problem: Failed to import bean definitions from URL location
 [classpath*:alfresco/extension/*-context.xml]Offending resource: class path resource
[alfresco/application-context.xml]; nested exception is
org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:
Line 1 in XML document from file [D:\\xxxxxx-model-context.xml] is invalid;
nested exception is org.xml.sax.SAXParseException:
The processing instruction target matching "[xX][mM][lL]" is not allowed.
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68)
....

Also watch out for the empty characters at the end of file paths in configuration xmls cause problems, alfresco can’t find these files.

Tomcat uses wrong path although CATALINA_HOME and CATALINA_BASE have correct values.

The cause of the problem may be that your web application uses tomcat6.exe instead of catalina.bat to start up.  From my observations it seems tomcat6.exe as opposed to catalina.bat uses environment variables only for the 1st run to copy CATALINA_HOME and CATALINA_BASE values to Windows Registry. Thereafter these parameters start to live on their own;) I had one instance of Tomcat for my Openbravo developer stack and the other for Alfresco. Tomcat6.exe used old values regardless of completly separate instalation of Alfresco dev stack.
You can edit these and other variables directly via Windows Registry editor, the path is:

HKLM/SOFTWARE/Apache Software Foundation/…..

or more conveniently you can use tomcat6w.exe GUI:

tomcat6w //ES//<serviceName> (eg. tomcat6w //ES//alfrescoTomcat)

calls a neat window where you can edit variables you need to run your web app properly.

there’s also //MS// parameter which will call a simple application monitoring given service:

tomcat6w //MS//<serviceName>

If you want to read more about running Tomcat as Windows service with tomcat6.exe go to an article from Apache Tomcat documentation.

Hope this helps someone:)

Alfresco repository full text search example using CMIS webservices

It’s easily achieavable to perform a full text search on common format documents (like .doc, .pdf) stored in Alfresco repository. What’s more it’s also possible to get relevance of documents found against desired keywords.

A short example:

POST <host>/alfresco/service/cmis/queries
Content-Type:application/cmisquery+xml
Http basic auth required!
<cmis:query xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/">
1.	<cmis:statement>SELECT cmis:name, SCORE() FROM cmis:document WHERE CONTAINS('niceWord')</cmis:statement>
	<cmis:searchAllVersions>true</cmis:searchAllVersions>
        <cmis:includeAllowableActions>false</cmis:includeAllowableActions>
        <cmis:includeRelationships>none</cmis:includeRelationships>
        <cmis:renditionFilter>*</cmis:renditionFilter>
2.     	<cmis:maxItems>50</cmis:maxItems>
3.     	<cmis:skipCount>0</cmis:skipCount>
</cmis:query>

A few important details:

1. SCORE() returns relevance of document found against expression passed to CONTAINS() function. In Alfresco you can use powerful FTS language as CONTAINS() parameter but it means losing compliance with CMIS-SQL standard.

2. , 3. Paging effect. It’s recommend to always use paging because Alfresco doesn’t guarantee that it will return all entries if there is a lot of them.

And that’s all, you will receive a neat list of entries meeting your requirements.

How to upload a new file to Alfresco using web services?

There are some examples how to upload a file to alfresco using UploadContentServlet but they all show how to code it directly in Java. Documentation and forum lacks an example showing how to upload a large file using Alfresco web services. After a bit of struggle I figured it out.

Quick and dirty recipe:

  1. Perform ticket authorization
POST /alfresco/service/api/login
{
"username" : "admin",
"password" : "admin"
}

You’ll get response with ticket in it.

  1. Create a new document using CMIS services
POST /alfresco/service/cmis/i/176c5f4d-db63-49ec-9886-c19d6d9eefce/children
Content-Type:application/atom+xml;type=entry

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/"
xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/">
<title>important document</title>
<summary>VERY important document</summary>
<content type="text/plain">
MS4gR2l0YSAKIDIuIEthcm1heW9nYSBieSBWaXZla2FuYW5k
</content>
<cmisra:object>
<cmis:properties>
<cmis:propertyId propertyDefinitionId="cmis:objectTypeId"><cmis:value>cmis:document</cmis:value></cmis:propertyId>
</cmis:properties>
</cmisra:object>
</entry>

/i/176c5f4d-db63-49ec-9886-c19d6d9eefce denotes folder to create document within.

It is also possible to refer to directories in more natural way – using names.

More info provided here:  http://wiki.alfresco.com/wiki/CMIS_Web_Scripts_Reference#Create_.2F_Move_a_Folder_or_Document_.28createDocument.2C_createFolder.2C_createPolicy.2C_moveObject.29You will get quite big chunk of xml with a lot info about newly uploaded document, among these informations you’ll find ID of the document.

3.  Overwrite the new document with your big file

PUT /alfresco/upload/workspace/SpacesStore/85c43689-4a38-4a0a-8e58-e24333ffec14/test.pdf?ticket=TICKET_fc7af4c45f138ad366dd5905aaf7a4ab8b9da268

Send file as body of request.

More info about UploadContentServlet : http://wiki.alfresco.com/wiki/URL_Addressability#UploadContentServlet

And that’s all, your file is ready in Alfresco repository!

Page 1 of 212