1

EDIT: Apparently I had to make an AMP and map it to Alfresco.war. But now I can't access the code I wrote, so I guess I'll have to use Webscripts and the like. Can someone provide an example of how to add a document to the Alfresco Repository with a Java backed webscript?

ORIGINAL QUESTION:

I've searched google-wide for a way to add a document to the Alfresco Repository with Java code. But I was not able to find a way that would work. I know how I can add a document to the Repository: use the NodeService. But the problem is that I cannot get an instance of the NodeService. I've tried to inject it with @Autowired, I've tried using a bean and I've tried using an ApplicationContext. None of the ways worked...

Way #1:
Injection in a class:

@Autowired
NodeService nodeService

Way #2:
In service-context.xml:

<bean id="somerandombeanname" class="management.FileManager" >
    <property name="moduleId" value="${project.artifactId}" />
    <property name="serviceRegistry" ref="ServiceRegistry" />
    <property name="nodeService" ref="NodeService" />
    <property name="transactionService" ref="TransactionService" />
    <property name="contentService" ref="ContentService" />
</bean>

In the class I added a getter and setter for all the services and the serviceRegistry:

private NodeService nodeService;

public void setNodeService(NodeService nodeService) {
        this.nodeService = nodeService;
}

Way #3:

appContext = new ClassPathXmlApplicationContext("classpath:alfresco/application-context.xml");

serviceRegistry = (ServiceRegistry) appContext.getBean(ServiceRegistry.SERVICE_REGISTRY);

nodeService = serviceRegistry.getNodeService();

Ways #1 and #2 gave me a NullPointerException simply stating the NodeService is null. Way #3 gave a mile long StackTrace because of an AlfrescoRuntimeException because it failed to initialize a keystore:

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ssl.keyStore' defined in class path resource [alfresco/encryption-context.xml]: Invocation of init method failed; nested exception is org.alfresco.error.AlfrescoRuntimeException: 04180000 Failed to initialize keystore:
Location: E:/Alfresco/alf_data/keystore/ssl.keystore
Provider: null
Type:     JCEKS
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1513)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:633)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
    at management.FileManager.<init>(FileManager.java:36)
    at simple.start.main(start.java:25)
    ... 5 more
Caused by: org.alfresco.error.AlfrescoRuntimeException: 04180000 Failed to initialize keystore:
Location: E:/Alfresco/alf_data/keystore/ssl.keystore
Provider: null
Type:     JCEKS
    at org.alfresco.encryption.AlfrescoKeyStoreImpl.loadKeyStore(AlfrescoKeyStoreImpl.java:566)
    at org.alfresco.encryption.AlfrescoKeyStoreImpl.safeInit(AlfrescoKeyStoreImpl.java:537)
    at org.alfresco.encryption.AlfrescoKeyStoreImpl.init(AlfrescoKeyStoreImpl.java:122)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1639)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1580)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1510)
    ... 18 more
Caused by: java.lang.IllegalArgumentException: name
    at sun.misc.URLClassPath$Loader.findResource(URLClassPath.java:494)
    at sun.misc.URLClassPath.findResource(URLClassPath.java:176)
    at java.net.URLClassLoader$2.run(URLClassLoader.java:551)
    at java.net.URLClassLoader$2.run(URLClassLoader.java:549)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findResource(URLClassLoader.java:548)
    at java.lang.ClassLoader.getResource(ClassLoader.java:1147)
    at org.springframework.core.io.ClassPathResource.resolveURL(ClassPathResource.java:147)
    at org.springframework.core.io.ClassPathResource.exists(ClassPathResource.java:135)
    at org.alfresco.encryption.SpringKeyResourceLoader.getSafeInputStream(SpringKeyResourceLoader.java:67)
    at org.alfresco.encryption.SpringKeyResourceLoader.loadKeyMetaData(SpringKeyResourceLoader.java:133)
    at org.alfresco.encryption.AlfrescoKeyStoreImpl$KeyInfoManager.loadKeyMetaData(AlfrescoKeyStoreImpl.java:1016)
    at org.alfresco.encryption.AlfrescoKeyStoreImpl$KeyInfoManager.<init>(AlfrescoKeyStoreImpl.java:998)
    at org.alfresco.encryption.AlfrescoKeyStoreImpl.getKeyInfoManager(AlfrescoKeyStoreImpl.java:395)
    at org.alfresco.encryption.AlfrescoKeyStoreImpl.loadKeyStore(AlfrescoKeyStoreImpl.java:560)
    ... 27 more

Yes, the keystore exists and yes I have regenerated a new keystore.

I'm using Alfresco 5.0.1 and I'm working on the Repository side (not Share).

Teysz
  • 741
  • 9
  • 33
  • 2
    Why don't you use a Java backed webscript to perform what you need? – abarisone May 18 '15 at 14:52
  • Because I'm working on the Repository side, not Share. – Teysz May 19 '15 at 07:55
  • 1
    WebScripts can be deployed to either the Repo or Share, and in fact large parts of the Alfresco application depend on repo-side java backed webscripts! – Gagravarr May 19 '15 at 08:43
  • So tell me why I should use webscripts while I'm not even touching HTML... – Teysz May 19 '15 at 11:38
  • Can you tell us at which location you have put `service-context.xml`? – Naman May 19 '15 at 12:37
  • service-context.xml is located at src/main/amp/config/alfresco/module//context – Teysz May 19 '15 at 12:48
  • @Gagravarr Do you know how I could be able to reach my java code while it's in an AMP that's mapped onto the Alfresco.war? – Teysz May 21 '15 at 14:53
  • Having a java-backed webscript in a repository AMP is perfectly normal and done by huge numbers of Alfresco installs. Just follow any tutorial! – Gagravarr May 21 '15 at 16:29
  • I've done the [http://docs.alfresco.com/5.0/tasks/ws-folderListing-Java-scripting.html tutorial] found on the official documentation website of Alfresco, but I don't see how I can alter that example to what I need. Could you link to a tutorial regarding this case? – Teysz May 22 '15 at 07:30

3 Answers3

4
@Autowired
NodeService nodeService

Will not work in alfresco

You need to inject it with proper setter method. Your bean should be like below.

<bean id="somerandombeanname" class="management.FileManager" >
    <property name="moduleId" value="${project.artifactId}" />
    <property name="serviceRegistry" ref="ServiceRegistry" />
    <property name="nodeService" ref="NodeService" />
    <property name="transactionService" ref="TransactionService" />
    <property name="contentService" ref="ContentService" />
</bean>

Your java class should contain following for injecting nodeService.

private NodeService nodeService;

public void setNodeService(NodeService nodeService) {
        this.nodeService = nodeService;
}
Krutik Jayswal
  • 3,165
  • 1
  • 15
  • 38
  • That's exactly what I had at the time of asking the question. I shall include it explicitly in my question too because I did mention it. Anyways it still doesn't work. – Teysz May 19 '15 at 07:24
  • are you using webscript ,policy or something else.can you please put your javacode as well? – Krutik Jayswal May 19 '15 at 07:26
  • The annotation was mentioned as way #1. The bean and setter are mentioned as way #2. The annotation was removed before I used the bean and setter way. – Teysz May 19 '15 at 07:27
  • Nope, no webscript (this is repository, not share). – Teysz May 19 '15 at 07:27
  • okay,your alfresco server is running and then you are executing code? – Krutik Jayswal May 19 '15 at 07:28
  • Yes, the Alfresco server is running and then I execute my code. – Teysz May 19 '15 at 07:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/78154/discussion-between-krutik-jayswal-and-teysz). – Krutik Jayswal May 19 '15 at 07:30
  • 1
    Have you added setter for all other services which you are injecting? and you do not need to inject "serviceRegistry" if you are explicitly injecting required services. – mitpatoliya Jun 01 '15 at 11:40
3

Luckily I have code for file upload thorugh JAVA backed webscript. Hope this help you too.To create java backed webscript see this

Create one class named CustomFileUpload.java and put following content

package com.upload;

import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.DeclarativeWebScript;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptRequest;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.io.IOUtils;
import org.json.JSONException;
import org.json.JSONObject;

public class CustomFileUpload extends DeclarativeWebScript {

    private final String UPLOAD_FILE_PATH = "C:\\Users\\Test\\Desktop\\test.txt";
    private int statusCode;

    protected Map<String, Object> executeImpl(WebScriptRequest arg0, Status status, Cache cache) {
        Map<String, Object> model = new HashMap<String, Object>();
        try {
            String URL = "http://localhost:8080/alfresco/service/upload/fileupload?alf_ticket=" +getAlfticket();
            File file = new File(UPLOAD_FILE_PATH);
            String filetype = "text/plain";
            String filename = file.getName();
            HttpClient client = new HttpClient();
            PostMethod post = new PostMethod(URL);

            Part[] parts = {

                    new FilePart("filedata", filename, file, filetype, null),
                    new StringPart("filename", filename),
                    new StringPart("description", "This is test description"),
                    new StringPart("destination", "workspace://SpacesStore/bb424b1d-0418-4954-8591-b8c807264df0")
            };

            post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams()));
            statusCode = client.executeMethod(post);
            System.out.println(post.getResponseBodyAsString());
            post.releaseConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (statusCode == 200) {
            model.put("result", "File uploaded successfully.");
            return model;
        } else {
            model.put("result", "There was an error while uploading document.");
            return model;
        }
    }

    private static String getAlfticket() throws IOException, JSONException {
        URL url = new URL("http://localhost:8080/alfresco/service/api/login?u=admin&pw=admin&format=json");
        URLConnection con = url.openConnection();
        InputStream in = con.getInputStream();
        String encoding = con.getContentEncoding();
        encoding = encoding == null ? "UTF-8" : encoding;
        String json = IOUtils.toString(in, encoding);
        JSONObject getData = new JSONObject(json);

        return getData.getJSONObject("data").get("ticket").toString();
    }
}

NOTE: In destination you can put nodeRef of folder in which you want to upload.

Than create bean in context file name it whatever you want say mycustom-context.xml and put it in ALFRESCO_HOME\tomcat\shared\classes\alfresco\extension and content

<bean id="webscript.com.upload.customupload.post" class="com.upload.CustomFileUpload" parent="webscript">
</bean>

Finally register this web script in alfresco by creating customupload.post.desc.xml. and put

<webscript>
   <shortname>File Upload</shortname>
   <description>Upload files to user home</description>
   <url>/upload/fileupload?alf_ticket={ticket}</url>
   <format default="json"/>
   <authentication>user</authentication>
</webscript>

And last create view as we have declared JSON is default format so we need to create customupload.post.json.ftl

${result}

And put these both files in ALFRESCO_HOME\tomcat\shared\classes\alfresco\extension\templates\webscripts\com\upload

Now restart server and hit http://localhost:8080/alfresco/service/upload/fileupload and you will see file uploaded in folder(Whatever you have given). For Reference

Naman
  • 2,205
  • 2
  • 19
  • 32
  • In ExecuteImpl you mention the URL is "/upload?alf_ticket=", but in the desc.xml you mention "/upload/fileupload?alf_ticket=". Shouldn't these be the same? – Teysz May 26 '15 at 08:34
  • Ohh...sorry my mistake there should be `/upload/fileupload` in `executeImpl.java` :) I have modified code accordingly. – Naman May 26 '15 at 08:58
  • Is the bean id important? Or can I add anything I like to it? Or should it be something like `webscript...`? – Teysz May 26 '15 at 09:31
  • Yes it is important it must be like `webscript...`. For simplicity `webscript...` – Naman May 26 '15 at 09:36
  • Could you add a JSON example? – Teysz May 26 '15 at 10:44
  • See the updated ans, change `CustomFileUpload.java` as well to get ouput in ftl. – Naman May 26 '15 at 11:15
  • I've added the files to the their destinations and tried to access that URL using PostMan so I can do the Post method. In PostMan it says it got a 200 statuscode, but the text returned in JSON shows that an error occured. So I tried to add the error to the `result` but it look likes it enters a infinit loop which renders the Repository inaccessible and I have to restart the server to fix the Repository. I've tried it over and over, the same thing keeps happening. – Teysz May 27 '15 at 12:29
  • Can you show us how you passing error to result? Also show some tomcat logs as well. – Naman May 27 '15 at 13:48
  • This is the code: `string errormessage = ""; errormessage = e.getMessage(); model.put("result", "There was an error while uploading document - statuscode: " + statusCode + " - Error: " + errorMessage);` And this is the error: `ERROR [solr.tracker.AbstractTracker] [SolrTrackerScheduler_Worker-3] Tracking failed javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target...` – Teysz May 27 '15 at 14:01
  • I am not sure but error looks like validation of certificate fails. I think something is wrong with your alfresco installation because code you have put doesn't lead webscript to infinite loop. – Naman May 27 '15 at 16:13
  • I've solved that error, but now no error is logged and it will not load in postman and cURL. I've checked all possible log files and it's not showing an error. – Teysz Jun 04 '15 at 13:22
  • I've added a remote debugger to it and it looks like the debugger does the same as postman and will hang on the line: statusCode = client.executeMethod(post); Do you know why it would do that? – Teysz Jun 25 '15 at 08:49
  • Try using `get method` instead of post once. – Naman Jun 25 '15 at 09:05
0

Finally! This is the solution for adding a file to the Repository:

CustomFileUpload.java:

package org.example;

import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.DeclarativeWebScript;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptRequest;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.io.FileUtils;

public class CustomFileUpload extends DeclarativeWebScript {
    private final String UPLOAD_FILE_PATH = "{someRandomFile}";
    private final String UPLOAD_DESTINATION = "workspace://SpacesStore/{someRandomNodeRef}";
    protected ServiceRegistry serviceRegistry;

    public ServiceRegistry getServiceRegistry() {
        return serviceRegistry;
    }

    public void setServiceRegistry(ServiceRegistry serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
    }

    protected Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache) {
        File file = new File(UPLOAD_FILE_PATH);
//      NodeRef parent = getCompanyHome();
        NodeRef parent = new NodeRef(UPLOAD_DESTINATION);
        String name = "name of file in Repository " + System.currentTimeMillis();

        Map<QName, Serializable> props = new HashMap<QName, Serializable>(1);
        props.put(ContentModel.PROP_NAME, name);

        // use the node service to create a new node
        NodeRef node = serviceRegistry.getNodeService().createNode(
                        parent,
                        ContentModel.ASSOC_CONTAINS,
                        QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name),
                        ContentModel.TYPE_CONTENT, props).getChildRef();

        // Use the content service to set the content onto the newly created
        // node
        ContentWriter writer = serviceRegistry.getContentService().getWriter(node, ContentModel.PROP_CONTENT, true);
        writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
        writer.setEncoding("UTF-8");
        String text = "";
        try {
            text = FileUtils.readFileToString(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        writer.putContent(text);

        Map<String, Object> model = new HashMap<String, Object>();
        if (status.getCode() == Status.STATUS_OK) {
            model.put("resultRepoWS", "File \"" + file.getName() + "\" uploaded successfully to the repository. Status: " + status.getCode());
            return model;
        } else {
            model.put("resultRepoWS", "There was an error while uploading document \"" + file.getName() + "\" - Status: " + status.getCode());
            return model;
        }
    }

    //If you want to test with CompanyHome first use this method instead of the NodeRef
    @SuppressWarnings("unused")
    private NodeRef getCompanyHome() {
        StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
        serviceRegistry.getSearchService();
        ResultSet rs = serviceRegistry.getSearchService().query(storeRef, SearchService.LANGUAGE_XPATH, "/app:company_home");
        NodeRef parent = null;
        try {
            if (rs.length() == 0) {
                throw new AlfrescoRuntimeException("Didn't find Company Home");
            }
            parent = rs.getNodeRef(0);
        } finally {
            rs.close();
        }
        return parent;
    }
}

This java class is placed in the following folder:

{tomcat}\webapps\alfresco\WEB-INF\classes\org\example

Where org\example is the same as the package org.example. Now we have a class, now we need the configuration files as I call them:

  • customfileupload-context.xml

Which is located here

  • {tomcat}\shared\classes\alfresco\extension

You will also need these:

  • customfileupload.post.desc.xml
  • customfileupload.post.json.ftl

Which are located here

  • {tomcat}\shared\classes\alfresco\extension\webscripts\org\example

Noticed the folder? It's the same like the package mentioned earlier.

Contents of customfileupload-context.xml:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN 2.0//EN'
  'http://www.springframework.org/dtd/spring-beans-2.0.dtd'>
<beans>
    <bean id="webscript.org.example.customfileupload.post" class="org.example.CustomFileUpload" parent="webscript">
        <property name="ServiceRegistry" ref="ServiceRegistry" />
    </bean>
</beans>

Contents of customfileupload.post.desc.xml:

<webscript>
   <shortname>File Upload</shortname>
   <description>Upload files to user home</description>
   <url>/upload/fileupload.json</url>
   <format default="json"/>
   <authentication runas="admin">guest</authentication>
   <transaction>required</transaction>
</webscript>

Contents of customfileupload.post.json.ftl:

<#escape x as jsonUtils.encodeJSONString(x)>  {  "resultRepoWS": "${resultRepoWS}"  }  </#escape>

This is it. With this you'll be able to upload a file to the Repository of Alfresco with Alfresco 5.

Teysz
  • 741
  • 9
  • 33