Last week I had a chance to experiment with the new File System Transfer Receiver (FSTR) also known as the File Transfer Receiver (FTR) in Alfresco 4.0.0 Enterprise. This is also available in the community version. Since directions are a little spotty, I thought I would give a rundown of the process of setting it up.

Background

Alfresco's Transfer Service (wiki, docs) was introduced in version 3.3 as a means to transfer from one Alfresco repository to another Alfresco repository. Alone this isn't particularly useful to an Alfresco user as it primarily allows programmatic access to the Transfer Service, but when combined with the Replication Service (wiki, docs) it becomes very powerful. The Replication Service allows an Alfresco user to create a one time or recurring transfer of content from one repository to another. You can select which content to transfer as well as where to transfer it to. A piece that was missing previously was a transfer from an Alfresco repository to the file system.

Overview

First we will need to download and install the FSTR, which is available from the "Custom Installs & Optional Modules" section of the community download page. As of this writing, the file was listed as "alfresco-community-file-transfer-receiver-4.0.d.zip". For the enterprise version, look in the Support Portal under Online Resources->Downloads->Alfresco Enterprise 4.0->4.0.0 the file is labeled "4.0 Alfresco Enterprise File Transfer Receiver".

Once installed, we need to check the configuration properties and start it up. We can then create a transfer target in Share and create a new replication job.

FSTR Installation

The FSTR can be installed anywhere on your file system. It uses an embedded version of Tomcat to create a server to give your Alfresco repository something to transfer to. Simply unzip the package and run the server. Directions on installing the FSTR can be found athttp://docs.alfresco.com.

FSTR Configuration and Startup

There are three configuration files for the FSTR. The first is the ftr-launcher.properties. This file contains the Tomcat base directory and the port number to startup on. The defaults should work fine and more details can be found in the docs at http://docs.alfresco.com.

The second file, ftr-custom.properties, is used to configure the operation of the FSTR. It contains settings for the root directory, staging directory, derby database connection string, username and password. Again, the defaults should work fine. More detailed information can be found at http://docs.alfresco.com.

The third file, log4j.properties, simply controlls the logging for the FSTR. You can change these settings if you want to see more verbose output in the alfresco-ftr.log. More info can be found at http://docs.alfresco.com.

To run the server simply use the following command from the directory where you installed the FSTR:

java –jar file-transfer-receiver.jar

Directions for running the FSTR can be found at http://docs.alfresco.com.

You can navigate to http://localhost:9090/alfresco-ftr/service/api/transfer to see if the FSTR is running. You should get something like this:

Alfresco Web Script Status 404 - Not Found The Web Script /alfresco-ftr/service/api/transfer has responded with a status of 404 - Not Found. 404 Description: Requested resource is not available. Message: 01090000 Script url /api/transfer does not map to a Web Script. Exception: org.springframework.extensions.webscripts.WebScriptException - 01090000 Script url /api/transfer does not map to a Web Script. org.springframework.extensions.webscripts.AbstractRuntime.executeScript(AbstractRuntime.java:173) org.springframework.extensions.webscripts.servlet.WebScriptServlet.service(WebScriptServlet.java:118) javax.servlet.http.HttpServlet.service(HttpServlet.java:722) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240) org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:164) org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:462) org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:403) org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:301) org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:162) org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:140) org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:309) java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source) java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) java.lang.Thread.run(Unknown Source) Server: Spring WebScripts - v1.0.0 (Release 968) schema 1,000 Time: Feb 9, 2012 9:33:58 AM

Creating a Transfer Target in Share

There are some brief directions on the Transfer Service wiki and http://docs.alfresco.com, but I will go into some detail here.
First, click on the "repository" icon, which is at the top of the page in Share (You may have to enable this, although it is enabled by default now. This can be done through share-config-custom.xml, directions on the wiki.):
Repository
From there, navigate to "Repository -> Data Dictionary -> Transfers-> Transfer Target Groups -> Default Group". You will create a new folder here, which will prompt you for a name, title, and description. Click on "Edit Properties" in the document actions (on the right hand side in the Document Library) and you will get something like this:
FSTR Transfer Target - Edit Properties
You can enter the Endpoint Host and Endpoint Port. The defaults are localhostand 9090 if you didn't change anything in the configuration and you are running your FSTR on the same server as your Alfresco repository. You will need to change your Endpoint Pathfrom /alfresco/service/api/transfer to /alfresco-ftr/service/api/transfer. This corresponds to the alfresco-ftr.war file that comes in the FSTR zip. Username and Password will be admin and admin unless you changed them during configuration (which you should on anything but a toy system). Finally, be sure to check Enabled and save.
In addition to this, we need to change the type of the transfer target folder to File Transfer Target. You can do this by clicking on your target in the breadcrumb two times. The first will show you the contents of the folder (which is empty) and the second will bring you to the folder details. On the right hand side you should see something like this:
FSTR Transfer Target - Change Type
From there click on "Change Type" and select "File Transfer Target" from the drop down list. You will not notice any changes immediately. Go back and edit the properties on the folder and you will see a new property:
FSTR Transfer Target - Edit Properties
There is now a Root Folder, which can be used to select which files to transfer. If you do not select a Root Folder, the replication job will run, but no files will be transferred.

If you do not change the type of the transfer target to "File Transfer Target" you might get exceptions like this:

14:40:33,808 INFO [org.alfresco.repo.transfer.LoggingTransferProgressMonitorImpl] Transfer Log (workspace://SpaceStore/fea1c64f-1b21-4b83-b072-dbb94ec50175): Error processing commit org.springframework.dao.DataIntegrityViolationException: Error setting null parameter. Most JDBC drivers require that the JdbcType must be specified for all nullable parameters. Cause: java.sql.SQLDataException: An attempt was made to get a data value of type 'VARCHAR' from a data value of type 'OTHER'. ; SQL []; An attempt was made to get a data value of type 'VARCHAR' from a data value of type 'OTHER'.; nested exception is java.sql.SQLDataException: An attempt was made to get a data value of type 'VARCHAR' from a data value of type 'OTHER'. at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:245) at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72) at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:71) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:346) at $Proxy4.insert(Unknown Source) at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:231) at org.alfresco.repo.transfer.fsr.FileTransferInfoDAOImpl.createFileTransferInfo(FileTransferInfoDAOImpl.java:64) at org.alfresco.repo.transfer.fsr.DbHelperImpl$5.execute(DbHelperImpl.java:131) at org.alfresco.repo.transfer.fsr.DbHelperImpl$5.execute(DbHelperImpl.java:128) at org.alfresco.repo.transaction.RetryingTransactionHelper.doInTransaction(RetryingTransactionHelper.java:388) at org.alfresco.repo.transfer.fsr.DbHelperImpl.createNodeInDB(DbHelperImpl.java:127) at org.alfresco.repo.transfer.fsr.ManifestProcessorImpl.startManifest(ManifestProcessorImpl.java:127) at org.alfresco.repo.transfer.AbstractManifestProcessorBase.startTransferManifest(AbstractManifestProcessorBase.java:131) at org.alfresco.repo.transfer.manifest.XMLTransferManifestReader.startDocument(XMLTransferManifestReader.java:92) at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startDocument(Unknown Source) at com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator.startDocument(Unknown Source) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.startEntity(Unknown Source) at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.startDocumentParsing(Unknown Source) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source) at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source) at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Unknown Source) at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source) at javax.xml.parsers.SAXParser.parse(Unknown Source) at javax.xml.parsers.SAXParser.parse(Unknown Source) at org.alfresco.repo.transfer.fsr.FileTransferReceiver.commit(FileTransferReceiver.java:177) at org.alfresco.repo.transfer.fsr.FileTransferReceiver$1.run(FileTransferReceiver.java:247) at java.lang.Thread.run(Unknown Source) Caused by: java.sql.SQLDataException: An attempt was made to get a data value of type 'VARCHAR' from a data value of type 'OTHER'. at org.apache.derby.impl.jdbc.SQLExceptionFactory40.getSQLException(Unknown Source) at org.apache.derby.impl.jdbc.Util.newEmbedSQLException(Unknown Source) at org.apache.derby.impl.jdbc.Util.newEmbedSQLException(Unknown Source) at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source) at org.apache.derby.impl.jdbc.EmbedConnection.newSQLException(Unknown Source) at org.apache.derby.impl.jdbc.ConnectionChild.newSQLException(Unknown Source) at org.apache.derby.impl.jdbc.EmbedPreparedStatement.dataTypeConversion(Unknown Source) at org.apache.derby.impl.jdbc.EmbedPreparedStatement.setNull(Unknown Source) at org.apache.commons.dbcp.DelegatingPreparedStatement.setNull(DelegatingPreparedStatement.java:108) at org.apache.commons.dbcp.DelegatingPreparedStatement.setNull(DelegatingPreparedStatement.java:108) at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:15) at org.apache.ibatis.executor.parameter.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:73) at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:61) at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:43) at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:56) at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:28) at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:87) at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:46) at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:118) at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:107) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:338) ... 24 more Caused by: java.sql.SQLException: An attempt was made to get a data value of type 'VARCHAR' from a data value of type 'OTHER'. at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source) at org.apache.derby.impl.jdbc.SQLExceptionFactory40.wrapArgsForTransportAcrossDRDA(Unknown Source) ... 49 more

Creating a Replication Job in Share

Before you get started configuring a replication job in Share. It is very important to make sure that alfresco-global.properties contains the following line:

replication.enabled=true

If this line is not present or the value is "false" then you will not be able to run any replication jobs. From Share you will see something like this if you try and run a replication job without it:Replication Job Started without replication.enabled=true
You will get an error similar to this in your alfresco.log if you try and run a replication job without it:

14:36:25,221 ERROR [org.alfresco.repo.action.AsynchronousActionExecutionQueueImpl] Failed to execute asynchronous action: Action[ id=80fa6061-eacc-4b5a-ac8b-69ad8b0a943f, node=workspace://SpacesStore/80fa6061-eacc-4b5a-ac8b-69ad8b0a943f ] org.alfresco.service.cmr.replication.ReplicationServiceException: 01120127 Unable to replicate. The replication service is not enabled at org.alfresco.repo.replication.ReplicationActionExecutor.executeImpl(ReplicationActionExecutor.java:277) at org.alfresco.repo.action.executer.ActionExecuterAbstractBase.execute(ActionExecuterAbstractBase.java:196) at org.alfresco.repo.action.ActionServiceImpl.directActionExecution(ActionServiceImpl.java:780) at org.alfresco.repo.action.ActionServiceImpl.executeActionImpl(ActionServiceImpl.java:700) at org.alfresco.repo.action.AsynchronousActionExecutionQueueImpl$ActionExecutionWrapper$1$1.execute(AsynchronousActionExecutionQueueImpl.java:403) at org.alfresco.repo.transaction.RetryingTransactionHelper.doInTransaction(RetryingTransactionHelper.java:388) at org.alfresco.repo.transaction.RetryingTransactionHelper.doInTransaction(RetryingTransactionHelper.java:259) at org.alfresco.repo.action.AsynchronousActionExecutionQueueImpl$ActionExecutionWrapper$1.doWork(AsynchronousActionExecutionQueueImpl.java:412) at org.alfresco.repo.security.authentication.AuthenticationUtil.runAs(AuthenticationUtil.java:519) at org.alfresco.repo.action.AsynchronousActionExecutionQueueImpl$ActionExecutionWrapper.run(AsynchronousActionExecutionQueueImpl.java:415) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:662)

You can configure a replication job by going to "Replication Jobs" from the "More..." icon at the top of the page in Share and selecting "Replication Jobs". From here click on "Create Job" and you should see an Alfresco form to configure the Replication Job. Filled in it should look something like this:
Replication Job - Configuration
You will need to enter a Name, select the Source Items, and be sure that Enabled is checked. For the Transfer Target you will select the transfer target that you previously configured. If you want you can schedule the replication job to recur by checking Schedule Job and using the configuration options provided.

You may have noticed that we have a Root Folder in the transfer target and a Source Items in the replication job. These were a little confusing to me as they seemed to be the same thing. The short story is that the Root Folder corresponds to the ftr-root folder on the file system. This is location from which all transferred files and folders will be transferred to. The Source Items property refers to the content actually being transferred from the repository to the transfer target. Often these will be the same value. It is difficult to get your head wrapped around, so for more information see this Alfresco forum post.

Once you have finished you can click on "Run Job" to begin a transfer. Use the "Refresh" button to see the status of the replication job.

Wrap Up

Although not very difficult to set up, there are many "moving parts" to the FSTR setup process. If you happen to get one of the steps wrong it might be very difficult to troubleshoot.

I want to thank Brian Robinson at Alfresco for helping me get started with the File System Transfer Receiver and providing the Alfresco FSTR wiki page. I also would like to thank Brian Remmington for helping me to understand the differences between the properties in the transfer target and the replication job.


Loading Conversation