From 8e970e4892a42dabe6ed0f3ded814d8ecd2844d5 Mon Sep 17 00:00:00 2001
From: "Brian M. Long" <brian@inteligr8.com>
Date: Tue, 3 May 2022 13:15:11 -0400
Subject: [PATCH] split from acs-public-rest-api

---
 .gitignore                                    |  19 ++
 pom.xml                                       | 213 ++++++++++++++++++
 .../acs/AcsClientCxfConfiguration.java        |  32 +++
 .../alfresco/acs/AcsClientCxfImpl.java        |  29 +++
 .../alfresco/acs/AcsPublicRestApiCxfImpl.java |  25 ++
 .../alfresco/acs/AcsClientConfiguration.java  | 138 ++++++++++++
 .../alfresco/acs/AcsPublicRestApiImpl.java    |  27 +++
 .../acs/AcsClientJerseyConfiguration.java     |  32 +++
 .../alfresco/acs/AcsClientJerseyImpl.java     |  29 +++
 .../acs/AcsPublicRestApiJerseyImpl.java       |  25 ++
 .../services/javax.ws.rs.client.ClientBuilder |   1 +
 .../alfresco/acs/ConnectionCxfClientIT.java   |  28 +++
 .../inteligr8/alfresco/acs/CxfUploadIT.java   |  57 +++++
 .../inteligr8/alfresco/acs/ConditionalIT.java |  35 +++
 .../alfresco/acs/ConnectionClientIT.java      |  94 ++++++++
 .../com/inteligr8/alfresco/acs/UploadIT.java  |  26 +++
 .../services/javax.ws.rs.client.ClientBuilder |   1 +
 .../acs/ConnectionJerseyClientIT.java         |  28 +++
 .../alfresco/acs/JerseyUploadIT.java          |  58 +++++
 src/test/resources/local-oauth.properties     |   5 +
 src/test/resources/local.properties           |   3 +
 src/test/resources/log4j2.properties          |  19 ++
 src/test/vscode/createNode.http               |  53 +++++
 23 files changed, 977 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 pom.xml
 create mode 100644 src/main/cxf/com/inteligr8/alfresco/acs/AcsClientCxfConfiguration.java
 create mode 100644 src/main/cxf/com/inteligr8/alfresco/acs/AcsClientCxfImpl.java
 create mode 100644 src/main/cxf/com/inteligr8/alfresco/acs/AcsPublicRestApiCxfImpl.java
 create mode 100644 src/main/java/com/inteligr8/alfresco/acs/AcsClientConfiguration.java
 create mode 100644 src/main/java/com/inteligr8/alfresco/acs/AcsPublicRestApiImpl.java
 create mode 100644 src/main/jersey/com/inteligr8/alfresco/acs/AcsClientJerseyConfiguration.java
 create mode 100644 src/main/jersey/com/inteligr8/alfresco/acs/AcsClientJerseyImpl.java
 create mode 100644 src/main/jersey/com/inteligr8/alfresco/acs/AcsPublicRestApiJerseyImpl.java
 create mode 100644 src/test/cxf/META-INF/services/javax.ws.rs.client.ClientBuilder
 create mode 100644 src/test/cxf/com/inteligr8/alfresco/acs/ConnectionCxfClientIT.java
 create mode 100644 src/test/cxf/com/inteligr8/alfresco/acs/CxfUploadIT.java
 create mode 100644 src/test/java/com/inteligr8/alfresco/acs/ConditionalIT.java
 create mode 100644 src/test/java/com/inteligr8/alfresco/acs/ConnectionClientIT.java
 create mode 100644 src/test/java/com/inteligr8/alfresco/acs/UploadIT.java
 create mode 100644 src/test/jersey/META-INF/services/javax.ws.rs.client.ClientBuilder
 create mode 100644 src/test/jersey/com/inteligr8/alfresco/acs/ConnectionJerseyClientIT.java
 create mode 100644 src/test/jersey/com/inteligr8/alfresco/acs/JerseyUploadIT.java
 create mode 100644 src/test/resources/local-oauth.properties
 create mode 100644 src/test/resources/local.properties
 create mode 100644 src/test/resources/log4j2.properties
 create mode 100644 src/test/vscode/createNode.http

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c5c31ee
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+# Maven
+pom.xml.versionsBackup
+target
+
+# Eclipse
+.project
+.classpath
+.settings
+
+# Visual Studio Code
+.factorypath
+
+# Swagger Codegen
+.swagger-codegen
+.swagger-codegen-ignore
+
+# Personal
+personal*.properties
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..056d9b5
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,213 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>com.inteligr8.alfresco</groupId>
+	<artifactId>acs-public-rest-client</artifactId>
+	<version>2.0-SNAPSHOT</version>
+	<name>Alfresco Content Services ReST API Client for Java</name>
+
+	<properties>
+		<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
+		<maven.compiler.source>8</maven.compiler.source>
+		<maven.compiler.target>8</maven.compiler.target>
+		<maven.compiler.debuglevel>lines,vars,source</maven.compiler.debuglevel>
+
+		<!-- If you want to build for your specific version, point to your own 
+			installation -->
+		<acs.baseUrl>http://localhost:8080/alfresco</acs.baseUrl>
+		<acs.platform.tag>acs7</acs.platform.tag>
+		<it.directory>src/test/none</it.directory>
+
+		<junit.version>5.7.2</junit.version>
+		<spring.version>5.2.14.RELEASE</spring.version>
+		<jersey.version>2.35</jersey.version>
+		<cxf.version>3.4.7</cxf.version>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>com.inteligr8</groupId>
+			<artifactId>common-rest-client</artifactId>
+			<version>2.0-SNAPSHOT</version>
+			<classifier>${jaxrs.impl}</classifier>
+		</dependency>
+		<dependency>
+			<groupId>com.inteligr8.alfresco</groupId>
+			<artifactId>acs-public-rest-api</artifactId>
+			<version>2.0-SNAPSHOT</version>
+			<classifier>${acs.platform.tag}</classifier>
+		</dependency>
+		<dependency>
+			<groupId>org.junit.jupiter</groupId>
+			<artifactId>junit-jupiter-api</artifactId>
+			<version>${junit.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-test</artifactId>
+			<version>${spring.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpclient</artifactId>
+			<version>4.5.9</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.logging.log4j</groupId>
+			<artifactId>log4j-slf4j-impl</artifactId>
+			<version>2.17.2</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+	
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>build-helper-maven-plugin</artifactId>
+				<version>3.2.0</version>
+				<executions>
+					<execution>
+						<id>add-jaxrs-src</id>
+						<goals><goal>add-source</goal></goals>
+						<configuration>
+							<sources>
+								<source>src/main/${jaxrs.impl}</source>
+							</sources>
+						</configuration>
+					</execution>
+					<execution>
+						<id>add-test-src</id>
+						<goals><goal>add-test-source</goal></goals>
+						<configuration>
+							<sources>
+								<source>src/test/${jaxrs.impl}</source>
+							</sources>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<classifier>${jaxrs.impl}</classifier>
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<version>3.0.0-M5</version>
+				<dependencies>
+					<dependency>
+						<groupId>org.junit.jupiter</groupId>
+						<artifactId>junit-jupiter-engine</artifactId>
+						<version>${junit.version}</version>
+					</dependency>
+				</dependencies>
+			</plugin>
+			<plugin>
+				<artifactId>maven-failsafe-plugin</artifactId>
+				<version>3.0.0-M5</version>
+				<dependencies>
+					<dependency>
+						<groupId>org.junit.jupiter</groupId>
+						<artifactId>junit-jupiter-engine</artifactId>
+						<version>${junit.version}</version>
+					</dependency>
+				</dependencies>
+			</plugin>
+			<plugin>
+				<artifactId>maven-javadoc-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>javadoc</id>
+						<phase>package</phase>
+						<goals><goal>jar</goal></goals>
+						<configuration>
+							<show>public</show>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+	<profiles>
+		<profile>
+			<id>jaxrs-jersey</id>
+			<activation>
+				<activeByDefault>true</activeByDefault>
+				<property>
+					<name>jaxrs.impl</name>
+					<value>jersey</value>
+				</property>
+			</activation>
+			<properties>
+				<jaxrs.impl>jersey</jaxrs.impl>
+			</properties>
+			<dependencies>
+				<dependency>
+					<groupId>org.glassfish.jersey.inject</groupId>
+					<artifactId>jersey-hk2</artifactId>
+					<version>${jersey.version}</version>
+					<scope>test</scope>
+				</dependency>
+				<dependency>
+					<groupId>org.glassfish.jersey.media</groupId>
+					<artifactId>jersey-media-json-jackson</artifactId>
+					<version>${jersey.version}</version>
+					<scope>test</scope>
+				</dependency>
+			</dependencies>
+		</profile>
+		<profile>
+			<id>jaxrs-cxf</id>
+			<activation>
+				<property>
+					<name>jaxrs.impl</name>
+					<value>cxf</value>
+				</property>
+			</activation>
+			<properties>
+				<jaxrs.impl>cxf</jaxrs.impl>
+			</properties>
+			<dependencies>
+				<dependency>
+					<groupId>org.apache.cxf</groupId>
+					<artifactId>cxf-rt-rs-client</artifactId>
+					<version>${cxf.version}</version>
+					<scope>provided</scope>
+				</dependency>
+			</dependencies>
+		</profile>
+	</profiles>
+	
+	<repositories>
+		<repository>
+			<id>inteligr8-releases</id>
+			<url>https://repos.inteligr8.com/nexus/repository/inteligr8-public</url>
+			<releases><enabled>true</enabled></releases>
+			<snapshots><enabled>false</enabled></snapshots>
+		</repository>
+		<repository>
+			<id>inteligr8-snapshots</id>
+			<url>https://repos.inteligr8.com/nexus/repository/inteligr8-snapshots</url>
+			<releases><enabled>false</enabled></releases>
+			<snapshots><enabled>true</enabled></snapshots>
+		</repository>
+	</repositories>
+	
+	<distributionManagement>
+		<repository>
+			<id>inteligr8-releases</id>
+			<url>https://repos.inteligr8.com/nexus/repository/inteligr8-public</url>
+		</repository>
+		<snapshotRepository>
+			<id>inteligr8-snapshots</id>
+			<url>https://repos.inteligr8.com/nexus/repository/inteligr8-snapshots</url>
+		</snapshotRepository>
+	</distributionManagement>
+</project>
diff --git a/src/main/cxf/com/inteligr8/alfresco/acs/AcsClientCxfConfiguration.java b/src/main/cxf/com/inteligr8/alfresco/acs/AcsClientCxfConfiguration.java
new file mode 100644
index 0000000..bfd1b40
--- /dev/null
+++ b/src/main/cxf/com/inteligr8/alfresco/acs/AcsClientCxfConfiguration.java
@@ -0,0 +1,32 @@
+package com.inteligr8.alfresco.acs;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+import com.inteligr8.rs.ClientCxfConfiguration;
+
+/**
+ * This class provides a POJO &amp; Spring-based implementation of the
+ * ClientCxfConfiguration interface.  You can use it outside of the Spring
+ * context, but you will need the spring-context and spring-beans libraries in
+ * your non-Spring application.
+ * 
+ * @author brian@inteligr8.com
+ */
+@Configuration
+@ComponentScan
+public class AcsClientCxfConfiguration extends AcsClientConfiguration implements ClientCxfConfiguration {
+
+	@Value("${content.service.cxf.defaultBusEnabled:true}")
+	private boolean defaultBusEnabled;
+	
+	public boolean isDefaultBusEnabled() {
+		return this.defaultBusEnabled;
+	}
+
+	public void setDefaultBusEnabled(boolean defaultBusEnabled) {
+		this.defaultBusEnabled = defaultBusEnabled;
+	}
+
+}
diff --git a/src/main/cxf/com/inteligr8/alfresco/acs/AcsClientCxfImpl.java b/src/main/cxf/com/inteligr8/alfresco/acs/AcsClientCxfImpl.java
new file mode 100644
index 0000000..4931c45
--- /dev/null
+++ b/src/main/cxf/com/inteligr8/alfresco/acs/AcsClientCxfImpl.java
@@ -0,0 +1,29 @@
+package com.inteligr8.alfresco.acs;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import com.inteligr8.rs.ClientCxfImpl;
+
+/**
+ * This class provides a POJO &amp; Spring-based implementation of the Apache
+ * CXF client.  You can use it outside of the Spring context, but you will need
+ * the spring-context and spring-beans libraries in your non-Spring
+ * application.
+ * 
+ * @author brian@inteligr8.com
+ */
+@Component("acs.client.cxf")
+@Lazy
+public class AcsClientCxfImpl extends ClientCxfImpl {
+	
+	/**
+	 * This constructor is for Spring or POJO use
+	 */
+	@Autowired
+	public AcsClientCxfImpl(AcsClientCxfConfiguration config) {
+		super(config);
+	}
+
+}
diff --git a/src/main/cxf/com/inteligr8/alfresco/acs/AcsPublicRestApiCxfImpl.java b/src/main/cxf/com/inteligr8/alfresco/acs/AcsPublicRestApiCxfImpl.java
new file mode 100644
index 0000000..9144701
--- /dev/null
+++ b/src/main/cxf/com/inteligr8/alfresco/acs/AcsPublicRestApiCxfImpl.java
@@ -0,0 +1,25 @@
+package com.inteligr8.alfresco.acs;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class provides the Apache CXF client to the JAX-RS API for the ACS
+ * Public ReST API.
+ * 
+ * @author brian@inteligr8.com
+ */
+@Component("acs.api.cxf")
+@Lazy
+public class AcsPublicRestApiCxfImpl extends AcsPublicRestApiImpl implements AcsPublicRestCxfApi {
+
+	/**
+	 * This constructor is for Spring or POJO use
+	 */
+	@Autowired
+	public AcsPublicRestApiCxfImpl(AcsClientCxfImpl client) {
+		super(client);
+	}
+
+}
diff --git a/src/main/java/com/inteligr8/alfresco/acs/AcsClientConfiguration.java b/src/main/java/com/inteligr8/alfresco/acs/AcsClientConfiguration.java
new file mode 100644
index 0000000..8781e29
--- /dev/null
+++ b/src/main/java/com/inteligr8/alfresco/acs/AcsClientConfiguration.java
@@ -0,0 +1,138 @@
+package com.inteligr8.alfresco.acs;
+
+import org.springframework.beans.factory.annotation.Value;
+
+import com.inteligr8.rs.ClientConfiguration;
+
+/**
+ * This class provides a POJO &amp; Spring-based implementation of the
+ * ClientConfiguration interface.  You can use it outside of the Spring
+ * context, but you will need the spring-context and spring-beans libraries in
+ * your non-Spring application.
+ * 
+ * @author brian@inteligr8.com
+ */
+public abstract class AcsClientConfiguration implements ClientConfiguration {
+
+	@Value("${content.service.baseUrl:http://localhost:8080/alfresco}")
+	private String baseUrl;
+
+	@Value("${content.service.security.basicAuth.username:admin}")
+	private String basicAuthUsername;
+
+	@Value("${content.service.security.basicAuth.password:admin}")
+	private String basicAuthPassword;
+
+	@Value("${content.service.security.bearerToken:#{null}}")
+	private String bearerToken;
+
+	@Value("${content.service.security.oauth.tokenUrl:#{null}}")
+	private String oAuthTokenUrl;
+
+	@Value("${content.service.security.oauth.clientId:#{null}}")
+	private String oAuthClientId;
+
+	@Value("${content.service.security.oauth.clientSecret:#{null}}")
+	private String oAuthClientSecret;
+
+	@Value("${content.service.security.oauth.authCode:#{null}}")
+	private String oAuthAuthCode;
+
+	@Value("${content.service.security.oauth.authRedirectUri:#{null}}")
+	private String oAuthAuthRedirectUri;
+
+	@Value("${content.service.security.oauth.grantUsername:#{null}}")
+	private String oAuthUsername;
+
+	@Value("${content.service.security.oauth.grantPassword:#{null}}")
+	private String oAuthPassword;
+
+	public String getBaseUrl() {
+		return this.baseUrl;
+	}
+	
+	public void setBaseUrl(String baseUrl) {
+		this.baseUrl = baseUrl;
+	}
+	
+	public String getBasicAuthUsername() {
+		return this.basicAuthUsername;
+	}
+
+	public void setBasicAuthUsername(String basicAuthUsername) {
+		this.basicAuthUsername = basicAuthUsername;
+	}
+	
+	public String getBasicAuthPassword() {
+		return this.basicAuthPassword;
+	}
+
+	public void setBasicAuthPassword(String basicAuthPassword) {
+		this.basicAuthPassword = basicAuthPassword;
+	}
+	
+	public String getBearerToken() {
+		return this.bearerToken;
+	}
+
+	public void setBearerToken(String bearerToken) {
+		this.bearerToken = bearerToken;
+	}
+	
+	public String getOAuthTokenUrl() {
+		return this.oAuthTokenUrl;
+	}
+
+	public void setOAuthTokenUrl(String oAuthTokenUrl) {
+		this.oAuthTokenUrl = oAuthTokenUrl;
+	}
+	
+	public String getOAuthClientId() {
+		return this.oAuthClientId;
+	}
+
+	public void setOAuthClientId(String oAuthClientId) {
+		this.oAuthClientId = oAuthClientId;
+	}
+	
+	public String getOAuthClientSecret() {
+		return this.oAuthClientSecret;
+	}
+
+	public void setOAuthClientSecret(String oAuthClientSecret) {
+		this.oAuthClientSecret = oAuthClientSecret;
+	}
+	
+	public String getOAuthAuthCode() {
+		return this.oAuthAuthCode;
+	}
+
+	public void setOAuthAuthCode(String oAuthAuthCode) {
+		this.oAuthAuthCode = oAuthAuthCode;
+	}
+	
+	public String getOAuthAuthRedirectUri() {
+		return this.oAuthAuthRedirectUri;
+	}
+
+	public void setOAuthAuthRedirectUri(String oAuthAuthRedirectUri) {
+		this.oAuthAuthRedirectUri = oAuthAuthRedirectUri;
+	}
+	
+	public String getOAuthUsername() {
+		return this.oAuthUsername;
+	}
+
+	public void setOAuthUsername(String oAuthUsername) {
+		this.oAuthUsername = oAuthUsername;
+	}
+	
+	public String getOAuthPassword() {
+		return this.oAuthPassword;
+	}
+
+	public void setOAuthPassword(String oAuthPassword) {
+		this.oAuthPassword = oAuthPassword;
+	}
+
+}
diff --git a/src/main/java/com/inteligr8/alfresco/acs/AcsPublicRestApiImpl.java b/src/main/java/com/inteligr8/alfresco/acs/AcsPublicRestApiImpl.java
new file mode 100644
index 0000000..1ff37ff
--- /dev/null
+++ b/src/main/java/com/inteligr8/alfresco/acs/AcsPublicRestApiImpl.java
@@ -0,0 +1,27 @@
+package com.inteligr8.alfresco.acs;
+
+import com.inteligr8.rs.Client;
+
+/**
+ * This class serves as the entrypoint to the JAX-RS API for the ACS Public
+ * ReST API.
+ * 
+ * @author brian@inteligr8.com
+ */
+public class AcsPublicRestApiImpl implements AcsPublicRestApi {
+	
+	private Client client;
+	
+	public AcsPublicRestApiImpl(Client client) {
+		this.client = client;
+	}
+	
+	public Client getClient() {
+		return this.client;
+	}
+	
+	public final <T> T getApi(Class<T> apiClass) {
+		return this.client.getApi(apiClass);
+	}
+
+}
diff --git a/src/main/jersey/com/inteligr8/alfresco/acs/AcsClientJerseyConfiguration.java b/src/main/jersey/com/inteligr8/alfresco/acs/AcsClientJerseyConfiguration.java
new file mode 100644
index 0000000..b70cbc5
--- /dev/null
+++ b/src/main/jersey/com/inteligr8/alfresco/acs/AcsClientJerseyConfiguration.java
@@ -0,0 +1,32 @@
+package com.inteligr8.alfresco.acs;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+import com.inteligr8.rs.ClientJerseyConfiguration;
+
+/**
+ * This class provides a POJO &amp; Spring-based implementation of the
+ * ClientJerseyConfiguration interface.  You can use it outside of the Spring
+ * context, but you will need the spring-context and spring-beans libraries in
+ * your non-Spring application.
+ * 
+ * @author brian@inteligr8.com
+ */
+@Configuration
+@ComponentScan
+public class AcsClientJerseyConfiguration extends AcsClientConfiguration implements ClientJerseyConfiguration {
+	
+	@Value("${content.service.jersey.putBodyRequired:true}")
+	private boolean putBodyRequired;
+	
+	public boolean isPutBodyRequired() {
+		return this.putBodyRequired;
+	}
+
+	public void setPutBodyRequired(boolean putBodyRequired) {
+		this.putBodyRequired = putBodyRequired;
+	}
+
+}
diff --git a/src/main/jersey/com/inteligr8/alfresco/acs/AcsClientJerseyImpl.java b/src/main/jersey/com/inteligr8/alfresco/acs/AcsClientJerseyImpl.java
new file mode 100644
index 0000000..d7f31a1
--- /dev/null
+++ b/src/main/jersey/com/inteligr8/alfresco/acs/AcsClientJerseyImpl.java
@@ -0,0 +1,29 @@
+package com.inteligr8.alfresco.acs;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import com.inteligr8.rs.ClientJerseyImpl;
+
+/**
+ * This class provides a POJO &amp; Spring-based implementation of the Jersey
+ * client configured for APS.  You can use it outside of the Spring context,
+ * but you will need the spring-context and spring-beans libraries in your
+ * non-Spring application.
+ * 
+ * @author brian@inteligr8.com
+ */
+@Component("acs.client.jersey")
+@Lazy
+public class AcsClientJerseyImpl extends ClientJerseyImpl {
+	
+	/**
+	 * This constructor is for Spring and POJO use
+	 */
+	@Autowired
+	public AcsClientJerseyImpl(AcsClientJerseyConfiguration config) {
+		super(config);
+	}
+
+}
diff --git a/src/main/jersey/com/inteligr8/alfresco/acs/AcsPublicRestApiJerseyImpl.java b/src/main/jersey/com/inteligr8/alfresco/acs/AcsPublicRestApiJerseyImpl.java
new file mode 100644
index 0000000..2ef9c15
--- /dev/null
+++ b/src/main/jersey/com/inteligr8/alfresco/acs/AcsPublicRestApiJerseyImpl.java
@@ -0,0 +1,25 @@
+package com.inteligr8.alfresco.acs;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class provides the Jersey client to the JAX-RS API for the ACS Public
+ * ReST API.
+ * 
+ * @author brian@inteligr8.com
+ */
+@Component("acs.api.jersey")
+@Lazy
+public class AcsPublicRestApiJerseyImpl extends AcsPublicRestApiImpl implements AcsPublicRestJerseyApi {
+
+	/**
+	 * This constructor is for Spring or POJO use
+	 */
+	@Autowired
+	public AcsPublicRestApiJerseyImpl(AcsClientJerseyImpl client) {
+		super(client);
+	}
+
+}
diff --git a/src/test/cxf/META-INF/services/javax.ws.rs.client.ClientBuilder b/src/test/cxf/META-INF/services/javax.ws.rs.client.ClientBuilder
new file mode 100644
index 0000000..018e11f
--- /dev/null
+++ b/src/test/cxf/META-INF/services/javax.ws.rs.client.ClientBuilder
@@ -0,0 +1 @@
+org.apache.cxf.jaxrs.client.spec.ClientBuilderImpl
\ No newline at end of file
diff --git a/src/test/cxf/com/inteligr8/alfresco/acs/ConnectionCxfClientIT.java b/src/test/cxf/com/inteligr8/alfresco/acs/ConnectionCxfClientIT.java
new file mode 100644
index 0000000..a37ad9c
--- /dev/null
+++ b/src/test/cxf/com/inteligr8/alfresco/acs/ConnectionCxfClientIT.java
@@ -0,0 +1,28 @@
+package com.inteligr8.alfresco.acs;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import com.inteligr8.rs.ClientConfiguration;
+
+@TestPropertySource(locations = {"/local.properties"})
+@SpringJUnitConfig(classes = {AcsClientCxfConfiguration.class, AcsPublicRestApiCxfImpl.class, AcsClientCxfImpl.class})
+public class ConnectionCxfClientIT extends ConnectionClientIT {
+	
+	@Autowired
+	@Qualifier("acs.api.cxf")
+	private AcsPublicRestApiImpl api;
+	
+	@Override
+	public AcsPublicRestApi getApi() {
+		return this.api;
+	}
+	
+	@Override
+	public ClientConfiguration getConfiguration() {
+		return this.api.getClient().getConfig();
+	}
+	
+}
diff --git a/src/test/cxf/com/inteligr8/alfresco/acs/CxfUploadIT.java b/src/test/cxf/com/inteligr8/alfresco/acs/CxfUploadIT.java
new file mode 100644
index 0000000..f088422
--- /dev/null
+++ b/src/test/cxf/com/inteligr8/alfresco/acs/CxfUploadIT.java
@@ -0,0 +1,57 @@
+package com.inteligr8.alfresco.acs;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Collections;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIf;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import com.inteligr8.alfresco.acs.model.Node;
+import com.inteligr8.alfresco.acs.model.NodeBodyCreate;
+import com.inteligr8.alfresco.acs.model.NodeBodyCreateMultipartCxf;
+import com.inteligr8.rs.ClientConfiguration;
+
+@TestPropertySource(locations = {"/local.properties"})
+@SpringJUnitConfig(classes = {AcsClientCxfConfiguration.class, AcsPublicRestApiCxfImpl.class, AcsClientCxfImpl.class})
+public class CxfUploadIT extends UploadIT {
+	
+	@Autowired
+	@Qualifier("acs.api.cxf")
+	private AcsPublicRestApiCxfImpl api;
+	
+	@Override
+	public AcsPublicRestApi getApi() {
+		return this.api;
+	}
+	
+	@Override
+	public ClientConfiguration getConfiguration() {
+		return this.api.getClient().getConfig();
+	}
+	
+	@Test
+	@EnabledIf("hostExists")
+	public void uploadFile() throws IOException {
+		String folderNodeId = this.getSharedFolder();
+		
+		NodeBodyCreate nodeBody = new NodeBodyCreate().nodeType("trx:transferReport").name("test-name1.txt")
+				.properties(Collections.singletonMap("cm:author", "Brian"));
+		
+		ByteArrayInputStream istream = new ByteArrayInputStream("This is a test".getBytes());
+		NodeBodyCreateMultipartCxf body = NodeBodyCreateMultipartCxf.from(nodeBody, "test-name2.txt", istream, true, null, null);
+		
+		Node newNode = this.api.getNodesExtApi().createNode(folderNodeId, body).getEntry();
+		Assertions.assertNotNull(newNode);
+		Assertions.assertNotNull(newNode.getId());
+		Assertions.assertEquals(folderNodeId, newNode.getParentId());
+		Assertions.assertEquals(nodeBody.getNodeType(), newNode.getNodeType());
+		Assertions.assertTrue(newNode.getName().startsWith("test-name1"));
+	}
+	
+}
diff --git a/src/test/java/com/inteligr8/alfresco/acs/ConditionalIT.java b/src/test/java/com/inteligr8/alfresco/acs/ConditionalIT.java
new file mode 100644
index 0000000..90c6ecd
--- /dev/null
+++ b/src/test/java/com/inteligr8/alfresco/acs/ConditionalIT.java
@@ -0,0 +1,35 @@
+package com.inteligr8.alfresco.acs;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.methods.RequestBuilder;
+import org.apache.http.impl.client.DefaultRedirectStrategy;
+import org.apache.http.impl.client.HttpClientBuilder;
+
+import com.inteligr8.rs.ClientConfiguration;
+
+public abstract class ConditionalIT {
+	
+	public abstract ClientConfiguration getConfiguration();
+	
+	public boolean hostExists() {
+		String uri = this.getConfiguration().getBaseUrl();
+		
+		HttpUriRequest request = RequestBuilder.get()
+				.setUri(uri)
+				.build();
+		
+		HttpClient client = HttpClientBuilder.create()
+				.setRedirectStrategy(DefaultRedirectStrategy.INSTANCE)
+				.build();
+		
+		try {
+			HttpResponse response = client.execute(request);
+			return response.getStatusLine().getStatusCode() < 300;
+		} catch (Exception e) {
+			return false;
+		}
+	}
+
+}
diff --git a/src/test/java/com/inteligr8/alfresco/acs/ConnectionClientIT.java b/src/test/java/com/inteligr8/alfresco/acs/ConnectionClientIT.java
new file mode 100644
index 0000000..7196c3f
--- /dev/null
+++ b/src/test/java/com/inteligr8/alfresco/acs/ConnectionClientIT.java
@@ -0,0 +1,94 @@
+package com.inteligr8.alfresco.acs;
+
+import java.util.List;
+import java.util.UUID;
+
+import javax.ws.rs.NotFoundException;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIf;
+
+import com.inteligr8.alfresco.acs.api.DiscoveryApi;
+import com.inteligr8.alfresco.acs.api.NodesApi;
+import com.inteligr8.alfresco.acs.api.V0Api;
+import com.inteligr8.alfresco.acs.model.RepositoryInfo;
+import com.inteligr8.alfresco.acs.model.v0.ClassInfo;
+import com.inteligr8.alfresco.acs.model.v0.MimeTypesData;
+import com.inteligr8.alfresco.acs.model.v0.PropertyInfo;
+import com.inteligr8.alfresco.acs.model.v0.ServerData;
+
+public abstract class ConnectionClientIT extends ConditionalIT {
+	
+	public abstract AcsPublicRestApi getApi();
+	
+	@Test
+	@EnabledIf("hostExists")
+	public void testDiscovery() {
+		DiscoveryApi api = this.getApi().getDiscoveryApi();
+		RepositoryInfo repoInfo = api.getRepositoryInformation().getEntry().getRepository();
+		
+		Assertions.assertNotNull(repoInfo);
+		Assertions.assertFalse(repoInfo.getStatus().isIsReadOnly());
+		Assertions.assertEquals("6", repoInfo.getVersion().getMajor());
+	}
+	
+	@Test
+	@EnabledIf("hostExists")
+	public void testMissingNode() {
+		NodesApi api = this.getApi().getNodesApi();
+		
+		Assertions.assertThrows(NotFoundException.class, () -> {
+			api.getNode(UUID.randomUUID().toString(), null, null, null);
+		});
+	}
+	
+	@Test
+	@EnabledIf("hostExists")
+	public void testV0ServerInfo() {
+		V0Api api = this.getApi().getV0Api();
+		ServerData server = api.getServer();
+		
+		Assertions.assertNotNull(server);
+		Assertions.assertNotNull(server.getData());
+		Assertions.assertNotNull(server.getData().getEdition());
+		Assertions.assertTrue(server.getData().getEdition().length() > 5);
+		Assertions.assertTrue(server.getData().getVersion().length() > 5);
+		Assertions.assertTrue(server.getData().getSchema().length() > 3);
+	}
+	
+	@Test
+	@EnabledIf("hostExists")
+	public void testV0MimeTypes() {
+		V0Api api = this.getApi().getV0Api();
+		MimeTypesData mimeTypes = api.getMimeTypes();
+		
+		Assertions.assertNotNull(mimeTypes);
+		Assertions.assertNotNull(mimeTypes.getData());
+		Assertions.assertTrue(mimeTypes.getData().getMimeTypes().size() > 50);
+		Assertions.assertNotNull(mimeTypes.getData().getMimeTypeInfo("application/pdf"));
+		Assertions.assertEquals("pdf", mimeTypes.getData().getMimeTypeInfo("application/pdf").getExtensions().getDefault());
+	}
+	
+	@Test
+	@EnabledIf("hostExists")
+	public void testV0Classes() {
+		V0Api api = this.getApi().getV0Api();
+		List<ClassInfo> classes = api.getClasses(null, null, null);
+		
+		Assertions.assertNotNull(classes);
+		Assertions.assertTrue(classes.size() > 50);
+	}
+	
+	@Test
+	@EnabledIf("hostExists")
+	public void testV0Property() {
+		V0Api api = this.getApi().getV0Api();
+		List<PropertyInfo> props = api.getProperties(null, "cm:content", null);
+		
+		Assertions.assertNotNull(props);
+		Assertions.assertEquals(1, props.size());
+		Assertions.assertEquals("d:content", props.iterator().next().getDataType());
+	}
+	
+}
diff --git a/src/test/java/com/inteligr8/alfresco/acs/UploadIT.java b/src/test/java/com/inteligr8/alfresco/acs/UploadIT.java
new file mode 100644
index 0000000..4bf00a7
--- /dev/null
+++ b/src/test/java/com/inteligr8/alfresco/acs/UploadIT.java
@@ -0,0 +1,26 @@
+package com.inteligr8.alfresco.acs;
+
+import com.inteligr8.alfresco.acs.model.RequestQuery;
+import com.inteligr8.alfresco.acs.model.RequestQuery.LanguageEnum;
+import com.inteligr8.alfresco.acs.model.ResultNode;
+import com.inteligr8.alfresco.acs.model.ResultSetPaging;
+import com.inteligr8.alfresco.acs.model.SearchRequest;
+
+public abstract class UploadIT extends ConditionalIT {
+	
+	public abstract AcsPublicRestApi getApi();
+	
+	public String getSharedFolder() {
+		RequestQuery query = new RequestQuery();
+		query.setLanguage(LanguageEnum.AFTS);
+		query.setQuery("=@cm:name:'Shared'");
+		
+		SearchRequest searchRequest = new SearchRequest();
+		searchRequest.setQuery(query);
+		
+		ResultSetPaging paging = this.getApi().getSearchApi().search(searchRequest);
+		ResultNode folderNode = paging.getList().getEntries().iterator().next().getEntry();
+		return folderNode.getId();
+	}
+	
+}
diff --git a/src/test/jersey/META-INF/services/javax.ws.rs.client.ClientBuilder b/src/test/jersey/META-INF/services/javax.ws.rs.client.ClientBuilder
new file mode 100644
index 0000000..48b9fa5
--- /dev/null
+++ b/src/test/jersey/META-INF/services/javax.ws.rs.client.ClientBuilder
@@ -0,0 +1 @@
+org.glassfish.jersey.client.JerseyClientBuilder
\ No newline at end of file
diff --git a/src/test/jersey/com/inteligr8/alfresco/acs/ConnectionJerseyClientIT.java b/src/test/jersey/com/inteligr8/alfresco/acs/ConnectionJerseyClientIT.java
new file mode 100644
index 0000000..e282036
--- /dev/null
+++ b/src/test/jersey/com/inteligr8/alfresco/acs/ConnectionJerseyClientIT.java
@@ -0,0 +1,28 @@
+package com.inteligr8.alfresco.acs;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import com.inteligr8.rs.ClientConfiguration;
+
+@TestPropertySource(locations = {"/local.properties"})
+@SpringJUnitConfig(classes = {AcsClientJerseyConfiguration.class, AcsPublicRestApiJerseyImpl.class, AcsClientJerseyImpl.class})
+public class ConnectionJerseyClientIT extends ConnectionClientIT {
+	
+	@Autowired
+	@Qualifier("acs.api.jersey")
+	private AcsPublicRestApiImpl api;
+	
+	@Override
+	public AcsPublicRestApi getApi() {
+		return this.api;
+	}
+	
+	@Override
+	public ClientConfiguration getConfiguration() {
+		return this.api.getClient().getConfig();
+	}
+	
+}
diff --git a/src/test/jersey/com/inteligr8/alfresco/acs/JerseyUploadIT.java b/src/test/jersey/com/inteligr8/alfresco/acs/JerseyUploadIT.java
new file mode 100644
index 0000000..f3a1341
--- /dev/null
+++ b/src/test/jersey/com/inteligr8/alfresco/acs/JerseyUploadIT.java
@@ -0,0 +1,58 @@
+package com.inteligr8.alfresco.acs;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Collections;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIf;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import com.inteligr8.alfresco.acs.model.Node;
+import com.inteligr8.alfresco.acs.model.NodeBodyCreate;
+import com.inteligr8.alfresco.acs.model.NodeBodyCreateMultipartJersey;
+import com.inteligr8.rs.ClientConfiguration;
+
+@TestPropertySource(locations = {"/local.properties"})
+@SpringJUnitConfig(classes = {AcsClientJerseyConfiguration.class, AcsPublicRestApiJerseyImpl.class, AcsClientJerseyImpl.class})
+public class JerseyUploadIT extends UploadIT {
+	
+	@Autowired
+	@Qualifier("acs.api.jersey")
+	private AcsPublicRestApiJerseyImpl api;
+	
+	@Override
+	public AcsPublicRestApi getApi() {
+		return this.api;
+	}
+	
+	@Override
+	public ClientConfiguration getConfiguration() {
+		return this.api.getClient().getConfig();
+	}
+	
+	@Test
+	@EnabledIf("hostExists")
+	public void uploadFile() throws IOException, ParseException {
+		String folderNodeId = this.getSharedFolder();
+		
+		NodeBodyCreate nodeBody = new NodeBodyCreate().nodeType("trx:transferReport").name("test-name1.txt")
+				.properties(Collections.singletonMap("cm:author", "Brian"));
+		
+		ByteArrayInputStream istream = new ByteArrayInputStream("This is a test".getBytes());
+		NodeBodyCreateMultipartJersey body = NodeBodyCreateMultipartJersey.from(nodeBody, "test-name2.txt", istream, true, null, null);
+		
+		Node newNode = this.api.getNodesExtApi().createNode(folderNodeId, body).getEntry();
+		Assertions.assertNotNull(newNode);
+		Assertions.assertNotNull(newNode.getId());
+		Assertions.assertEquals(folderNodeId, newNode.getParentId());
+		Assertions.assertEquals(nodeBody.getNodeType(), newNode.getNodeType());
+		Assertions.assertTrue(newNode.getName().startsWith("test-name1"));
+	}
+	
+}
diff --git a/src/test/resources/local-oauth.properties b/src/test/resources/local-oauth.properties
new file mode 100644
index 0000000..c9ea910
--- /dev/null
+++ b/src/test/resources/local-oauth.properties
@@ -0,0 +1,5 @@
+content.service.baseUrl=http://localhost:8080/alfresco
+content.service.security.oauth.tokenUrl=http://auth.example.org:8080/auth/realms/alfresco/protocol/openid-connect/token
+content.service.security.oauth.clientId=alfresco
+content.service.security.oauth.grantUsername=admin
+content.service.security.oauth.grantPassword=admin
diff --git a/src/test/resources/local.properties b/src/test/resources/local.properties
new file mode 100644
index 0000000..17bb34c
--- /dev/null
+++ b/src/test/resources/local.properties
@@ -0,0 +1,3 @@
+content.service.baseUrl=http://localhost:8080/alfresco
+content.service.security.basicAuth.username=admin
+content.service.security.basicAuth.password=admin
diff --git a/src/test/resources/log4j2.properties b/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..df444d4
--- /dev/null
+++ b/src/test/resources/log4j2.properties
@@ -0,0 +1,19 @@
+rootLogger.level=trace
+rootLogger.appenderRef.stdout.ref=STDOUT
+
+logger.spring.name=org.springframework
+logger.spring.level=info
+
+logger.common-rest-api.name=com.inteligr8.rs
+logger.common-rest-api.level=trace
+
+logger.this.name=com.inteligr8.alfresco.acs
+logger.this.level=trace
+
+logger.jaxrslog.name=jaxrs.request
+logger.jaxrslog.level=trace
+
+appender.stdout.type=Console
+appender.stdout.name=STDOUT
+appender.stdout.layout.type=PatternLayout
+appender.stdout.layout.pattern=%C [%t] %m%n
diff --git a/src/test/vscode/createNode.http b/src/test/vscode/createNode.http
new file mode 100644
index 0000000..99f9ed1
--- /dev/null
+++ b/src/test/vscode/createNode.http
@@ -0,0 +1,53 @@
+@baseUrl = http://localhost:8080/alfresco
+@username = admin
+@password = admin
+
+### Find Folder
+# @name find
+POST {{baseUrl}}/api/-default-/public/search/versions/1/search HTTP/1.1
+Authorization: Basic {{username}}:{{password}}
+
+{
+    "query": {
+        "language": "afts",
+        "query": "=@cm:name:'Shared'"
+    }
+}
+
+@folderNodeId = {{find.response.body.list.entries[0].entry.id}}
+
+### Upload File
+# @name upload
+POST {{baseUrl}}/api/-default-/public/alfresco/versions/1/nodes/{{folderNodeId}}/children?autoRename=true
+Authorization: Basic {{username}}:{{password}}
+Content-type: application/json
+
+{
+    "name": "TestFolder",
+    "nodeType": "cm:content",
+    "properties": null
+}
+
+### Upload File
+# @name upload-form
+POST {{baseUrl}}/api/-default-/public/alfresco/versions/1/nodes/{{folderNodeId}}/children
+Authorization: Basic {{username}}:{{password}}
+Content-type: multipart/form-data; boundary=----Test
+
+------Test
+Content-disposition: form-data; name="name"
+
+TestFolder
+------Test
+Content-disposition: form-data; name="nodeType"
+
+cm:content
+------Test
+Content-disposition: form-data; name="autoRename"
+
+true
+------Test
+Content-disposition: form-data; name="filedata"; filename="test.txt"
+
+This is a test
+------Test--
\ No newline at end of file