IngresTutorial Alfresco Web Service API for Java

cancel
Showing results for 
Search instead for 
Did you mean: 

IngresTutorial Alfresco Web Service API for Java

resplin
Intermediate
0 0 3,266

Obsolete Pages{{Obsolete}}

The official documentation is at: http://docs.alfresco.com



Ingres


Table of Contents


Preamble


During the development of the Mediaclient demo application I figured out that Alfresco's Web Service API for Java could need a tutorial. So this tutorial aims to extend the following existing documentation:




About the Mediaclient demo application: The demo application is JBoss Seam based and was developed by using the 'Ingres Development Stack for JBoss'. The purpose of the application is to show how to develop a JBoss Seam based application which uses Alfresco as the file store. A second tutorial with further details will follow soon. The application itself can be used to manage MP3 files by storing them inside Alfresco.

About the Ingres Development Stack for JBoss: It contains the following components:


  • The Ingres Database
  • The JBoss Enterprise Application Platform including the JBoss Application Server, Hibernate and Seam
  • An Eclipse based IDE

Further information can be found at http://info.ingres.com/g/?TATV5CKFX4 or http://esd.ingres.com/product/Enterpr.../JBoss.

About Ingres: Ingres is the vendor of the Open Source database system named 'Ingres Database'. Further information can be found at http://www.ingres.com or http://community.ingres.com . An article how to install Ingres with Alfresco can be found at http://wiki.alfresco.com/wiki/Installing_Alfresco_Labs_3b_and_Ingr....


The architecture


A Java package named 'community.ingres.mediaclient.alfresco' was created. This package contains the following base classes:


  • Aspect
  • Client

The child classes of Client are:


  • UploadClient
  • UpdateClient
  • DownloadClient

So I will show in this tutorial how to use Alfresco's Web Service API for Java to perform uploads to, updates on and downloads from your Alfresco server.

There is only one example child of the class Aspect which is named:


  • MediaClientAspect

We will talk about aspects later in this tutorial.

Additionally the following file is part of the package:


  • alfresco.properties

This file contains the connection information to your Alfresco server. In my case it contains the following:



server.host=localhost
server.port=8180
server.user=admin
server.password=admin




The Clients


Let's begin to look at the implementation of the several clients!


The mother named Client


The Client class contains all the stuff which is used by all the other clients.


The store's definition


The Client class has a global constant named STORE which is shared over all other clients. STORE defines Alfresco's default location to store data:




protected final Store STORE = new Store(Constants.WORKSPACE_STORE, 'SpacesStore');





The session properties


It also has the following global variables:



       // Connection properties
protected String server_host = '';
protected String server_port = '';
protected String server_username = '';
protected String server_password = '';

The connection properties we want to use are stored inside the file 'alfresco.properties'. So the following method can be used to bind the connection properties to our Client:




protected void setSessionProperties()
{
 
  Properties prop = new Properties();

   try
   {
  prop.load(getClass().getResourceAsStream('alfresco.properties'));
    }
  catch(Exception e)
     {
         System.err.println('Can not load alfresco.properties');
         e.printStackTrace();
     }

  server_host = prop.getProperty('server.host');
  server_port = prop.getProperty('server.port');
  server_username = prop.getProperty('server.user');
  server_password = prop.getProperty('server.password');
 
}




Open and close sessions


Before we can communicate with the Alfresco server we have to open a session with it. The following code can be used to open and close a session:




protected void startSession()
{
  try
  {
   setSessionProperties();
   System.out.println('Connecting to: ' + 'http://'+server_host+':'+server_port+'/alfresco');
   WebServiceFactory.setEndpointAddress('http://'+server_host+':'+server_port+'/alfresco/api');
   AuthenticationUtils.startSession(server_username, server_password);
  }
  catch(Exception e)
  {
          System.err.println('Can not initiate session with Alfresco server.');
          e.printStackTrace();
         }
}

protected void endSession()
{
 
  System.out.println('Closing connection.');
  AuthenticationUtils.endSession();
}


Get the services to communicate with


We will use the 'ContentService' and the 'RepositoryService' to communicate with our Alfresco server. Here you can see how to get these services:




protected RepositoryServiceSoapBindingStub getRepositoryService()
{
  return WebServiceFactory.getRepositoryService();
}

protected ContentServiceSoapBindingStub getContentService()
{
  return WebServiceFactory.getContentService();
}


Some helper methods


The method 'ReferenceToParent(Reference)' transforms the given node reference to a parent reference. The method 'normilizeNodeName(String)' is used to validate the paths we will use as part of references. 




protected ParentReference ReferenceToParent(Reference spaceref)
{
  ParentReference parent = new ParentReference();
 
  parent.setStore(STORE);
  parent.setPath(spaceref.getPath());
  parent.setUuid(spaceref.getUuid());
  parent.setAssociationType(Constants.ASSOC_CONTAINS);
 
  return parent;
}

//Blanks are allowed in space names but not in paths. Because the path should depend on the name we need a version of the name which contains no blanks

protected String normilizeNodeName(String name)
{
  return name.replace(' ','_');
}


Reference to the 'Company Home' space


We will create all our documents in folders under 'Company Home'. To reference the 'Company Home' space the following code could be used:




protected ParentReference getCompanyHome()
{
        ParentReference companyHomeParent = new ParentReference(STORE, null, '/app:company_home', Constants.ASSOC_CONTAINS, null);
        return companyHomeParent;
}


As you can see we are working with 'References', which means references to nodes. A node could be a space or a document.  A parent reference is a special kind of reference and indicates that it contains children. The path of the Company Home space is '/app:company_home'.


The UploadClient


The purpose of this client (surprise ;-) ) is to upload documents to Alfresco. This chapter covers how to create spaces and documents.




Create a space inside the root folder named 'Company Home'


To create a space under 'Company Home' the following code can be used:



protected Reference createSpace(String spacename) throws Exception
{
  Reference space = null;
  
  // Create the space if it is not already existent
  try {
  
   //Therefore a reference to the maybe not existent space is required
  
   System.out.println('Entering space ' + spacename);
    
   space = new Reference(STORE, null, getCompanyHome().getPath() + '/cm:' + normilizeNodeName(spacename));
   getRepositoryService().get(new Predicate(new Reference[]{space}, STORE, null));
  }
  catch (Exception e1)
  {
   System.out.println('The space named ' + spacename + ' does not exist. Creating it.');
  
   ParentReference companyHome = getCompanyHome();
  
   // Set Company Home as the parent space
   companyHome.setChildName(Constants.createQNameString(Constants.NAMESPACE_CONTENT_MODEL, normilizeNodeName(spacename)));
  
   //Set the space's property name
   NamedValue[] properties = new NamedValue[]{Utils.createNamedValue(Constants.PROP_NAME,spacename)};
           
   // Create the space using CML (Content Manipulation Language)
   CMLCreate create = new CMLCreate('1', companyHome, null, null, null, Constants.TYPE_FOLDER, properties);
                       CML cml = new CML();
                       cml.setCreate(new CMLCreate[]{create});
           
                    //Execute the CML create statement
                     try {
    getRepositoryService().update(cml);
   } catch (Exception e2) {
   
    System.err.println('Can not create the space.');
    throw e2;
   }             
           
  }


return space;
}


In summary the following steps are performed:


  1. Try to enter the space by it's reference. This causes an exception if the space does not exist which will be catched by the following steps.
  2. Create a reference to the space which should contain this new one. In our case the parent space is the 'Company Home' space
  3. Set the just referenced space as the parent of the new space
  4. Named values are tuples (name, value). Set the properties of the space by using an array of named values. We only set the 'name' property (Constants.PROP_NAME).
  5. Create a Content Manipulation Language object to create the new space. Those objects are named CML statements.
  6. Add the just created CML statement to the list of CML to execute
  7. Get Alfresco's Repository Service and execute the CML




Create a space inside another space


The way is nearly the same as in the previous section, but additionally we pass a reference to the parent space.



protected Reference createSpace(Reference parentref, String spacename) throws Exception
{
 
  Reference space = null;
  ParentReference parent = ReferenceToParent(parentref);
 
   
 
  try {
  
   System.out.println('Entering space ' + spacename);
   space = new Reference(STORE, null, parent.getPath() + '/cm:' + normilizeNodeName(spacename));
   WebServiceFactory.getRepositoryService().get(new Predicate(new Reference[]{space}, STORE, null));
  }
  catch (Exception e1)
  {
   System.out.println('The space named ' + spacename + ' does not exist. Creating it.');
  
   parent.setChildName(Constants.createQNameString(Constants.NAMESPACE_CONTENT_MODEL, normilizeNodeName(spacename)));
  
   //Set the space's property name
   NamedValue[] properties = new NamedValue[]{Utils.createNamedValue(Constants.PROP_NAME, spacename)};
           

   // Create the space using CML (Content Manipulation Language)
   CMLCreate create = new CMLCreate('1', parent, null, null, null, Constants.TYPE_FOLDER, properties);
                        CML cml = new CML();
                        cml.setCreate(new CMLCreate[]{create});
           
                       //Execute the CML create statement
                       try {
    getRepositoryService().update(cml);
   } catch (Exception e2) {
   
    System.err.println('Can not create the space.');
    throw e2;
   }             
           
  
  }


return space;
 
 
}



So we have methods to create a space inside the root space and in another space. The following example shows how these methods could be used:




startSession();

//To create the space 'Comany Home/Mediaclient/artist_name/album_name/title_name'
 
Reference r = createSpace(createSpace(createSpace('Mediaclient'), artist_name), album_name),title_name);
 
endSession();





Create and upload a document


A document is always a child of a space.




protected Reference createDocument(Reference parentref, String docname, byte[] content) throws Exception
{
  Reference document = null;
  ParentReference parent = ReferenceToParent(parentref);
 
 
  parent.setChildName(Constants.createQNameString(Constants.NAMESPACE_CONTENT_MODEL, normilizeNodeName(docname)));
 
  NamedValue[] properties = new NamedValue[]{Utils.createNamedValue(Constants.PROP_NAME, docname)};
                CMLCreate create = new CMLCreate('1', parent, null, null, null, Constants.TYPE_CONTENT, properties);
                CML cml = new CML();
                cml.setCreate(new CMLCreate[]{create});
       
                //Execute the CML create statement
       
                UpdateResult[] results = null;
       
                try {
            System.out.println('Creating the document ' + docname);
            results = getRepositoryService().update(cml);
            document = results[0].getDestination();
                }
               catch (Exception e)
               {
           System.err.println('Can not create the document.');
    throw e;
               }
       
 
        //Set the content
       
        ContentFormat format = new ContentFormat(Constants.MIMETYPE_TEXT_PLAIN, 'UTF-8');
       
        try {
         System.out.println('Setting the content of the document');
   getContentService().write(document, Constants.PROP_CONTENT, content, format);
  } catch (Exception e2) {
   System.err.println('Can not set the content of the document.');
   throw e2;
  }
       
 
        return document;
}


In addition to the steps those were already explained for the 'createSpace' method, the content of the document must be set. Be aware of that the method above does create a text document without a special aspect. The UploadClient does also contain another method with the following signature:


  • protected Reference createDocument(Reference parentref, String docname, byte[] content, Aspect aspect, String mime_type);

It allows to specify the document's mime_type and aspect. I will explain it later in the chapter 'User defined Aspects'.

The following example shows how you can use the createDocument method to upload a file to a specific space. If any of the spaces is not existent it will be created.




public String uploadFile(String name, byte[] data) throws Exception
{
 
  startSession();
 
  Reference r = createDocument(createSpace(createSpace('Mediaclient'),'Uploads'),name,data);
 
  endSession();
 
  return r.getPath();
}


The UpdateClient


This client enables us to delete, move or rename nodes.


Delete a node



protected void deleteSpace(Reference space) throws Exception
{
 
  CMLDelete delete = new CMLDelete(new Predicate(new Reference[]{space},null,null));
 
  CML cml = new CML();
  cml.setDelete(new CMLDelete[]{delete});
 
  //Execute the CMLDelete statement
       
       try {
          System.out.println('Deleting the space ' + space.getPath());
   WebServiceFactory.getRepositoryService().update(cml);
       
        } catch (Exception e2) {
   
    System.err.println('Can not delete the space.');
    throw e2;
        }    
 
 
}

A document can be deleted the same way as a space.




Get the parent of a space


The renameSpace method needs to know where a space is located. Therefore the following method is used:




protected Reference getParent(Reference space) throws Exception
{
  QueryResult result = getRepositoryService().queryParents(space);
  ResultSet resultset = result.getResultSet();
 
  String first_parent_id = resultset.getRows(0).getNode().getId();
 
  System.out.println('The queried parent id is: ' + first_parent_id);
 
  Reference parent = new Reference(STORE, first_parent_id, null);
 
  return parent;
}


In summary the RepositoryService's method 'queryParents(Reference)' is used to get the parent of a space.




Move a space




protected void moveSpace(Reference space, Reference dest, String newname) throws Exception
{
   ParentReference parentDest = ReferenceToParent(dest);
   parentDest.setChildName(Constants.createQNameString(Constants.NAMESPACE_CONTENT_MODEL, normilizeNodeName(newname)));
  
   CMLMove move = new CMLMove();
   move.setTo(parentDest);
   move.setWhere(new Predicate(new Reference[]{space},STORE,null));
  
  
   NamedValue[] properties = new NamedValue[]{Utils.createNamedValue(Constants.PROP_NAME,newname)};
   CMLUpdate update = new CMLUpdate();
   update.setProperty(properties);
   update.setWhere(new Predicate(new Reference[]{space},STORE,null));
   
   CML cml  = new CML();
   cml.setMove(new CMLMove[]{move});
   cml.setUpdate(new CMLUpdate[]{update});
  
  //Execute the CML move and Update statement
         try {
           System.out.println('Moving the space with path ' + space.getPath() + ' or id ' + space.getUuid() + '\n' +
                           'to destination space with path ' + dest.getPath() + ' or id ' + dest.getUuid()  + '\n' +
                           'by using the name ' + newname + '.');
   getRepositoryService().update(cml);
        
         } catch (Exception e2) {
   
    System.err.println('Can not move the space.');
    throw e2;
           }   
}





Rename a space


Since we know how to move a space we also can use the 'moveSpace' method to rename a space:



protected void renameSpace(Reference space, String newName) throws Exception
{
  Reference parent = getParent(space);  
  moveSpace(space, parent, newName);
}

To rename a space simply means to move it with another name to the same parent space.


The DownloadClient


To get the content of a document as an array of bytes the following source code can be used:




protected byte[] getContent(Reference node) throws Exception
{
  Content content = null;
 
  System.out.println('Getting content of document with path ' + node.getPath() + ' or id ' + node.getUuid() + '.' );
 
  try {
   Content[] read = getContentService().read(new Predicate(new Reference[]{node}, STORE, null),Constants.PROP_CONTENT);
   content = read[0];
   System.out.println('Got ' +  read.length + ' content elements.');
   System.out.println('The first content element has a size of '+ content.getLength() + ' segments.');
  }
  catch (Exception e)
  {
   System.err.println('Can not get the content.');
   throw e;
  }
 
 
  ByteArrayOutputStream out = new ByteArrayOutputStream();
  InputStream in = ContentUtils.getContentAsInputStream(content);
  byte[] buf = new byte[2048];
  int size;
  while ((size=in.read(buf)) != -1 ) {
   out.write(buf, 0, size);
  }
 
  return out.toByteArray();
}


The following example shows how to use this method:




public byte[] downloadTitle(String artistSpaceName, String albumSpaceName, String titleName) throws Exception
{
 
  startSession();
 
  Reference title = new Reference(STORE, null, getCompanyHome().getPath() + '/cm:Mediaclient' + '/cm:' + normilizeNodeName(artistSpaceName) + '/cm:' + normilizeNodeName(albumSpaceName) + '/cm:' + normilizeNodeName(titleName));
  byte[] content = getContent(title);
 
  endSession();
 
  return content;
}


Combined usage of the clients


Let's combine our clients to update a document by replacing it.




Update a document by replacing it


In my case the document is an MP3 file and has a special aspect assigned. Instead updating the documents properties directly I used the existing clients to perform this update. The following examples shows the run method of a Java bean named TitleUpload and may explain what I am meaning:



public void run() throws Exception
{
  System.out.println('Uploadig file ' + titleName);
  System.out.println('================================================================');
 
  UploadClient alfresco_upload = new UploadClient();
  UpdateClient alfresco_update = new UpdateClient();
  DownloadClient alfresco_download = new DownloadClient();
 
  String path = '';

  if ( create )
  {
   path = alfresco_upload.uploadTitle(artistName,albumName,titleName,titleGenre,titleLength,data);
  }
 
  if ( !create && override )
  {
   alfresco_update.deleteTitle(artistName, albumName, oldTitleName);
   path = alfresco_upload.uploadTitle(artistName,albumName,titleName,titleGenre,titleLength,data);
  }
 
  if ( !create && !override)
  {
   byte[] oldData = alfresco_download.downloadTitle(artistName, albumName, oldTitleName);
   alfresco_update.deleteTitle(artistName, albumName, oldTitleName);
   path = alfresco_upload.uploadTitle(artistName,albumName,titleName,titleGenre,titleLength,oldData);
  }
 
  System.out.println('The path is:' + path);
 
  System.out.println('================================================================');
}

In summary the following cases are covered:


  • If we want to create a new document we can simply upload the file.
  • If the document is already existent in the repository but the properties and the content needs to be updated, then delete the old document and create a new one with the new properties and the new content.
  • If the document is already existent and the properties do need to be updated, but the content needs no update, then temp. store the old content of the document, delete the old document and and create a new one by using the new properties and the old content.




User defined Aspects


One node can have assigned several aspects. An out of the box available aspect is for instance 'versionable'. It adds version information to a document. I will show in this chapter how to use an user defined aspect to add additional properties to an document.


Register the aspect


Before we can use the aspect we have to tell the Alfresco server how our aspect looks like. Therefore an XML definition of our aspect must be placed inside the server's extension folder. Here an example definition, which means the contents of the file 'MediaClientAspect.xml':




<model name='my:mediaclient' xmlns='http://www.alfresco.org/model/dictionary/1.0'>
   <description>The Mediaclient Model</description>
   <author></author>
   <version>1.0</version>


  <imports>
         <import uri='http://www.alfresco.org/model/dictionary/1.0' prefix='d'/>
         <import uri='http://www.alfresco.org/model/content/1.0' prefix='cm'/>
  </imports>


   <namespaces>
      <namespace uri='my.mediaclient.model' prefix='my'/>
   </namespaces>


    <aspects>
      <aspect name='my:mediaclientAspect'>
         <title>The Ingres Mediaclient aspect</title>
         <properties>
           
            <property name='my:Ingres Mediaclient name'>
               <type>d:text</type>
            </property>
           
     <property name='my:Ingres Mediaclient genre'>
               <type>d:text</type>
            </property>
           
     <property name='my:Ingres Mediaclient length'>
      <type>d:text</type>
     </property> 
       
     <property name='my:Ingres Mediaclient album'>
  <type>d:text</type>
     </property>

            <property name='my:Ingres Mediaclient artist'>
  <type>d:text</type>
     </property>
  </properties>
      </aspect>
   </aspects>


</model>




As you can see we defined that any document with the 'mediaclientAspect' has the additional properties 'Ingres Mediaclient name', 'Ingres Mediaclient genre', 'Ingres Mediaclient length', 'Ingres Mediaclient album' and 'Ingres Mediaclient artist'.

To register this new Aspect we have to create/edit two additional files those are also placed inside the server's extension folder. The first one is the file 'custom-model-content.xml':




<beans>

 
     <bean id='extension.dictionaryBootstrap' parent='dictionaryModelBootstrap' depends-on='dictionaryBootstrap'>
         <property name='models'>
             <list>
                 <value>alfresco/extension/MediaClientModel.xml</value>
             </list>
         </property>
     </bean>
          
</beans>


The second file which must be adapted is the 'web-client-config-custom.xml':




<alfresco-config>

  <config evaluator='aspect-name' condition='my:mediaclientAspect'>
      <property-sheet>
         <show-property name='my:Ingres Mediaclient name'/>
         <show-property name='my:Ingres Mediaclient genre'/>
  <show-property name='my:Ingres Mediaclient length'/>
         <show-property name='my:Ingres Mediaclient album'/>
         <show-property name='my:Ingres Mediaclient artist'/>
      </property-sheet>
   </config>

   <config evaluator='string-compare' condition='Action Wizards'>
      <aspects>
         <aspect name='my:mediaclientAspect'/>
      </aspects>
   </config>

</alfresco-config>


The father named Aspect


To encapsulate the attributes of an aspect a new class named Aspect was created inside the the package community.ingres.mediaclient.alfresco. This class has the following global variables:




protected String aspect_name = '';
protected String aspect_domain = '';
protected ArrayList<String> property_names = new ArrayList<String>();
protected ArrayList<String> property_values = new ArrayList<String>();


Additionally the constructor 'public Aspect(String aspect_name, String aspect_domain, String[] property_names, String[] property_values)'  and some getter, setter and helper methods are included to access the aspect's attributes.


The MediaClientAspect


The class MediaClientAspect is a child of Aspect. It extends the class Aspect with an own constructor and special setter and getter methods to access the MediaClientAspect's properties directly.

Here the constructor:




MediaclientAspect()
{
  aspect_name = 'mediaclientAspect';
  aspect_domain = '{my.mediaclient.model}';
  property_names = ArrayToList(new String[]{'Ingres Mediaclient name','Ingres Mediaclient genre','Ingres Mediaclient length','Ingres Mediaclient album', 'Ingres Mediaclient artist' });
  property_values = ArrayToList(new String[]{'','','','','',''});
}


You can see that the aspect's name, domain and properties are the same as we did choose in our XML definition.

Here one of the getters and and the setters those were added to the MediaClientAspect class to access the apect's properties directly:




public void setName(String name)
{
  property_values.set(0, name);
}

public String getName()
{
  return property_values.get(0);
}


Create and upload a document by choosing an aspect


This section explains how to set the aspect of a document during it's creation. The source code is nearly the same as in the section 'Create and upload a document' but it additionally contains the CML for adding the aspect.




protected Reference createDocument(Reference parentref, String docname, byte[] content, Aspect aspect, String mime_type) throws Exception
{
  Reference document = null;
  ParentReference parent = ReferenceToParent(parentref);
 
 
  parent.setChildName(Constants.createQNameString(Constants.NAMESPACE_CONTENT_MODEL,normilizeNodeName(docname)));
 
  //CML for the Create
  NamedValue[] properties = new NamedValue[]{Utils.createNamedValue(Constants.PROP_NAME, docname)};
  CMLCreate create = new CMLCreate('1', parent, null, null, null, Constants.TYPE_CONTENT, properties);
       
 
  //CML for adding the Aspect 
  String[] aspect_prop_tmp = aspect.getProperty_names_as_array();
  String[] aspect_val_tmp = aspect.getProperty_values_as_array();
  NamedValue[] aspect_properties = new NamedValue[aspect_prop_tmp.length];
 
  for (int i = 0; i




Conclusion


This was the first of two tutorials those describe how the Mediaclient demo application was developed. It contained detailed information about the clients those were implemented to communicate with the Alfresco server by focusing on Alfreso's Web Service API for Java. The second tutorial will provide further details how to integrate these 'Alfresco clients' into a JBoss Seam based web application.

Positive and negative feedback is very welcome. Please send it via email to david.maier@ingres.com !



--david.maier@ingres.com 11:48, 24 June 2009 (BST)