Showing posts with label alfresco community. Show all posts
Showing posts with label alfresco community. Show all posts

Monday, October 10, 2022

Control indexing in Alfresco with Alfresco Search Services

I came acorss a query from a friend to disable content indexing and allow only matadata indexing recently. The last time i tried this when i was using Alfresco 5.0 with Solr4. This time i tried with Alfresco Content Services 7.2.0.1 and Alfresco Search Services 2.0.3.5 and the good news is that, it still works. 

If your application does not require full-text content search capability, then disabling content indexing comes handy and increases the performance as well.

If you are curious to try out, then follow along.

Pre-requisites
  • You have an environment up and running with Alfresco Content Services 7.x and Alfresco Search Services 2.x (Solr6)
  • You have administrative privileges

Looking for Alfresco Content Services 7.x with Alfresco Search Services 2.x installation steps? , checkout these posts:




Content and metadata are indexed by default, it is out-of-the-box behavior. There are two ways you can control content/metadata indexing behavior in order to fulfil the search and indexing requirements. We will go over both options.

Control indexing behavior with help of content model aspect:


To control the indexing behavior, you can make use of a content model aspect named "cm:indexControl" which has two properties. These properties indicate whether content/metadata should be indexed.

The value of these properties are set to true by default.

<aspect name="cm:indexControl">
	<title>Index Control</title>
	<properties>
		<property name="cm:isIndexed">
			<title>Is indexed</title>
			<type>d:boolean</type>
			<default>true</default>
		</property>
		<property name="cm:isContentIndexed">
			<title>Is content indexed</title>
			<type>d:boolean</type>
			<default>true</default>
		</property>
	</properties>
</aspect>

You can apply cm:indexControl aspect on the nodes to control the indexing behavior by setting the appropriate properties. Note that, this approach works only for certain types like cm:folder, cm:content and sub-types. You need to keep in mind that, if you have a large number of nodes which needs to be excluded from content/metadata indexing then this option is not a right choice as you will have to apply the aspect by setting "cm:isContentIndexed" to "false" on all those nodes. 
In this situation second option (which we will see next) comes handy.

To learn more on content model, aspects and their application, refer:  Content Model Extension Point

If you wish to bulk apply the aspect with updated values, this post may be useful as a reference: Applying the aspects in bulk

Friday, July 24, 2020

Change ACS 6.x/7.x, Share 6.x/7.x, Proxy (nginx), Solr6 and DB (postgres) ports using docker-compose.yml and DockerFile


It is a very common requirement to use different set of available port (s) as per company policy rather than using default port (s) for the applications.


Background:


Before container based environments, we had to follow below given steps in order to change the ports (these points still applies to an environment setup via distribution package):

    • Update default connector ports 8080, 8443, 8009 and 8005 to required ports.
    • Sometimes we use JPDA_ADDRESS for remote debug which is default '8000' in $ALFRESCO_INSTALL_DIR/tomcat/bin/catalina.sh. If we use remote debug, them update to required port as needed.
  • Update the required 'alfresco' and 'share' ports in $ALFRESCO_INSTALL_DIR/tomcat/shared/classes/alfresco-global.properties
  • Update the required 'alfresco' ports in $ALFRESCO_INSTALL_DIR/tomcat/shared/classes/alfresco/web-extension/share-config-custom.xml for remote configuration (<config evaluator="string-compare" condition="Remote">).
        • http://localhost:{REQUIRED_PORT}/alfresco/s --> DEFAULT: 8080
      • Update 'alfresco' endpoint url: 
        • http://localhost:{REQUIRED_PORT}/alfresco/s --> DEFAULT: 8080
      • Update 'alfresco-feed' endpoint url: 
        • http://localhost:{REQUIRED_PORT}/alfresco/s --> DEFAULT: 8080
      • Update 'alfresco-api' endpoint url: 
        • http://localhost:{REQUIRED_PORT}/alfresco/api --> DEFAULT: 8080
  • Update the required 'alfresco' port in 'solrcore.properties' file.
    • Find the 'alfresco.port' property in solrcore.properties file and update:
      • alfresco.port=<requiredPort>, default : 8080
    • For SOLR4, we used below paths:
      • $ALFRESCO_INSTALL_DIR/solr4/workspace-SpacesStore/conf/solrcore.properties
      • $ALFRESCO_INSTALL_DIR/solr4/archive-SpacesStore/conf/solrcore.properties
      • $SOLR_HOME/solrhome/alfresco/conf/solrcore.properties
      • $SOLR_HOME/solrhome/archive/conf/solrcore.properties



All the above given steps will be almost same for ACS 6.x as well if you are using standalone installation and not managing the services, images and containers via docker based deployment. 

When using docker based deployment, we use docker-compose.yml file to configure all the services which will be used as a base for launching the corresponding containers. We configure all the required ports (host and container ports) in the docker-compose.yml file and expose any ports if required either via docker-compose.yml or DockerFile.

It is possible to change the host ports via docker-compose.yml file but default ports (container ports) which are exposed within docker images (specially connector ports in tomcat which is shipped with acs and share images) can't be changed via docker-compose.yml alone. We have to take help of DockerFile, which can be used to update required ports at the time of build process.

Similarly, if you are using proxy (nginx) then 'ngnix.conf' configuration also needs an update to reference the required ports. By default ngnix will try to forward all requests to '8080' which is default port for acs and share.

It will be like re-builing the original images (acs, share, proxy etc. images) with updated ports and containers will be launced using the updated images. 

For some of the servives such as 'postgres', you can change the default port directly from docker-compose.yml as it gets access to command line, it is like executing 'postgres -p 5433' via command line. 
We can simply pass the command line param '-p <requiredPort>' or use 'expose' option in docker-compose.yml in the 'postgres' service definition.

For 'solr6 (alfresco-search-service)', we can either update the startup script or update the shared.properties via DockerFile or add SOLR_PORT environment variable in docker-compose.yml. This env variable will be used by jetty server to start service on required port. 
Additionally, you can also pass Jvm param using JAVA_OPTS, e.g. -Djetty.port=9999

Change Alfresco, Share, Nginx (Proxy), Solr and Database (postgres) ports with help of DockerFile and docker-compose.yml:


Considering the aforementioned steps for changing the ports, we need to follow the same  approach for docker based deployment as well but with help of docker-compose.yml and DockerFile.

I will be using port '7080' instead of '8080' for acs, share and proxy. I will also update the tomcat connector ports to 7005, 7009 and 7443. I will use '5555' instead of '5432' for postgres and '9999' instead of '8983' for solr6.


Here are default ports:

Service

Default Ports

Note

Tomcat connector ports

8005, 8080, 8443, 8009

Default within tomcat shipped with alfresco and share images.

alfresco

8080

 

share

8080

 

proxy

80, 8080 -> 8080

Default port on proxy(nginx) is 80, where port 8080 is exposed for providing access to alfresco and share. nginx forwards requests on 8080 (host port) to alfresco’s and share’s port 8080.

We can change the host port to any other port as well easily. E.g. 81 -> 8080 (Request will come on port 81 which nginx will forward to 8080)

postgres

5432

 

solr6

8083 -> 8983

8083 is host port and 8983 is container port. Alfresco uses 8983 to communicate with solr6. To access solr admin, administrators use 8083

 

Access via browser: http://localhost:8083/

 

transform-core-aio

8090-> 8090

Both host and container ports are same here.

Alfresco uses 8090 to communicate with transformation services.

We can use the port 8090 to access the transformation services via browser.

 

Access via browser: http://localhost:8090/

 

activemq

8161 -> 8161 # Web Console

Both host and container ports are same here.

port 8161 can be used for accessing the ‘Web Console’ via browser and alfresco would use the same port to communicate with activemq.

 

Access WebConsole via browser: http://localhost:8161/

 


The steps we are going to follow, are applicable to ACS 6.x , ACS7.1 and ACS 7.2

This post has been updated to match the latest ACS version (ACS 7.3) as well. 

Let's create some directories for keeping the DockerFile and required configs which will be used for re-building the updated images from OOTB images.

  • Create a directory 'configs-to-override' in the same directory where you have kept your 'docker-compose.yml' file.
  • Under 'configs-to-override' directory, create following directories:
    • Create 'alfresco' directory --> It will be used to keep 'DockerFile' for acs image
      • Create an empty 'DockerFile' file which we will use to put build instructions for 'alfresco' service
    • Create 'share' directory --> It will be used to keep 'DockerFile' for share image
      • Create an empty 'DockerFile' file which we will use to put build instructions for 'share' service
    • Create 'proxy' directory --> It will be used to keep 'DockerFile' and 'nginx.conf' file for nginx image
      • Create an empty 'DockerFile' file which we will use to put build instructions for 'proxy' service
      • Create an empty 'nginx.conf' file which we will use to put proxy configuration for services

Monday, June 1, 2020

Unlock a node in Alfresco which could not be unlocked


I had an issue recently when i was trying to edit a word document. I deleted the working copy node somehow. I have no clues how it happend. But this word document node was locked foreever. Cancel checkout action was always failing. I was not able to cancel checkout and unlock the node.

While i was investigating the issue, i looked for the nodeRef of the word document via node browser. I found that it had cm:checkedOut aspect (because i checkedOut for edit oboviously).

I thought removing the aspect and unlocking will solve my problem. I tried to remove cm:checkedOut aspect from the node via repository js webscript but it failed. 

In my further investigation i reviewed, org.alfresco.service.cmr.coci.CheckOutCheckInService.cancelCheckout(NodeRef) method defintion in CheckOutCheckInService class:

public NodeRef cancelCheckout(NodeRef workingCopyNodeRef);

It indicated that, it expects workingCopyNode in order to cancel the checkout.

To unlock the node i have to somehow remove the cm:checkout aspect and then unlock the node. 

When i investigated more in logs (enabled the debug log on org.alfresco.repo.coci package), i saw that on click of Cancel Checkout, a backend behavior (org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCancelCheckOut) was stopping the process. The below given method seemd to be validating working copy node by firing org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCancelCheckOut.beforeCancelCheckOut(NodeRef) behavior.

org.alfresco.repo.coci.CheckOutCheckInServiceImpl.invokeBeforeCancelCheckOut(NodeRef)

The above validation was happening before the unlock (org.alfresco.service.cmr.lock.LockService.unlock(NodeRef, boolean, boolean)) method call. 

So i decided to disable the behaviors and then unlock the node followed by aspect removal. Here is the fix:

function main () { 
//using the string nodeRef of the word document which was locked. var nodeRef = search.findNode("workspace://SpacesStore/7c093a3d-c16e-416d-8087-9ccd93aa2a06"); var webContext = Packages.org.springframework.web.context.ContextLoader.getCurrentWebApplicationContext(); var behaviourFilter = webContext.getBean("policyBehaviourFilter", org.alfresco.repo.policy.BehaviourFilter);  
// Disable all behaviors, so we can remove the aspect without any validations behaviourFilter.disableAllBehaviours(); // Remove cm:checkedOut aspect and try to unlock the node which was not getting unlocked due to behavior validations nodeRef.removeAspect("cm:checkedOut"); nodeRef.unlock(); // Enable all the behaviors behaviourFilter.enableAllBehaviours(); 
} main();

Sunday, September 21, 2014

Upload documents to alfresco using REST Api


Upload documents to alfresco using REST Api:

Prerequisites:----- 

Following jar files are required (httpcomponents-client-4.3.4):

 commons-codec-1.6.jar
 commons-logging-1.1.3.jar
 commons-httpclient-3.1.jar
 httpcore-4.1.3.jar
 junit-4.12.0.jar

**************************************************************************
Alfresco's Login (POST) REST Api '/alfresco/service/api/login' return authentication ticket in response.

This is how authTicket will look like:  <ticket>TICKET_4b36ecaxxxxx5cdc5d782xxxxxxxxxxxx</ticket>

In the response you will get the authentication ticket, this authentication ticket will be used in all subsequent requests in order to get authenticated with alfresco.

In the subsequent request (as example given below in Test class) i am trying to upload a file to alfresco using REST URI::

'/alfresco/service/api/upload?alf_ticket=TICKET_4b36ecaxxxxx5cdc5d782xxxxxxxxxxxx'

Here i am passing alf_ticket as a parameter with the 'authentication ticket' value which i got from '/alfresco/service/api/login' REST call.

For more details visit: http://docs.alfresco.com/5.0/references/RESTful-intro.html

**************************************************************************

1- HttpUtils

/*
 * Created By: Abhinav Kumar Mishra
 * Copyright &copy; 2014. Abhinav Kumar Mishra.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
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.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.ParseException;

/**
 * The Class HttpUtils.
 *
 * @author Abhinav Kumar Mishra
 * @since 2014
 */
public final class HttpUtils {
 
 /** The Constant logger. */
 private static final Log LOG = LogFactory.getLog(HttpUtils.class);
 
 /**
  * Gets the login response.
  *
  * @param authURI the path
  * @param username the username
  * @param password the password
  * @return the login response
  * @throws ParseException the parse exception
  * @throws IOException Signals that an I/O exception has occurred.
  */
 public Map<String, String> getAuthResponse(final String authURI,
   final String username, final String password)
   throws ParseException, IOException {
  
  LOG.info("Authenticating request..");
  final Map<String, String> responseMap = new ConcurrentHashMap<String, String>();
  GetMethod getRequest = null;
  try {
   final HttpClient httpclient = new HttpClient();
   getRequest = new GetMethod(getAuthURL(authURI, username, password));
   final int statusCode = httpclient.executeMethod(getRequest);
   LOG.info("Auth Response Status: "+ statusCode+"|"+ getRequest.getStatusText());
 
   responseMap.put(Constants.RESP_BODY, getRequest.getResponseBodyAsString());
   responseMap.put(Constants.CONTENT_TYPE, getRequest.getResponseHeader(Constants.CONTENT_TYPE_HDR).getValue());
   responseMap.put(Constants.STATUS_CODE, String.valueOf(statusCode));
   
  } finally {
   if(getRequest!=null){
    getRequest.releaseConnection();
   }
  }
  return responseMap;
 }
 
  
 /**
  * Gets the auth ticket.
  *
  * @param authURI the auth uri
  * @param username the username
  * @param password the password
  * @return the auth ticket
  * @throws IOException Signals that an I/O exception has occurred.
  */
 public String getAuthTicket(final String authURI,
   final String username, final String password) throws IOException {
  final Map<String, String> responseMap = getAuthResponse(authURI, username, password);
  final String ticketFrmResponse = responseMap.get(Constants.RESP_BODY);
  final int startindex = ticketFrmResponse.indexOf("TICKET");
  final int endindex = ticketFrmResponse.indexOf("</");
  return ticketFrmResponse.substring(startindex, endindex);
 }
 

 /**
  * Document upload.
  *
  * @param docFileObj the doc file obj
  * @param authTicket the auth ticket
  * @param uploadURI the upload uri
  * @param siteID the site id
  * @param uploadDir the upload dir
  * @return the string
  * @throws IOException Signals that an I/O exception has occurred.
  */
 public String documentUpload(final File docFileObj,
   final String authTicket, final String uploadURI,
   final String siteID, final String uploadDir) throws IOException {

  String uploadResponse = Constants.EMPTY;
  PostMethod postRequest = null;
  try {
   final String uploadURL = getFileUploadURL(uploadURI,authTicket);
   
   LOG.info("documentUpload() | Upload URL: " + uploadURL);
   
   final HttpClient httpClient = new HttpClient();
   postRequest = new PostMethod(uploadURL);
      final String mimeType = getMimeType(docFileObj);
   final String docName = docFileObj.getName();
   LOG.info("documentUpload() | Uploading document: "+docName+" , content-type: "+mimeType);

   //To always replace the file then end following parameters in parts array
   //uploaddirectory can accept a folder name or a folder path. Do not include documentLibrary in the path 
   final Part[] parts = {
     new FilePart("filedata", docName, docFileObj, mimeType,null),
     new StringPart("filename", docName),
     new StringPart("overwrite", "true"),
     new StringPart("siteid",siteID),
     new StringPart("containerid","documentLibrary"),
     new StringPart("uploaddirectory",uploadDir)
  };
  
   postRequest.setRequestEntity(new MultipartRequestEntity(parts, postRequest.getParams()));
   
   final int statusCode = httpClient.executeMethod(postRequest); 
   
   uploadResponse = postRequest.getResponseBodyAsString();
   LOG.info("documentUpload() | Upload status: "+statusCode+"  \nUpload response: "+uploadResponse);
  
  } finally{
   if(postRequest!=null){
    //releaseConnection http connection
    postRequest.releaseConnection();
   }
  }
  return uploadResponse;
 }

 /**
  * Gets the auth url.
  *
  * @param path the path
  * @param username the username
  * @param password the password
  * @return the url
  */
 private String getAuthURL(final String path, final String username,
   final String password) {
  final StringBuffer urlStrb = new StringBuffer(path);
  urlStrb.append(Constants.QUES);
  urlStrb.append(Constants.U);
  urlStrb.append(Constants.EQL);
  urlStrb.append(username);
  urlStrb.append(Constants.AMPERSND);
  urlStrb.append(Constants.PW);
  urlStrb.append(Constants.EQL);
  urlStrb.append(password);
  return urlStrb.toString();
 }
 
 
 /**
  * Url file upload.
  *
  * @param path the path
  * @param ticket the ticket
  * @return the string
  */
 private String getFileUploadURL(final String path, final String ticket) {
  final StringBuffer urlStrb = new StringBuffer(path);
  urlStrb.append(Constants.QUES);
  urlStrb.append(Constants.TICKET_QRY);
  urlStrb.append(Constants.EQL);
  urlStrb.append(ticket);
  return urlStrb.toString();
 }
 
 /**
  * Gets the mime type.
  *
  * @param fileObj the file obj
  * @return the mime type
  * @throws IOException Signals that an I/O exception has occurred.
  */
 public String getMimeType(final File fileObj) throws IOException {
  final Path source = Paths.get(fileObj.getPath());
  return Files.probeContentType(source);
 }
}


2- Constants:

/*
 * Created By: Abhinav Kumar Mishra
 * Copyright &copy; 2014. Abhinav Kumar Mishra.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * The Class Constants.
 */
public final class Constants{

 /** The Constant EMPTY. */
 public static final String EMPTY = "";

 /** The Constant BR. */
 public static final String BR = "\n";

 /** The Constant SERVER. */
 public static final String SERVER = "server";

 /** The Constant BASEPATH. */
 public static final String BASEPATH = "baseurl";

 /** The Constant LOGIN_PATH. */
 public static final String LOGIN_PATH = "authURI";

 /** The Constant UPLOAD_PATH. */
 public static final String UPLOAD_PATH = "uploadURI";

 /** The Constant INPUT_PATH. */
 public static final String INPUT_PATH = "inputPath";

 /** The Constant DESC. */
 public static final String DESC = "description";

 /** The Constant U. */
 public static final String U = "u";

 /** The Constant PW. */
 public static final String PW = "pw";

 /** The Constant USERNAME. */
 public static final String USERNAME = "username";

 /** The Constant PASSWORD. */
 public static final String PASSWORD = "password";

 /** The Constant ENCODING. */
 public static final String ENCODING = "UTF-8";

 /** The Constant SERVER_ERR. */
 public static final String SERVER_ERR = "500";

 /** The Constant EQL. */
 public static final String EQL = "=";

 /** The Constant QUES. */
 public static final String QUES = "?";

 /** The Constant AMPERSND. */
 public static final String AMPERSND = "&";

 /** The Constant RESP_BODY. */
 public static final String RESP_BODY = "responsebody";

 /** The Constant CONTENT_TYPE. */
 public static final String CONTENT_TYPE = "content-type";

 /** The Constant STATUS_CODE. */
 public static final String STATUS_CODE = "statuscode";

 /** The Constant CONTENT_TYPE_HDR. */
 public static final String CONTENT_TYPE_HDR = "Content-Type";

 /** The Constant MIME_TYPE. */
 public static final String MIME_TYPE = "application/json";

 /** The Constant GLOBAL_PROPERTIESFILE. */
 public static final String GLOBAL_PROPERTIESFILE = "config.properties";

 /** The Constant TICKET_QRY. */
 public static final String TICKET_QRY = "alf_ticket";

 /** The Constant SITE_ID. */
 public static final String SITE_ID = "siteid";

 /** The Constant CONTAINER_ID. */
 public static final String CONTAINER_ID = "containerid";

 /** The Constant UPLOAD_DIR. */
 public static final String UPLOAD_DIR = "uploaddirectory";

}

3- Directory utility class

/*
 * Created By: Abhinav Kumar Mishra
 * Copyright &copy; 2014. Abhinav Kumar Mishra.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;


/**
 * This class DirectoryTraverser.<br/>
 * It returns set of files by traversing directories recursively.
 *
 * @author Abhinav Kumar Mishra
 * @since 2014
 */
public final class DirectoryTraverser {

 /**
  * Gets the file uris.
  *
  * @param startDir the start dir
  * @return the file uris
  * @throws FileNotFoundException the file not found exception
  */
 public static Set<File> getFileUris(final File startDir) throws FileNotFoundException {
  checkDirectories(startDir); // throw exception if not valid.
  return getUrisRecursive(startDir);
 }

 /**
  * Gets the uris recursive.<br/>
  * Recursively traverse each directory and uris of files.
  *
  * @param startDir the a starting dir
  * @return the uris recursive
  * @throws FileNotFoundException the file not found exception
  */
  private static Set<File> getUrisRecursive(final File startDir) throws FileNotFoundException {
 final Set<File> sortedSetOfFiles = new HashSet<File>();
 final File[] filesAndDirs = startDir.listFiles();
 final List<File> filesDirs = Arrays.asList(filesAndDirs);
 final Iterator<File> filesDirsItr = filesDirs.iterator();
 while (filesDirsItr.hasNext()) {
  final File file = filesDirsItr.next();
  sortedSetOfFiles.add(file); // Add files and directory URIs both
  // If uri is a directory the revisit it recursively.
  if (!file.isFile()) {
   // Call 'getUrisRecursive' to extract uris from directory
   final Set<File> innerSet = getUrisRecursive(file);
   sortedSetOfFiles.addAll(innerSet);
  }
 }

 return sortedSetOfFiles;
 }




/**
  * Checks if is valid directory.<br/>
  * If directory exists then it is valid. If directory is valid then it can
  * be read.
  *
  * @param directoryUri the a directory
  * @throws FileNotFoundException the file not found exception
  */
  private static void checkDirectories(final File directoryUri) throws FileNotFoundException {
   if (directoryUri == null) {
    throw new IllegalArgumentException("Directory should not be null.");
   }if (!directoryUri.exists()) {
    throw new FileNotFoundException("Directory does not exist: "+ directoryUri);
   }if (!directoryUri.isDirectory()) {
    throw new IllegalArgumentException("Is not a directory: "+ directoryUri);
   }if (!directoryUri.canRead()) {
    throw new IllegalArgumentException("Directory cannot be read: "+ directoryUri);
   }
 }

  private DirectoryTraverser() {
  super();
  }
}


4- Test

/*
 * Created By: Abhinav Kumar Mishra
 * Copyright &copy; 2014. Abhinav Kumar Mishra.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;

import junit.framework.TestCase;

/**
 * The Class UploadDocumentHttpTest.
 *
 * @author Abhinav Kumar Mishra
 * @since 2014
 */
public class UploadDocumentHttpTest extends TestCase{

 /**
  * Test document upload.
  *
  * @throws IOException Signals that an I/O exception has occurred.
  */
 @Test
 public void testDocumentUpload() throws IOException {
  final String uploadURI = "http://127.0.0.1:8080/alfresco/service/api/upload";
  final String authURI =  "http://127.0.0.1:8080/alfresco/service/api/login";
  final String username = "admin";
  final String password = "admin";
  final String inputUri = "C:/Users/Abhi/Desktop/data"; // files to be uploaded from this directory
  final String siteID = "testpoc"; //id of the site for e.g if site name is TestPoc the id will be testpoc
  final String uploadDir = "testUpload"; //directory created under document library
  
  final HttpUtils httpUtils = new HttpUtils();
  String authTicket = Constants.EMPTY;
  try {

    // Get the authentication ticket from alfresco.
    //This authTicket will be used in all subsequent requests in order to get authenticated with alfresco.
   // e.g TICKET_4b36ecaxxxxx5cdc5d782xxxxxxxxxxxx

   authTicket = httpUtils.getAuthTicket(authURI, username, password);
  } catch (IOException e) {
   e.printStackTrace();
  }

  final StringBuffer responseBody= new StringBuffer();

  final File fileObject = new File (inputUri);
  //if uri is a directory the upload all files..
  if(fileObject.isDirectory()){
   final Set<File> setOfFiles = DirectoryTraverser.getFileUris(fileObject);
   for (Iterator<File> iterator = setOfFiles.iterator(); iterator.hasNext();) {
    final File fileObj = iterator.next();
    //call document upload
    if(fileObj.isFile()){
     responseBody.append(httpUtils.documentUpload(
       fileObj, authTicket, uploadURI, siteID,
       uploadDir));
     responseBody.append(Constants.BR);
    }
       }
  }else{
   responseBody.append(httpUtils.documentUpload(
     fileObject, authTicket, uploadURI, siteID,
     uploadDir));
  }

  System.out.println("Response of upload operation >>>: "+responseBody);
  assertEquals(true, responseBody.toString().contains("File uploaded successfully"));
 }
}



Further implementations:

If you want to upload a new version of already existing file, you just have to get the nodeRef of existing file and pass below given parts parameter to same Upload REST API call. See documentUpload method above, I have highlighted the parts parameter. You have to replace the parts parameter and upload the file.

// To upload the file as a new version once the file was uploaded, then send following parameters in 
// parts array. Note that we have to remove the "override" parameter from the parts array and add 
// "updateNodeRef" parameter.
 //uploaddirectory can accept a folder name or a folder path. Do not include documentLibrary in the path 
final Part[] partsUploadAsVersion = {
     new FilePart("filedata", docName, docFileObj, mimeType,null),
     new StringPart("filename", docName),
     new StringPart("siteid",siteID),
     new StringPart("containerid","documentLibrary"),
     new StringPart("uploaddirectory",uploadDir), //Optional Parameter
    new StringPart("updateNodeRef ", nodeRefOfExistingFile) 
  };

Note:- You will still need the old parts array to upload the file for the first time. Once file is uploaded then to upload new version of that file (node) you have to use above given option.

If you want to upload file to a target destination for example: 'Shared' folder in the alfresco repository, then you can use the destination parameter to pass the nodeRef and additionally pass the folder name inside the destination node.

Here is the sample:

final Part[] partsUploadAsVersion = {
     new FilePart("filedata", docName, docFileObj, mimeType,null),
     new StringPart("filename", docName),
     new StringPart("destination", destinationNodeRef), // nodeRef of 'Shared' folder i.e. workspace://SpacesStore/85d2a84d-271b-41b1-9449-02f5942893a0
     new StringPart("uploaddirectory",uploadDir), //Optional Parameter
    new StringPart("overwrite ", "true") 
  };


Here is the detailed description for upload api:

Upload file content and meta-data into repository.


POST /alfresco/service/api/upload

 

HTML form data
  • filedata, (mandatory) HTML type file
  • You must specify one of:
    • destination (the folder NodeRef where the node will be created)
    • updateNodeRef (the NodeRef of an existing node that will be updated)
    • siteid and containerid (the Site name and the container in that site where the document will be created)
  • uploaddirectory - name of the folder (either in the site container or the destination) where the document will be uploaded. This folder must already exist
  • description - Description for a version update (versionDescription)
  • contenttype - The content type that this document should be specialised to
  • majorversion
  • overwrite
  • thumbnails
Return content
  • nodeRef

Return status: STATUS_OK (200)

The web script description document specifies the following options:
ValueDescription
jsonThe default response format
userThe authentication access
requiredThe transaction level
anyThe format style

----------------------------------------------------------------------------------------------------------------------
If you want to download or delete a node/file then you can write a piece of code taking the reference of above code to download/delete the node/file.

REST API for download file/node is: (It should be HTTP Get request)

GET /alfresco/service/api/node/content/{store_type}/{store_id}/{id}?a=false&alf_ticket={TICKET}

Example:

http://127.0.0.1:8080/alfresco/service/api/node/content/workspace/SpacesStore/85d2a84d-271b-41b1-9449-02f5942893a0?a=false&alf_ticket=TICKET_5550fa5e9b87bead8f008e906185e023b7ce21ed

Where: 
a: means attach. if true, force download of content as attachment. Possible values are true/false
store_type: workspace
store_id: SpacesStore
id: nodeRefId (UUID of the node)

A StoreRef is comprised of:

Store Protocol - that is, the type of store
Store Identifier - the id of the store

Example storeRefs are:

workspace://SpacesStore  (store_type://store_id)
version://versionStore  (store_type://store_id)
archive://SpacesStore  (store_type://store_id)

For more on Download Services visit my another post here: Downloading Content From Alfresco

--------------------------------------------------------------------------------------------------------------------------------


REST API for delete file/node is (It should be HTTP Delete request)

DELETE /alfresco/service/api/node/{store_type}/{store_id}/{id}?alf_ticket={TICKET}

Example:
http://127.0.0.1:8080/alfresco/service/api/node/workspace/SpacesStore/0a1b9c30-39a6-4249-8f2b-78c4e8b29b2c?alf_ticket=TICKET_5550fa5e9b87bead8f008e906185e023b7ce21ed

Note: You need to pass alf_ticket as a parameter for very REST call. You can see, i am adding alf_ticket parameter for upload, download and delete examples above.

Refer to Repository REST API references:

https://docs.alfresco.com/5.0/references/RESTful-Repository.html