dneuman64 closed pull request #2289: Foundation for Java Api Client
URL: https://github.com/apache/trafficcontrol/pull/2289
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/traffic_control/clients/java/.gitignore 
b/traffic_control/clients/java/.gitignore
new file mode 100644
index 000000000..7f078166e
--- /dev/null
+++ b/traffic_control/clients/java/.gitignore
@@ -0,0 +1,5 @@
+.settings
+.project
+.classpath
+.factorypath
+target
diff --git a/traffic_control/clients/java/CONTRIBUTE.md 
b/traffic_control/clients/java/CONTRIBUTE.md
new file mode 100644
index 000000000..563d34d34
--- /dev/null
+++ b/traffic_control/clients/java/CONTRIBUTE.md
@@ -0,0 +1,41 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+-->
+
+# IDE Setup
+
+Most all of the code contained uses Builders for implementation. This ensures 
all properties are managed and defaults are used. As well as facilitate YAML 
based construction. 
+Most all of these builders are constructed using Google's AutoValue library. 
This allows for auto code generation based on annotations and abstract classes. 
+To support this within your IDE you will need to do a couple things listed 
below.
+
+## Eclipse
+
+1. Install m2e-apt from the Eclipse marketplace. Help -> Eclipse Marketplace 
-> Search "m2 apt" -> Install m2e-apt
+2. Activate the apt processing. Preferences -> Maven -> Annotation processing 
-> Switch to Experimental
+3. Import project or if it has already been imported refresh the projects form 
the maven sub-menu.
+
+## IntelliJ
+
+1. Open Annotation Processors settings. Settings -> Build, Execution, 
Deployment -> Compiler -> Annotation Processors
+2. Select the following buttons:
+   * Enable annotation processing
+   * Obtain processors from project classpath
+   * Store generated sources relative to: Module content root
+3. Set the generated source directories to be equal to the Maven directories:
+   * Set “Production sources directory:” to 
t"arget/generated-sources/annotations"
+   * Set “Test sources directory:” to 
"target/generated-test-sources/test-annotations"
\ No newline at end of file
diff --git a/traffic_control/clients/java/README.md 
b/traffic_control/clients/java/README.md
new file mode 100644
index 000000000..013ae5bde
--- /dev/null
+++ b/traffic_control/clients/java/README.md
@@ -0,0 +1,28 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+-->
+
+# Java API Framework
+
+Simple framework for Rest based API communication. 
+
+See [TrafficOps Readme](trafficops/README.md) for example and usage with 
communicating with the TrafficOps API
+
+## Development
+
+See [Contribute](CONTRIBUTE.md) for information on setting up your IDE.
diff --git a/traffic_control/clients/java/common/.gitignore 
b/traffic_control/clients/java/common/.gitignore
new file mode 100644
index 000000000..b83d22266
--- /dev/null
+++ b/traffic_control/clients/java/common/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/traffic_control/clients/java/common/pom.xml 
b/traffic_control/clients/java/common/pom.xml
new file mode 100644
index 000000000..4e26bc20e
--- /dev/null
+++ b/traffic_control/clients/java/common/pom.xml
@@ -0,0 +1,48 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+-->
+
+<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 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.apache.trafficcontrol</groupId>
+               <artifactId>traffic-control-java-client</artifactId>
+               <version>0.0.1-SNAPSHOT</version>
+       </parent>
+       <artifactId>traffic-control-java-client-common</artifactId>
+       <dependencies>
+               <dependency>
+                       <groupId>org.slf4j</groupId>
+                       <artifactId>slf4j-api</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.httpcomponents</groupId>
+                       <artifactId>httpasyncclient</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>com.google.auto.value</groupId>
+                       <artifactId>auto-value</artifactId>
+                       <version>1.5.2</version>
+               </dependency>
+               <dependency>
+                       <groupId>com.google.guava</groupId>
+                       <artifactId>guava</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git 
a/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/RestApiSession.java
 
b/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/RestApiSession.java
new file mode 100644
index 000000000..e8f1a0198
--- /dev/null
+++ 
b/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/RestApiSession.java
@@ -0,0 +1,180 @@
+/*
+ *
+ * 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.
+ */
+
+package org.apache.trafficcontrol.client;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.config.CookieSpecs;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.methods.RequestBuilder;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.message.BasicHeader;
+import org.apache.trafficcontrol.client.exception.OperationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+
+@AutoValue
+public abstract class RestApiSession implements Closeable {
+       private static final Logger LOG = 
LoggerFactory.getLogger(RestApiSession.class);
+
+       public static final ImmutableList<Header> DEFAULT_HEADERS;
+       static {
+               DEFAULT_HEADERS = ImmutableList.<Header>builder()
+                               .add(new BasicHeader("Content-Type", 
"application/json; charset=UTF-8")).build();
+       };
+       
+       public abstract ImmutableList<Header> defaultHeaders();
+       public abstract Builder toBuilder();
+
+       @AutoValue.Builder
+       public abstract static class Builder {
+               public abstract RestApiSession build();
+
+               public abstract Builder setDefaultHeaders(ImmutableList<Header> 
headers);
+               public abstract ImmutableList.Builder<Header> 
defaultHeadersBuilder();
+       }
+       public static Builder builder() {
+               return new AutoValue_RestApiSession.Builder()
+                               .setDefaultHeaders(DEFAULT_HEADERS);
+       }
+
+       private CloseableHttpAsyncClient httpclient;
+
+       public void open() {
+               if (httpclient == null) {
+                       RequestConfig globalConfig = RequestConfig.custom()
+                                       .setCookieSpec(CookieSpecs.STANDARD) 
//User standard instead of default. Default will result in cookie parse 
exceptions with the Mojolicous cookie
+                                       .setConnectTimeout(5000)
+                                       .build();
+                       CookieStore cookieStore = new BasicCookieStore();
+                       HttpClientContext context = HttpClientContext.create();
+                       context.setCookieStore(cookieStore);
+
+                       httpclient = HttpAsyncClients.custom()
+                                       .setDefaultRequestConfig(globalConfig)
+                                       .setDefaultCookieStore(cookieStore)
+                                       .build();
+               }
+
+               if (!httpclient.isRunning()) {
+                       httpclient.start();
+               }
+       }
+
+       public boolean isRunning() {
+               if (httpclient == null) {
+                       return false;
+               } else {
+                       return httpclient.isRunning();
+               }
+       }
+
+       public void close() throws IOException {
+               if (httpclient != null) {
+                       httpclient.close();
+                       httpclient = null;
+               }
+       }
+       
+       public CompletableFuture<HttpResponse> get(String url) {
+               return execute(RequestBuilder.get(url));
+       }
+       
+       public CompletableFuture<HttpResponse> post(String url, String body) {
+               final HttpEntity e = new StringEntity(body, Charsets.UTF_8);
+               return execute(RequestBuilder.post()
+                               .setUri(url)
+                               .setEntity(e));
+       }
+       
+       public CompletableFuture<HttpResponse> execute(RequestBuilder request) {
+               for(Header h: this.defaultHeaders()) {
+                       request.addHeader(h);
+               }
+               
+               return this.execute(request.build());
+       }
+       
+       private CompletableFuture<HttpResponse> execute(HttpUriRequest request) 
{
+               final CompletableFutureCallback future = new 
CompletableFutureCallback();
+               try {
+                       LOG.debug("Opening RestClient");
+                       this.open();
+                       
+                       LOG.debug("Dispatching request: {}", request);
+                       final Future<HttpResponse> reFuture = 
this.httpclient.execute(request, future);
+                       future.setReFuture(reFuture);
+               } catch(Throwable e) {
+                       future.completeExceptionally(e);
+               }
+               
+               return future;
+       }
+       
+       /**
+        * An adapter around a FutureCallback to provide a CompletableFuture 
for use with HttpAsyncClients.  
+        */
+       private class CompletableFutureCallback extends 
CompletableFuture<HttpResponse> implements FutureCallback<HttpResponse>{
+               private Future<HttpResponse> reFuture;
+               
+               @Override
+               public boolean cancel(boolean mayInterruptIfRunning) {
+                       if(reFuture != null) {
+                               return reFuture.cancel(mayInterruptIfRunning);
+                       }
+                       return false;
+               }
+               
+               @Override
+               public void completed(HttpResponse result) {
+                       LOG.debug("Request Completed: {}", result);
+                       this.complete(result);
+               }
+
+               @Override
+               public void failed(Exception ex) {
+                       LOG.debug("Request Failed", ex);
+                       this.completeExceptionally(ex);
+               }
+
+               @Override
+               public void cancelled() {
+                       LOG.debug("Request Cancelled");
+                       this.completeExceptionally(new OperationException("HTTP 
Request was cancelled"));
+               }
+
+               public void setReFuture(Future<HttpResponse> reFuture) {
+                       this.reFuture = reFuture;
+               }
+       }
+}
diff --git 
a/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/InvalidJsonException.java
 
b/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/InvalidJsonException.java
new file mode 100644
index 000000000..b1a07c730
--- /dev/null
+++ 
b/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/InvalidJsonException.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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.
+ */
+
+package org.apache.trafficcontrol.client.exception;
+
+public class InvalidJsonException extends TrafficControlException {
+       private static final long serialVersionUID = 1884362711438565843L;
+
+       public InvalidJsonException() {
+               super();
+       }
+
+       public InvalidJsonException(String message, Throwable cause, boolean 
enableSuppression, boolean writableStackTrace) {
+               super(message, cause, enableSuppression, writableStackTrace);
+       }
+
+       public InvalidJsonException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+       public InvalidJsonException(String message) {
+               super(message);
+       }
+
+       public InvalidJsonException(Throwable cause) {
+               super(cause);
+       }
+       
+}
diff --git 
a/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/LoginException.java
 
b/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/LoginException.java
new file mode 100644
index 000000000..ad172ec97
--- /dev/null
+++ 
b/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/LoginException.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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.
+ */
+
+package org.apache.trafficcontrol.client.exception;
+
+public class LoginException extends TrafficControlException {
+       private static final long serialVersionUID = -5021620597469977194L;
+
+       public LoginException() {
+               super();
+       }
+
+       public LoginException(String message, Throwable cause, boolean 
enableSuppression, boolean writableStackTrace) {
+               super(message, cause, enableSuppression, writableStackTrace);
+       }
+
+       public LoginException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+       public LoginException(String message) {
+               super(message);
+       }
+
+       public LoginException(Throwable cause) {
+               super(cause);
+       }
+       
+}
diff --git 
a/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/OperationException.java
 
b/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/OperationException.java
new file mode 100644
index 000000000..414bae517
--- /dev/null
+++ 
b/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/OperationException.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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.
+ */
+
+package org.apache.trafficcontrol.client.exception;
+
+public class OperationException extends TrafficControlException {
+       private static final long serialVersionUID = 8799467021892976240L;
+
+       public OperationException() {
+               super();
+       }
+
+       public OperationException(String message, Throwable cause, boolean 
enableSuppression, boolean writableStackTrace) {
+               super(message, cause, enableSuppression, writableStackTrace);
+       }
+
+       public OperationException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+       public OperationException(String message) {
+               super(message);
+       }
+
+       public OperationException(Throwable cause) {
+               super(cause);
+       }
+       
+}
diff --git 
a/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/TrafficControlException.java
 
b/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/TrafficControlException.java
new file mode 100644
index 000000000..ad9142f9e
--- /dev/null
+++ 
b/traffic_control/clients/java/common/src/main/java/org/apache/trafficcontrol/client/exception/TrafficControlException.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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.
+ */
+
+package org.apache.trafficcontrol.client.exception;
+
+public class TrafficControlException extends Exception {
+       private static final long serialVersionUID = 914940907727369814L;
+
+       public TrafficControlException() {
+               super();
+       }
+
+       public TrafficControlException(String message, Throwable cause, boolean 
enableSuppression, boolean writableStackTrace) {
+               super(message, cause, enableSuppression, writableStackTrace);
+       }
+
+       public TrafficControlException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+       public TrafficControlException(String message) {
+               super(message);
+       }
+
+       public TrafficControlException(Throwable cause) {
+               super(cause);
+       }
+       
+}
diff --git a/traffic_control/clients/java/pom.xml 
b/traffic_control/clients/java/pom.xml
new file mode 100644
index 000000000..ae9ce558d
--- /dev/null
+++ b/traffic_control/clients/java/pom.xml
@@ -0,0 +1,66 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+-->
+
+<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 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+       <modelVersion>4.0.0</modelVersion>
+       <groupId>org.apache.trafficcontrol</groupId>
+       <artifactId>traffic-control-java-client</artifactId>
+       <version>0.0.1-SNAPSHOT</version>
+       <packaging>pom</packaging>
+       <modules>
+               <module>common</module>
+               <module>trafficops</module>
+       </modules>
+       <properties>
+               <java.version>1.8</java.version>
+       </properties>
+       
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                               <configuration>
+                                       <source>${java.version}</source>
+                                       <target>${java.version}</target>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencyManagement>
+               <dependencies>
+                       <dependency>
+                               <groupId>org.slf4j</groupId>
+                               <artifactId>slf4j-api</artifactId>
+                               <version>1.7.25</version>
+                       </dependency>
+                       <dependency>
+                               <groupId>org.apache.httpcomponents</groupId>
+                               <artifactId>httpasyncclient</artifactId>
+                               <version>4.1.3</version>
+                       </dependency>
+                       <dependency>
+                               <groupId>com.google.guava</groupId>
+                               <artifactId>guava</artifactId>
+                               <version>23.5-jre</version>
+                       </dependency>
+               </dependencies>
+       </dependencyManagement>
+</project>
\ No newline at end of file
diff --git a/traffic_control/clients/java/trafficops/.gitignore 
b/traffic_control/clients/java/trafficops/.gitignore
new file mode 100644
index 000000000..b83d22266
--- /dev/null
+++ b/traffic_control/clients/java/trafficops/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/traffic_control/clients/java/trafficops/README.md 
b/traffic_control/clients/java/trafficops/README.md
new file mode 100644
index 000000000..9bd3e0d91
--- /dev/null
+++ b/traffic_control/clients/java/trafficops/README.md
@@ -0,0 +1,88 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+-->
+
+# TrafficOps Api Client
+
+Simple Java API client for communicating with the TrafficOps API
+
+## Example Usage
+
+### Create Traffic Ops session
+
+**Using a provided URI**
+
+```java
+//Construct TrafficOps Session
+final URI trafficOpsUri = new URI("https://trafficops.mycdn.com:443";);
+final TOSession.Builder toSessionBuilder = TOSession.builder()
+       .fromURI(trafficOpsUri);
+final RestApiSession.Builder restSession = 
toSessionBuilder.restClientBuilder();
+final TOSession toSession = toSessionBuilder.build();
+```
+**Explicitly set properties**
+
+```java
+//Construct TrafficOps Session
+final URI trafficOpsUri = new URI("http://trafficops.mycdn.com:443";);
+final TOSession.Builder toSessionBuilder = TOSession.builder()
+       .setHost("trafficops.mycdn.com")
+       .setPort(443)
+       .setSsl(true);
+final RestApiSession.Builder restSession = 
toSessionBuilder.restClientBuilder();
+final TOSession toSession = toSessionBuilder.build();
+```
+
+### Logging In
+
+```java
+//Login
+final CompletableFuture<Boolean> loginFuture = toSession
+       .login("MyUsername", "MyPassword");
+try {
+       //Timeout if login takes longer then 1sec
+       loginFuture.get(1000, TimeUnit.MILLISECONDS);
+} catch(TimeoutException e) {
+       loginFuture.cancel(true);
+       LOG.error("Timeout while logging in");
+       System.exit(1);
+}
+```
+
+### Getting a list of All Servers
+
+**Synchronously**
+
+```java
+final CollectionResponse response = toSession.getServers().get();
+```
+
+**Asynchronously**
+
+```java
+toSession
+       .getServers()
+       .whenCompleteAsync((servers, exception)->{
+               if(exception != null){
+                       //Handle Exception
+               } else {
+                       //Do something with your server list
+               }
+       });
+
+```
\ No newline at end of file
diff --git a/traffic_control/clients/java/trafficops/pom.xml 
b/traffic_control/clients/java/trafficops/pom.xml
new file mode 100644
index 000000000..a04105d26
--- /dev/null
+++ b/traffic_control/clients/java/trafficops/pom.xml
@@ -0,0 +1,59 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+-->
+
+<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 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.apache.trafficcontrol</groupId>
+               <artifactId>traffic-control-java-client</artifactId>
+               <version>0.0.1-SNAPSHOT</version>
+       </parent>
+       <artifactId>traffic-control-java-client-trafficops</artifactId>
+       <dependencies>
+               <dependency>
+                       <groupId>org.apache.trafficcontrol</groupId>
+                       
<artifactId>traffic-control-java-client-common</artifactId>
+                       <version>${project.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>com.google.code.gson</groupId>
+                       <artifactId>gson</artifactId>
+                       <version>2.8.2</version>
+               </dependency>
+               <dependency>
+                       <groupId>junit</groupId>
+                       <artifactId>junit</artifactId>
+                       <version>4.12</version>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>ch.qos.logback</groupId>
+                       <artifactId>logback-classic</artifactId>
+                       <version>1.1.3</version>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.mockito</groupId>
+                       <artifactId>mockito-core</artifactId>
+                       <version>2.12.0</version>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git 
a/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/ResponseFuture.java
 
b/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/ResponseFuture.java
new file mode 100644
index 000000000..2e96a2151
--- /dev/null
+++ 
b/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/ResponseFuture.java
@@ -0,0 +1,143 @@
+/*
+ *
+ * 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.
+ */
+
+package org.apache.trafficcontrol.client.trafficops;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.BiConsumer;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.RequestBuilder;
+import org.apache.http.entity.StringEntity;
+import org.apache.trafficcontrol.client.RestApiSession;
+import org.apache.trafficcontrol.client.exception.LoginException;
+import org.apache.trafficcontrol.client.exception.OperationException;
+import org.apache.trafficcontrol.client.trafficops.models.Alert;
+import org.apache.trafficcontrol.client.trafficops.models.Response;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Charsets;
+import com.google.common.base.Strings;
+
+@AutoValue
+public abstract class ResponseFuture<T extends Response> extends 
CompletableFuture<T> implements BiConsumer<HttpResponse, Throwable> {
+       public static enum Method{
+               POST, GET
+       }
+       
+       public abstract Optional<BiConsumer<ResponseFuture<T>, Throwable>> 
handleException();
+       public abstract Class<T> responseType();
+       public abstract ResponseFuture.Method method();
+       public abstract URI uri();
+       public abstract RestApiSession session();
+       public abstract Optional<String> body();
+       
+       private CompletableFuture<HttpResponse> subFuture;
+       
+       public static <T extends Response> ResponseFuture.Builder<T> 
builder(Class<T> response) {
+               return new AutoValue_ResponseFuture.Builder<T>()
+                               .setResponseType(response);
+       }
+       public static ResponseFuture.Builder<Response> builder() {
+               return builder(Response.class);
+       }
+       
+       @Override
+       public boolean cancel(boolean mayInterruptIfRunning) {
+               if(subFuture != null) {
+                       return subFuture.cancel(mayInterruptIfRunning);
+               }
+               return false;
+       }
+       
+       public ResponseFuture<T> execute(){
+               RequestBuilder rBuilder = RequestBuilder
+                               .create(this.method().toString())
+                               .setUri(this.uri());
+               if(this.body().isPresent()) {
+                       rBuilder.setEntity(new StringEntity(this.body().get(), 
Charsets.UTF_8));
+               }
+               subFuture = this.session().execute(rBuilder);
+               subFuture.whenComplete(this);
+               return this;
+       }
+       
+       @AutoValue.Builder
+       public abstract static class Builder<T extends Response> {
+               public ResponseFuture<T> build(){
+                       return autoBuild().execute();
+               }
+               protected abstract ResponseFuture<T> autoBuild();
+               public abstract ResponseFuture.Builder<T> 
setHandleException(BiConsumer<ResponseFuture<T>, Throwable> function);
+               public abstract ResponseFuture.Builder<T> 
setResponseType(Class<T> respone);
+               public abstract ResponseFuture.Builder<T> 
setMethod(ResponseFuture.Method method);
+               public abstract ResponseFuture.Builder<T> setUri(URI uri);
+               public ResponseFuture.Builder<T> setUri(String uri){
+                       if(!Strings.isNullOrEmpty(uri)) {
+                               this.setUri(URI.create(uri));
+                       }
+                       return this;
+               }
+               
+               public abstract ResponseFuture.Builder<T> 
setSession(RestApiSession session);
+               public abstract ResponseFuture.Builder<T> setBody(String body);
+       }
+       
+       @Override
+       public void accept(HttpResponse res, Throwable u) {
+               try {
+                       switch(res.getStatusLine().getStatusCode()) {
+                               case 200:
+                                       break;
+                               case 401:
+                                       _handleException(new 
LoginException("Login required"));
+                                       return;
+                               default:
+                                       _handleException(new 
OperationException(String.format("None 200 response: %s %s", 
res.getStatusLine().getStatusCode(), res.getStatusLine().getReasonPhrase())));
+                                       return;
+                       }
+                       
+                       InputStreamReader r = new 
InputStreamReader(res.getEntity().getContent());
+                       T resp = TOSession.gson.fromJson(r, responseType());
+                       if(resp.getAlerts() != null) {
+                               for(Alert a: resp.getAlerts()) {
+                                       if("error".equals(a.getLevel())) {
+                                               _handleException(new 
OperationException("Recieved error from server: "+ a.getText()));
+                                               return;
+                                       }
+                               }
+                       }
+                       
+                       this.complete(resp);
+               } catch (UnsupportedOperationException | IOException e) {
+                       _handleException(new OperationException("Reading 
response failed", e));
+                       return;
+               }
+       }
+       
+       private void _handleException(Throwable t) {
+               if(handleException().isPresent()) {
+                       handleException().get().accept(this, t);
+               }
+               
+               if(!this.isDone()) {
+                       this.completeExceptionally(t);
+               }
+       }
+}
\ No newline at end of file
diff --git 
a/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/TOSession.java
 
b/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/TOSession.java
new file mode 100644
index 000000000..804517273
--- /dev/null
+++ 
b/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/TOSession.java
@@ -0,0 +1,185 @@
+/*
+ *
+ * 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.
+ */
+
+package org.apache.trafficcontrol.client.trafficops;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.trafficcontrol.client.RestApiSession;
+import org.apache.trafficcontrol.client.exception.LoginException;
+import org.apache.trafficcontrol.client.trafficops.models.Response;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+@AutoValue
+public abstract class TOSession implements Closeable {
+       private static final Logger LOG = 
LoggerFactory.getLogger(TOSession.class);
+       
+       private static final String URL_FORMAT_STR = "/%s/%s/%s";
+       
+       public static final String DEFAULT_API_PATH = "api";
+       public static final String DEFAULT_API_VERSION = "1.2";
+       
+       public abstract RestApiSession restClient();
+       public abstract String host();
+       public abstract int port();
+       public abstract boolean ssl();
+       public abstract String apiVersion();
+       public abstract String apiBasePath();
+       
+       static final Gson gson = new GsonBuilder()
+                       .create();
+       
+       private boolean isLoggedIn = false;
+       
+       protected URIBuilder newUriBuilder(final String path) {
+               final String _path = String.format(URL_FORMAT_STR, 
this.apiBasePath(), this.apiVersion(), path);
+               
+               return new URIBuilder()
+                               .setScheme(this.ssl() ? "https" : "http")
+                               .setHost(this.host())
+                               .setPort(this.port())
+                               .setPath(_path);
+       }
+       protected List<NameValuePair> toPairs(Map<String, ?> params){
+               if(params != null && !params.isEmpty()) {
+                       List<NameValuePair> pairs = new LinkedList<>();
+                       for(Map.Entry<String, ?> param: params.entrySet()) {
+                               pairs.add(new 
BasicNameValuePair(param.getKey(), param.getValue().toString()));
+                       }
+               }
+               return Collections.emptyList();
+       }
+       
+       public void close() throws IOException {
+               this.restClient().close();
+       }
+       public boolean isLoggedIn() {
+               return isLoggedIn;
+       }
+       
+       public CompletableFuture<Boolean> login(final String username, final 
String password) {
+               URI uri;
+               try {
+                       uri = this.newUriBuilder("user/login.json")
+                                       .build();
+               } catch (Throwable e) {
+                       final CompletableFuture<Boolean> f = new 
CompletableFuture<>();
+                       f.completeExceptionally(e);
+                       return f;
+               }
+               
+               LOG.debug("Logging into: {}", uri);
+               return ResponseFuture.builder()
+                       .setHandleException((f,t)-> {
+                               f.completeExceptionally(new 
LoginException(String.format("Failed to login with username %s", username), t));
+                       })
+                       .setMethod(ResponseFuture.Method.POST)
+                       .setUri(uri)
+                       .setSession(this.restClient())
+                       
.setBody(gson.toJson(ImmutableMap.<String,String>of("u", username, "p", 
password))).build()
+                       .thenApply(r->{
+                               isLoggedIn = true;
+                               return true;
+                       });
+       }
+       public CompletableFuture<Response.CollectionResponse> getServers(){
+               return this.getServers(null);
+       }
+       public CompletableFuture<Response.CollectionResponse> getServers(final 
Map<String, ?> filterParams){
+               URI uri;
+               try {
+                       uri = this.newUriBuilder("servers.json")
+                                       
.setParameters(this.toPairs(filterParams))
+                                       .build();
+               } catch (Throwable e) {
+                       final CompletableFuture<Response.CollectionResponse> f 
= new CompletableFuture<>();
+                       f.completeExceptionally(e);
+                       return f;
+               }
+               return ResponseFuture.builder(Response.CollectionResponse.class)
+                               .setMethod(ResponseFuture.Method.GET)
+                               .setUri(uri)
+                               .setSession(this.restClient())
+                               .build();
+       }
+       
+       public CompletableFuture<Response.CollectionResponse> 
getDeliveryServices(){
+               return this.getDeliveryServices(null);
+       }
+       public CompletableFuture<Response.CollectionResponse> 
getDeliveryServices(final Map<String, ?> filterParams){
+               URI uri;
+               try {
+                       uri = this.newUriBuilder("deliveryservices.json")
+                                       
.setParameters(this.toPairs(filterParams))
+                                       .build();
+               } catch (Throwable e) {
+                       final CompletableFuture<Response.CollectionResponse> f 
= new CompletableFuture<>();
+                       f.completeExceptionally(e);
+                       return f;
+               }
+               LOG.debug("getDeliveryService url {}", uri);
+               return ResponseFuture.builder(Response.CollectionResponse.class)
+                               .setMethod(ResponseFuture.Method.GET)
+                               .setUri(uri)
+                               .setSession(this.restClient())
+                               .build();
+       }
+       
+       
+       public static Builder builder() {
+               final Builder b = new AutoValue_TOSession.Builder()
+                               .setApiBasePath(DEFAULT_API_PATH)
+                               .setApiVersion(DEFAULT_API_VERSION);
+               
+               return b;
+       }
+       public abstract Builder toBuilder();
+       
+       @AutoValue.Builder
+       public abstract static class Builder {
+               public abstract TOSession build();
+               
+               public abstract Builder setRestClient(RestApiSession 
restClient);
+               public abstract RestApiSession.Builder restClientBuilder();
+
+               public abstract Builder setHost(String host);
+               public abstract Builder setPort(int port);
+               public abstract Builder setApiVersion(String version);
+               public abstract Builder setApiBasePath(String version);
+               public abstract Builder setSsl(boolean ssl);
+               
+               public Builder fromURI(URI uri){
+                       return this.setSsl(uri.getScheme().equals("http") ? 
false: true)
+                                       .setHost(uri.getHost())
+                                       .setPort(uri.getPort());
+               }
+       }
+}
diff --git 
a/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/models/Alert.java
 
b/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/models/Alert.java
new file mode 100644
index 000000000..5f92eb6a9
--- /dev/null
+++ 
b/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/models/Alert.java
@@ -0,0 +1,35 @@
+/*
+ *
+ * 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.
+ */
+
+package org.apache.trafficcontrol.client.trafficops.models;
+
+public class Alert {
+       private String level;
+       private String text;
+       
+       public String getLevel() {
+               return level;
+       }
+       public void setLevel(String level) {
+               this.level = level;
+       }
+       public String getText() {
+               return text;
+       }
+       public void setText(String text) {
+               this.text = text;
+       }
+       
+}
diff --git 
a/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/models/Response.java
 
b/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/models/Response.java
new file mode 100644
index 000000000..0ba3e38f3
--- /dev/null
+++ 
b/traffic_control/clients/java/trafficops/src/main/java/org/apache/trafficcontrol/client/trafficops/models/Response.java
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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.
+ */
+
+package org.apache.trafficcontrol.client.trafficops.models;
+
+import java.util.List;
+import java.util.Map;
+
+public class Response {
+       private List<Alert> alerts;
+
+       public List<Alert> getAlerts() {
+               return alerts;
+       }
+       public void setAlerts(List<Alert> alerts) {
+               this.alerts = alerts;
+       }
+       
+       public class CollectionResponse extends Response {
+               private List<Map<String, ?>> response;
+
+               public List<Map<String, ?>> getResponse() {
+                       return response;
+               }
+
+               public void setResponse(List<Map<String, ?>> response) {
+                       this.response = response;
+               }
+       }
+}
diff --git 
a/traffic_control/clients/java/trafficops/src/test/java/org/apache/trafficcontrol/client/trafficops/TOSessionTest.java
 
b/traffic_control/clients/java/trafficops/src/test/java/org/apache/trafficcontrol/client/trafficops/TOSessionTest.java
new file mode 100644
index 000000000..605a5076a
--- /dev/null
+++ 
b/traffic_control/clients/java/trafficops/src/test/java/org/apache/trafficcontrol/client/trafficops/TOSessionTest.java
@@ -0,0 +1,128 @@
+/*
+ *
+ * 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.
+ */
+
+package org.apache.trafficcontrol.client.trafficops;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.methods.RequestBuilder;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.message.BasicStatusLine;
+import org.apache.trafficcontrol.client.RestApiSession;
+import org.apache.trafficcontrol.client.exception.LoginException;
+import 
org.apache.trafficcontrol.client.trafficops.models.Response.CollectionResponse;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TOSessionTest {
+       private static final Logger LOG = 
LoggerFactory.getLogger(TOSessionTest.class);
+       
+       public static final URI baseUri = 
URI.create("http://trafficcontrol.apache.org:443";);
+       
+       public static final String DeliveryService_Good_Response = 
"{\"response\": [{\"cachegroup\": \"us-co-denver\"}]}";
+       
+       private RestApiSession sessionMock;
+       
+       @Before
+       public void before() {
+               sessionMock = Mockito.mock(RestApiSession.class, 
Mockito.CALLS_REAL_METHODS);
+       }
+       @After
+       public void after() {
+               sessionMock=null;
+       }
+
+       @Test
+       public void testBuild() {
+               TOSession.builder()
+                       .setRestClient(sessionMock)
+                       .fromURI(baseUri)
+                       .build();
+       }
+       
+       @Test(expected=LoginException.class)
+       public void test401Response() throws Throwable {
+               HttpResponse resp = Mockito.mock(HttpResponse.class);
+               Mockito
+                       .when(resp.getStatusLine())
+                       .thenReturn(new BasicStatusLine(HttpVersion.HTTP_1_0, 
401, "Not Auth"));
+               
+               final CompletableFuture<HttpResponse> f = new 
CompletableFuture<>();
+               f.complete(resp);
+               
+               Mockito
+                       .doReturn(f)
+                       .when(sessionMock)
+                       .execute(Mockito.any(RequestBuilder.class));
+               
+               TOSession session = TOSession
+                               .builder()
+                               .fromURI(baseUri)
+                               .setRestClient(sessionMock)
+                               .build();
+               
+               try {
+                       session.getDeliveryServices().get();
+               } catch(Throwable e) {
+                       throw e.getCause();
+               }
+       }
+       
+       @Test
+       public void deliveryServices() throws Throwable {
+               final HttpResponse resp = Mockito.mock(HttpResponse.class);
+               Mockito
+                       .doReturn(new BasicStatusLine(HttpVersion.HTTP_1_0, 
200, "Ok"))
+                       .when(resp)
+                       .getStatusLine();
+               Mockito
+                       .doReturn(new 
StringEntity(DeliveryService_Good_Response))
+                       .when(resp)
+                       .getEntity();
+               
+               final CompletableFuture<HttpResponse> f = new 
CompletableFuture<>();
+               f.complete(resp);
+               
+               Mockito
+                       .doReturn(f)
+                       .when(sessionMock)
+                       .execute(Mockito.any(RequestBuilder.class));
+               
+               final TOSession session = TOSession.builder()
+                               .fromURI(baseUri)
+                               .setRestClient(sessionMock)
+                               .build();
+               
+               CollectionResponse cResp = session.getDeliveryServices().get();
+               
+               assertNotNull(cResp);
+               assertNotNull(cResp.getResponse());
+               assertEquals(1, cResp.getResponse().size());
+               
+               final Map<String,?> service = cResp.getResponse().get(0);
+               assertEquals("us-co-denver", service.get("cachegroup"));
+               LOG.debug("Service: {}", service);
+       }
+}
diff --git 
a/traffic_control/clients/java/trafficops/src/test/resources/logback-test.xml 
b/traffic_control/clients/java/trafficops/src/test/resources/logback-test.xml
new file mode 100644
index 000000000..cca2d3471
--- /dev/null
+++ 
b/traffic_control/clients/java/trafficops/src/test/resources/logback-test.xml
@@ -0,0 +1,29 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+-->
+<configuration>
+       <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+               <encoder>
+                       <pattern>%d{yyyy-MM-dd HH:mm:ss} [%p] [%t] %C:%L 
%m%n</pattern>
+               </encoder>
+       </appender>
+
+       <root level="DEBUG">
+               <appender-ref ref="STDOUT" />
+       </root>
+</configuration>
\ No newline at end of file


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to