summaryrefslogtreecommitdiffstats
path: root/cadi/client/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'cadi/client/src/main')
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsAuthentication.java130
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsTransferSS.java76
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/client/BasicAuth.java28
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/client/EClient.java51
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/client/Future.java33
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/client/Holder.java46
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/client/Rcli.java974
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/client/Result.java60
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/client/Retryable.java71
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/http/HAuthorizationHeader.java54
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/http/HBasicAuthSS.java68
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/http/HClient.java444
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/http/HMangr.java248
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/http/HNoAuthSS.java45
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/http/HRcli.java130
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/http/HTokenSS.java34
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/http/HTransferSS.java64
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/http/HX509SS.java152
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/locator/DNSLocator.java210
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/locator/HClientHotPeerLocator.java60
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/locator/HotPeerLocator.java302
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/locator/PropertyLocator.java289
-rw-r--r--cadi/client/src/main/java/org/onap/aaf/cadi/routing/GreatCircle.java188
23 files changed, 3757 insertions, 0 deletions
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsAuthentication.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsAuthentication.java
new file mode 100644
index 00000000..7a32473c
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsAuthentication.java
@@ -0,0 +1,130 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.client;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+
+/**
+ * AbsAuthentication is a class representing how to Authenticate onto a Client.
+ *
+ * Methods of setting Authentication on a Client vary, so CLIENT is a Generic Type
+ * This allows the ability to apply security onto Different Client Types, as they come
+ * into vogue, or change over time.
+ *
+ * Password is encrypted at rest.
+ *
+ * @author Jonathan
+ *
+ * @param <CLIENT>
+ */
+public abstract class AbsAuthentication<CLIENT> implements SecuritySetter<CLIENT> {
+ // HTTP Header for Authentication is "Authorization". This was from an early stage of internet where
+ // Access by Credential "Authorized" you for everything on the site. Since those early days, it became
+ // clear that "full access" wasn't appropriate, so the split between Authentication and Authorization
+ // came into being... But the Header remains.
+ public static final String AUTHORIZATION = "Authorization";
+ private static final Symm symm;
+
+ protected static final String REPEAT_OFFENDER = "This call is aborted because of repeated usage of invalid Passwords";
+ private static final int MAX_TEMP_COUNT = 10;
+ private static final int MAX_SPAM_COUNT = 10000;
+ private static final long WAIT_TIME = 1000*60*4;
+ private final byte[] headValue;
+ private String user;
+ protected final SecurityInfoC<CLIENT> securityInfo;
+ protected long lastMiss;
+ protected int count;
+
+ static {
+ try {
+ symm = Symm.encrypt.obtain();
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot create critical internal encryption key",e);
+ }
+
+ }
+
+ public AbsAuthentication(final SecurityInfoC<CLIENT> securityInfo, final String user, final byte[] headValue) throws IOException {
+ this.headValue = headValue==null?null:symm.encode(headValue);
+ this.user = user;
+ this.securityInfo = securityInfo;
+ lastMiss=0L;
+ count=0;
+ }
+
+ protected String headValue() throws IOException {
+ if(headValue==null) {
+ return "";
+ } else {
+ return new String(symm.decode(headValue));
+ }
+ }
+
+ protected void setUser(String id) {
+ user = id;
+ }
+
+ @Override
+ public String getID() {
+ return user;
+ }
+
+ public boolean isDenied() {
+ if(lastMiss>0 && lastMiss>System.currentTimeMillis()) {
+ return true;
+ } else {
+ lastMiss=0L;
+ return false;
+ }
+ }
+
+ public synchronized int setLastResponse(int httpcode) {
+ if(httpcode == 401) {
+ ++count;
+ if(lastMiss==0L && count>MAX_TEMP_COUNT) {
+ lastMiss=System.currentTimeMillis()+WAIT_TIME;
+ }
+ // if(count>MAX_SPAM_COUNT) {
+ // System.err.printf("Your service has %d consecutive bad service logins to AAF. \nIt will now exit\n",
+ // count);
+ // System.exit(401);
+ // }
+ if(count%1000==0) {
+ System.err.printf("Your service has %d consecutive bad service logins to AAF. AAF Access will be disabled after %d\n",
+ count,MAX_SPAM_COUNT);
+ }
+
+ } else {
+ lastMiss=0;
+ }
+ return count;
+ }
+
+ public int count() {
+ return count;
+ }
+
+} \ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsTransferSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsTransferSS.java
new file mode 100644
index 00000000..3815bc67
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsTransferSS.java
@@ -0,0 +1,76 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.client;
+
+import java.security.Principal;
+
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+
+/**
+ * This client represents the ability to Transfer the Identity of the caller to the authenticated
+ * user being transferred to. This ability is critical for App-to-App communication to ensure that
+ * Authorization can happen on the End-Users' credentials when appropriate, even though Authentication
+ * to App1 by App2 must be by App2's credentials.
+ *
+ * @author Jonathan
+ *
+ * @param <CLIENT>
+ */
+public abstract class AbsTransferSS<CLIENT> implements SecuritySetter<CLIENT> {
+ protected String value;
+ protected SecurityInfoC<CLIENT> securityInfo;
+ protected SecuritySetter<CLIENT> defSS;
+ private Principal principal;
+
+ //Format:<ID>:<APP>:<protocol>[:AS][,<ID>:<APP>:<protocol>]*
+ public AbsTransferSS(TaggedPrincipal principal, String app) {
+ init(principal, app);
+ }
+
+ public AbsTransferSS(TaggedPrincipal principal, String app, SecurityInfoC<CLIENT> si) {
+ init(principal,app);
+ securityInfo = si;
+ this.defSS = si.defSS;
+ }
+
+ private void init(TaggedPrincipal principal, String app) {
+ this.principal=principal;
+ if(principal==null) {
+ return;
+ } else {
+ value = principal.getName() + ':' +
+ app + ':' +
+ principal.tag() + ':' +
+ "AS";
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.cadi.SecuritySetter#getID()
+ */
+ @Override
+ public String getID() {
+ return principal==null?"":principal.getName();
+ }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/BasicAuth.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/BasicAuth.java
new file mode 100644
index 00000000..1eb8d7c4
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/BasicAuth.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.client;
+
+/**
+ * Basic Auth is a marker Interface, because certain kinds of behaviors apply only to User/Password Combinations
+ */
+public interface BasicAuth {
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/EClient.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/EClient.java
new file mode 100644
index 00000000..d5dfebf5
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/EClient.java
@@ -0,0 +1,51 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.client;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+
+
+public interface EClient<CT> {
+ public void setMethod(String meth);
+ public void setPathInfo(String pathinfo);
+ public void setPayload(Transfer transfer);
+ public void addHeader(String tag, String value);
+ public void setQueryParams(String q);
+ public void setFragment(String f);
+ public void send() throws APIException;
+ public<T> Future<T> futureCreate(Class<T> t);
+ public Future<String> futureReadString();
+ public<T> Future<T> futureRead(RosettaDF<T> df,Data.TYPE type);
+ public<T> Future<T> future(T t);
+ public Future<Void> future(HttpServletResponse resp, int expected) throws APIException;
+
+ public interface Transfer {
+ public void transfer(OutputStream os) throws IOException, APIException;
+ }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Future.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Future.java
new file mode 100644
index 00000000..2579dc11
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Future.java
@@ -0,0 +1,33 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.client;
+
+import org.onap.aaf.cadi.CadiException;
+
+public abstract class Future<T> {
+ public T value;
+ public abstract boolean get(int timeout) throws CadiException;
+
+ public abstract int code();
+ public abstract String body();
+ public abstract String header(String tag);
+} \ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Holder.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Holder.java
new file mode 100644
index 00000000..c13afc25
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Holder.java
@@ -0,0 +1,46 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.client;
+
+/**
+ * Use to set Variables outside of Anonymous classes.
+ * @author Jonathan
+ *
+ * @param <T>
+ */
+public class Holder<T> {
+ private T value;
+ public Holder(T t) {
+ value = t;
+ }
+ public T set(T t) {
+ value = t;
+ return t;
+ }
+
+ public T get() {
+ return value;
+ }
+ public String toString() {
+ return value.toString();
+ }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Rcli.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Rcli.java
new file mode 100644
index 00000000..5ebc017e
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Rcli.java
@@ -0,0 +1,974 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.client;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.URI;
+import java.util.Enumeration;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.client.EClient.Transfer;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data.TYPE;
+import org.onap.aaf.misc.env.util.Pool;
+import org.onap.aaf.misc.env.util.Pool.Pooled;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+
+public abstract class Rcli<CT> {
+ public static final String FORM_ENCODED = "application/x-www-form-urlencoded";
+ public static final String APPL_JSON = "application/json";
+ public static final String APPL_XML = "application/xml";
+ public static final String BLANK = "";
+ public static final String CONTENT_TYPE = "Content-Type";
+ public static final String ACCEPT = "Accept";
+
+ protected static final String POST = "POST";
+ protected static final String GET = "GET";
+ protected static final String PUT = "PUT";
+ protected static final String DELETE = "DELETE";
+ protected TYPE type;
+ protected String apiVersion;
+ protected int readTimeout = 5000;
+ protected int connectionTimeout = 3000;
+ protected URI uri;
+ private String queryParams, fragment;
+ public static Pool<byte[]> buffPool = new Pool<byte[]>(new Pool.Creator<byte[]>() {
+ @Override
+ public byte[] create() throws APIException {
+ return new byte[1024];
+ }
+
+ @Override
+ public void destroy(byte[] t) {
+ }
+
+ @Override
+ public boolean isValid(byte[] t) {
+ return true;
+ }
+
+ @Override
+ public void reuse(byte[] t) {
+ }
+ });
+
+
+ public Rcli() {
+ super();
+ }
+
+ public abstract void setSecuritySetter(SecuritySetter<CT> ss);
+ public abstract SecuritySetter<CT> getSecuritySetter();
+
+
+ public Rcli<CT> forUser(SecuritySetter<CT> ss) {
+ Rcli<CT> rv = clone(uri==null?this.uri:uri,ss);
+ setSecuritySetter(ss);
+ rv.type = type;
+ rv.apiVersion = apiVersion;
+ return rv;
+ }
+
+ protected abstract Rcli<CT> clone(URI uri, SecuritySetter<CT> ss);
+
+ public abstract void invalidate() throws CadiException;
+
+ public Rcli<CT> readTimeout(int millis) {
+ readTimeout = millis;
+ return this;
+ }
+
+ public Rcli<CT> connectionTimeout(int millis) {
+ connectionTimeout = millis;
+ return this;
+ }
+
+ public Rcli<CT> type(TYPE type) {
+ this.type=type;
+ return this;
+ }
+
+ public Rcli<CT> apiVersion(String apiVersion) {
+ this.apiVersion = apiVersion;
+ return this;
+ }
+
+ public boolean isApiVersion(String prospective) {
+ return apiVersion.equals(prospective);
+ }
+
+
+ public String typeString(Class<?> cls) {
+ return "application/"+cls.getSimpleName()+"+"+type.name().toLowerCase()+
+ (apiVersion==null?BLANK:";version="+apiVersion);
+ }
+
+ protected abstract EClient<CT> client() throws CadiException;
+
+
+ public<T> Future<T> create(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(POST);
+ client.addHeader(CONTENT_TYPE,contentType);
+ client.setPathInfo(pathinfo);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+ client.send();
+ queryParams = fragment = null;
+ return client.futureCreate(df.getTypeClass());
+ }
+
+ public<T> Future<T> create(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(POST);
+ client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass()));
+ client.setPathInfo(pathinfo);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+ client.send();
+ queryParams = fragment = null;
+ return client.futureCreate(df.getTypeClass());
+ }
+
+ public<T> Future<T> create(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(POST);
+ client.addHeader(CONTENT_TYPE,typeString(cls));
+ client.setPathInfo(pathinfo);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+ client.send();
+ queryParams = fragment = null;
+ return client.futureCreate(df.getTypeClass());
+ }
+
+ public<T> Future<T> create(String pathinfo, Class<T> cls) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+
+ EClient<CT> client = client();
+ client.setMethod(POST);
+ client.addHeader(CONTENT_TYPE,typeString(cls));
+ client.setPathInfo(pathinfo);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPayload(null);
+ client.send();
+ queryParams = fragment = null;
+ return client.futureCreate(cls);
+ }
+
+ public Future<Void> create(String pathinfo, String contentType) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+
+ EClient<CT> client = client();
+ client.setMethod(POST);
+ client.addHeader(CONTENT_TYPE,contentType);
+ client.setPathInfo(pathinfo);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPayload(null);
+ client.send();
+ queryParams = fragment = null;
+ return client.futureCreate(Void.class);
+ }
+
+
+ /**
+ * Post Data in WWW expected format, with the format tag1=value1&tag2=value2, etc
+ * Note Shortcut:
+ * Because typically, you will want to have a variable as value, you can type, as long as tag ends with "="
+ * postForm(..., "tag1=value1","tag2=",var2);
+ * @param pathinfo
+ * @param df
+ * @param cls
+ * @param formParam
+ * @return
+ * @throws APIException
+ * @throws CadiException
+ */
+ public <T> Future<T> postForm(String pathinfo, final RosettaDF<T> df, final String ... formParam) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(POST);
+ client.addHeader(CONTENT_TYPE,FORM_ENCODED);
+ switch(type) {
+ case JSON:
+ client.addHeader(ACCEPT, APPL_JSON);
+ break;
+ case XML:
+ client.addHeader(ACCEPT, APPL_XML);
+ break;
+ default:
+ break;
+ }
+ client.setPathInfo(pathinfo);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPayload(new Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ PrintStream ps;
+ if(os instanceof PrintStream) {
+ ps = (PrintStream)os;
+ } else {
+ ps = new PrintStream(os);
+ }
+ boolean first = true;
+ for(String fp : formParam) {
+ if(fp!=null) {
+ if(first) {
+ first = false;
+ } else {
+ ps.print('&');
+ }
+ if(fp.endsWith("=")) {
+ first = true;
+ }
+ ps.print(fp);
+ }
+ }
+ }});
+ client.send();
+ queryParams = fragment = null;
+ return client.futureRead(df,TYPE.JSON);
+ }
+
+ /**
+ * Read String, using POST for keyInfo
+ *
+ * @param pathinfo
+ * @param df
+ * @param t
+ * @param resp
+ * @return
+ * @throws APIException
+ * @throws CadiException
+ */
+ public<T> Future<String> readPost(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(POST);
+ client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass()));
+ client.setPathInfo(pathinfo);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+ client.send();
+ queryParams = fragment = null;
+ return client.futureReadString();
+ }
+
+ /**
+ * Read using POST for keyInfo, responding with marshaled Objects
+ *
+ * @param pathinfo
+ * @param df
+ * @param t
+ * @param resp
+ * @return
+ * @throws APIException
+ * @throws CadiException
+ */
+ public<T,R> Future<R> readPost(String pathinfo, final RosettaDF<T> df, final T t, final RosettaDF<R> resp) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(POST);
+ client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass()));
+ client.setPathInfo(pathinfo);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+ client.send();
+ queryParams = fragment = null;
+ return client.futureRead(resp,resp.getOutType());
+ }
+
+ public Future<String> readPost(String pathinfo, String contentType, String ... headers) throws CadiException, APIException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(POST);
+ client.addHeader(CONTENT_TYPE,contentType);
+ client.setPathInfo(pathinfo);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ }});
+ client.send();
+ queryParams = fragment = null;
+ return client.futureReadString();
+ }
+
+ public Future<String> read(String pathinfo, String accept, String ... headers) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(GET);
+ client.addHeader(ACCEPT, accept);
+
+ for(int i=1;i<headers.length;i=i+2) {
+ client.addHeader(headers[i-1],headers[i]);
+ }
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+
+ client.setPathInfo(pathinfo);
+
+ client.setPayload(null);
+ client.send();
+ queryParams = fragment = null;
+ return client.futureReadString();
+ }
+
+ public<T> Future<T> read(String pathinfo, String accept, RosettaDF<T> df, String ... headers) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+
+ EClient<CT> client = client();
+ client.setMethod(GET);
+ client.addHeader(ACCEPT, accept);
+ for(int i=1;i<headers.length;i=i+2) {
+ client.addHeader(headers[i-1],headers[i]);
+ }
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+
+ client.setPayload(null);
+ client.send();
+ queryParams = fragment = null;
+ return client.futureRead(df,type);
+ }
+
+ public<T> Future<T> read(String pathinfo, RosettaDF<T> df,String ... headers) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+
+ EClient<CT> client = client();
+ client.setMethod(GET);
+ client.addHeader(ACCEPT, typeString(df.getTypeClass()));
+ for(int i=1;i<headers.length;i=i+2) {
+ client.addHeader(headers[i-1],headers[i]);
+ }
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+
+ client.setPayload(null);
+ client.send();
+ queryParams = fragment = null;
+ return client.futureRead(df,type);
+ }
+
+ public<T> Future<T> read(String pathinfo, Class<?> cls, RosettaDF<T> df) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(GET);
+ client.addHeader(ACCEPT, typeString(cls));
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+
+ client.setPayload(null);
+ client.send();
+ queryParams = fragment = null;
+ return client.futureRead(df,type);
+ }
+
+ public<T> Future<T> update(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+
+ EClient<CT> client = client();
+ client.setMethod(PUT);
+ client.addHeader(CONTENT_TYPE,contentType);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+ client.send();
+ queryParams = fragment = null;
+ return client.future(t);
+ }
+
+ public<T> Future<String> updateRespondString(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+
+ EClient<CT> client = client();
+ client.setMethod(PUT);
+ client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+ client.send();
+ queryParams = fragment = null;
+ return client.futureReadString();
+ }
+
+
+ public<T> Future<T> update(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(PUT);
+ client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+ client.send();
+ queryParams = fragment = null;
+ return client.future(t);
+ }
+
+ public<T> Future<T> update(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+
+ EClient<CT> client = client();
+ client.setMethod(PUT);
+ client.addHeader(CONTENT_TYPE, typeString(cls));
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+ client.send();
+ queryParams = fragment = null;
+ return client.future(t);
+ }
+
+ /**
+ * A method to update with a VOID
+ * @param pathinfo
+ * @param resp
+ * @param expected
+ * @return
+ * @throws APIException
+ * @throws CadiException
+ */
+ public<T> Future<Void> update(String pathinfo) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+
+ EClient<CT> client = client();
+ client.setMethod(PUT);
+ client.addHeader(CONTENT_TYPE, typeString(Void.class));
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+// client.setPayload(new EClient.Transfer() {
+// @Override
+// public void transfer(OutputStream os) throws IOException, APIException {
+// }
+// });
+ client.send();
+ queryParams = fragment = null;
+ return client.future(null);
+ }
+
+ public<T> Future<T> delete(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+
+ EClient<CT> client = client();
+ client.setMethod(DELETE);
+ client.addHeader(CONTENT_TYPE, contentType);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+ client.send();
+ queryParams = fragment = null;
+ return client.future(t);
+ }
+
+ public<T> Future<T> delete(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+
+ EClient<CT> client = client();
+ client.setMethod(DELETE);
+ client.addHeader(CONTENT_TYPE, typeString(cls));
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+ client.send();
+ queryParams = fragment = null;
+ return client.future(t);
+ }
+
+ public<T> Future<T> delete(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(DELETE);
+ client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ df.newData().out(type).direct(t,os);
+ }
+ });
+
+ client.send();
+ queryParams = fragment = null;
+ return client.future(t);
+ }
+
+
+ public<T> Future<T> delete(String pathinfo, Class<T> cls) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+
+ EClient<CT> client = client();
+ client.setMethod(DELETE);
+ client.addHeader(CONTENT_TYPE, typeString(cls));
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+ client.setPayload(null);
+ client.send();
+ queryParams = fragment = null;
+ return client.future((T)null);
+ }
+
+ public Future<Void> delete(String pathinfo, String contentType) throws APIException, CadiException {
+ final String qp;
+ if(pathinfo==null) {
+ qp=queryParams;
+ } else {
+ final int idx = pathinfo.indexOf('?');
+ if(idx>=0) {
+ qp=pathinfo.substring(idx+1);
+ pathinfo=pathinfo.substring(0,idx);
+ } else {
+ qp=queryParams;
+ }
+ }
+
+ EClient<CT> client = client();
+ client.setMethod(DELETE);
+ client.addHeader(CONTENT_TYPE, contentType);
+ client.setQueryParams(qp);
+ client.setFragment(fragment);
+ client.setPathInfo(pathinfo);
+ client.setPayload(null);
+ client.send();
+ queryParams = fragment = null;
+ return client.future(null);
+ }
+
+ public Future<Void> transfer(final HttpServletRequest req, final HttpServletResponse resp, final String pathParam, final int expected) throws CadiException, APIException {
+ EClient<CT> client = client();
+ URI uri;
+ try {
+ uri = new URI(req.getRequestURI());
+ } catch (Exception e) {
+ throw new CadiException("Invalid incoming URI",e);
+ }
+ String name;
+ for(Enumeration<String> en = req.getHeaderNames();en.hasMoreElements();) {
+ name = en.nextElement();
+ client.addHeader(name,req.getHeader(name));
+ }
+ client.setQueryParams(req.getQueryString());
+ client.setFragment(uri.getFragment());
+ client.setPathInfo(pathParam);
+ String meth = req.getMethod();
+ client.setMethod(meth);
+ if(!"GET".equals(meth)) {
+ client.setPayload(new EClient.Transfer() {
+ @Override
+ public void transfer(OutputStream os) throws IOException, APIException {
+ final ServletInputStream is = req.getInputStream();
+ int read;
+ // reuse Buffers
+ Pooled<byte[]> pbuff = buffPool.get();
+ try {
+ while((read=is.read(pbuff.content))>=0) {
+ os.write(pbuff.content,0,read);
+ }
+ } finally {
+ pbuff.done();
+ }
+ }
+ });
+ }
+ client.send();
+ return client.future(resp, expected);
+ }
+
+ public String toString() {
+ return uri.toString();
+ }
+
+ /**
+ * @param queryParams the queryParams to set
+ * @return
+ */
+ public Rcli<CT> setQueryParams(String queryParams) {
+ this.queryParams = queryParams;
+ return this;
+ }
+
+
+ /**
+ * @param fragment the fragment to set
+ * @return
+ */
+ public Rcli<CT> setFragment(String fragment) {
+ this.fragment = fragment;
+ return this;
+ }
+
+ public URI getURI() {
+ return uri;
+ }
+
+} \ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Result.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Result.java
new file mode 100644
index 00000000..fecb847b
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Result.java
@@ -0,0 +1,60 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.client;
+
+public class Result<T> {
+ public final int code;
+ public final T value;
+ public final String error;
+
+ private Result(int code, T value, String error) {
+ this.code = code;
+ this.value = value;
+ this.error = error;
+ }
+
+ public static<T> Result<T> ok(int code,T t) {
+ return new Result<T>(code,t,null);
+ }
+
+ public static<T> Result<T> err(int code,String body) {
+ return new Result<T>(code,null,body);
+ }
+
+ public static<T> Result<T> err(Result<?> r) {
+ return new Result<T>(r.code,null,r.error);
+ }
+
+ public boolean isOK() {
+ return error==null;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder("Code: ");
+ sb.append(code);
+ if(error!=null) {
+ sb.append(" = ");
+ sb.append(error);
+ }
+ return sb.toString();
+ }
+} \ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Retryable.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Retryable.java
new file mode 100644
index 00000000..8208efe1
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Retryable.java
@@ -0,0 +1,71 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.client;
+
+import java.net.ConnectException;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.misc.env.APIException;
+
+/**
+ *
+ * @author Jonathan
+ *
+ * @param <RT>
+ * @param <RET>
+ */
+public abstract class Retryable<RET> {
+ // be able to hold state for consistent Connections. Not required for all connection types.
+ public Rcli<?> lastClient;
+ private Locator.Item item;
+
+ public Retryable() {
+ lastClient = null;
+ item = null;
+ }
+
+ public Retryable(Retryable<?> ret) {
+ lastClient = ret.lastClient;
+ item = ret.item;
+ }
+
+ public Locator.Item item(Locator.Item item) {
+ lastClient = null;
+ this.item = item;
+ return item;
+ }
+ public Locator.Item item() {
+ return item;
+ }
+
+ public abstract RET code(Rcli<?> client) throws CadiException, ConnectException, APIException;
+
+ /**
+ * Note, Retryable is tightly coupled to the Client Utilizing. It will not be the wrong type.
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public <CLIENT> Rcli<CLIENT> lastClient() {
+ return (Rcli<CLIENT>)lastClient;
+ }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HAuthorizationHeader.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HAuthorizationHeader.java
new file mode 100644
index 00000000..787c5c29
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HAuthorizationHeader.java
@@ -0,0 +1,54 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.client.AbsAuthentication;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+
+public class HAuthorizationHeader extends AbsAuthentication<HttpURLConnection> {
+
+ public HAuthorizationHeader(SecurityInfoC<HttpURLConnection> si, String user, String headValue) throws IOException {
+ super(si,user,headValue==null?null:headValue.getBytes());
+ }
+
+ @Override
+ public void setSecurity(HttpURLConnection huc) throws CadiException {
+ if(isDenied()) {
+ throw new CadiException(REPEAT_OFFENDER);
+ }
+ try {
+ huc.addRequestProperty(AUTHORIZATION , headValue());
+ } catch (IOException e) {
+ throw new CadiException(e);
+ }
+ if(securityInfo!=null && huc instanceof HttpsURLConnection) {
+ securityInfo.setSocketFactoryOn((HttpsURLConnection)huc);
+ }
+ }
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HBasicAuthSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HBasicAuthSS.java
new file mode 100644
index 00000000..9e86c7fb
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HBasicAuthSS.java
@@ -0,0 +1,68 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.client.BasicAuth;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.principal.BasicPrincipal;
+
+public class HBasicAuthSS extends HAuthorizationHeader implements BasicAuth {
+ public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si, String user, String password) throws IOException {
+ super(si, user, "Basic " + Symm.base64noSplit.encode(user + ':' + password));
+ }
+
+ public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si) throws IOException {
+ this(si,si.access.getProperty(Config.AAF_APPID, null),
+ si.access.decrypt(si.access.getProperty(Config.AAF_APPPASS, null), false));
+ }
+
+ public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si, boolean setDefault) throws IOException {
+ this(si,si.access.getProperty(Config.AAF_APPID, null),
+ si.access.decrypt(si.access.getProperty(Config.AAF_APPPASS, null), false),setDefault);
+ }
+
+
+ public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si, String user, String pass, boolean asDefault) throws IOException {
+ this(si, user,pass);
+ if(asDefault) {
+ si.set(this);
+ }
+ }
+
+ public HBasicAuthSS(BasicPrincipal bp, SecurityInfoC<HttpURLConnection> si) throws IOException {
+ this(si, bp.getName(),new String(bp.getCred()));
+ }
+
+ public HBasicAuthSS(BasicPrincipal bp, SecurityInfoC<HttpURLConnection> si, boolean asDefault) throws IOException {
+ this(si, bp.getName(),new String(bp.getCred()));
+ if(asDefault) {
+ si.set(this);
+ }
+ }
+
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HClient.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HClient.java
new file mode 100644
index 00000000..46099887
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HClient.java
@@ -0,0 +1,444 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.client.EClient;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data;
+import org.onap.aaf.misc.env.Data.TYPE;
+import org.onap.aaf.misc.env.util.Pool.Pooled;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+
+/**
+ * Low Level Http Client Mechanism. Chances are, you want the high level "HRcli"
+ * for Rosetta Object Translation
+ *
+ * @author Jonathan
+ *
+ */
+public class HClient implements EClient<HttpURLConnection> {
+ private URI uri;
+ private ArrayList<Header> headers;
+ private String meth;
+ private String pathinfo;
+ private String query;
+ private String fragment;
+ private Transfer transfer;
+ private SecuritySetter<HttpURLConnection> ss;
+ private HttpURLConnection huc;
+ private int connectTimeout;
+
+ public HClient(SecuritySetter<HttpURLConnection> ss, URI uri,int connectTimeout) throws LocatorException {
+ if (uri == null) {
+ throw new LocatorException("No Service available to call");
+ }
+ this.uri = uri;
+ this.ss = ss;
+ this.connectTimeout = connectTimeout;
+ pathinfo = query = fragment = null;
+ }
+
+ @Override
+ public void setMethod(String meth) {
+ this.meth = meth;
+ }
+
+ @Override
+ public void setPathInfo(String pathinfo) {
+ this.pathinfo = pathinfo;
+ }
+
+ @Override
+ public void setPayload(Transfer transfer) {
+ this.transfer = transfer;
+ }
+
+ @Override
+ public void addHeader(String tag, String value) {
+ if (headers == null)
+ headers = new ArrayList<Header>();
+ headers.add(new Header(tag, value));
+ }
+
+ @Override
+ public void setQueryParams(String q) {
+ query = q;
+ }
+
+ @Override
+ public void setFragment(String f) {
+ fragment = f;
+ }
+
+ @Override
+ public void send() throws APIException {
+ try {
+ // Build URL from given URI plus current Settings
+ if(uri.getPath()==null) {
+ throw new APIException("Invalid URL entered for HClient");
+ }
+ StringBuilder pi=null;
+ if(pathinfo!=null) { // additional pathinfo
+ pi = new StringBuilder(uri.getPath());
+ if(!pathinfo.startsWith("/")) {
+ pi.append('/');
+ }
+ pi.append(pathinfo);
+ }
+ URL url = new URI(
+ uri.getScheme(),
+ uri.getUserInfo(),
+ uri.getHost(),
+ uri.getPort(),
+ pi==null?uri.getPath():pi.toString(),
+ query,
+ fragment).toURL();
+ pathinfo=null;
+ query=null;
+ fragment=null;
+ huc = (HttpURLConnection) url.openConnection();
+ huc.setRequestMethod(meth);
+ if(ss!=null) {
+ ss.setSecurity(huc);
+ }
+ if (headers != null)
+ for (Header d : headers) {
+ huc.addRequestProperty(d.tag, d.value);
+ }
+ huc.setDoInput(true);
+ huc.setDoOutput(true);
+ huc.setUseCaches(false);
+ huc.setConnectTimeout(connectTimeout);
+ huc.connect();
+ if (transfer != null) {
+ transfer.transfer(huc.getOutputStream());
+ }
+ // TODO other settings? There's a bunch here.
+ } catch (Exception e) {
+ throw new APIException(e);
+ } finally { // ensure all these are reset after sends
+ meth=pathinfo=null;
+ if(headers!=null) {
+ headers.clear();
+ }
+ pathinfo = query = fragment = "";
+ }
+ }
+
+ public URI getURI() {
+ return uri;
+ }
+
+ public int timeout() {
+ return connectTimeout;
+ }
+
+ public abstract class HFuture<T> extends Future<T> {
+ protected HttpURLConnection huc;
+ protected int respCode;
+ protected String respMessage;
+ protected IOException exception;
+ protected StringBuilder errContent;
+
+ public HFuture(final HttpURLConnection huc) {
+ this.huc = huc;
+ }
+
+ protected boolean evalInfo(HttpURLConnection huc) throws APIException, IOException{
+ return respCode == 200;
+ };
+
+ @Override
+ public final boolean get(int timeout) throws CadiException {
+ try {
+ huc.setReadTimeout(timeout);
+ respCode = huc.getResponseCode();
+ ss.setLastResponse(respCode);
+ if(evalInfo(huc)) {
+ return true;
+ } else {
+ extractError();
+ return false;
+ }
+ } catch (IOException | APIException e) {
+ throw new CadiException(e);
+ } finally {
+ close();
+ }
+ }
+
+ private void extractError() {
+ InputStream is = huc.getErrorStream();
+ try {
+ if(is==null) {
+ is = huc.getInputStream();
+ }
+ if(is!=null) {
+ errContent = new StringBuilder();
+ int c;
+ while((c=is.read())>=0) {
+ errContent.append((char)c);
+ }
+ }
+ } catch (IOException e) {
+ exception = e;
+ }
+ }
+
+ // Typically only used by Read
+ public StringBuilder inputStreamToString(InputStream is) {
+ // Avoids Carriage returns, and is reasonably efficient, given
+ // the buffer reads.
+ try {
+ StringBuilder sb = new StringBuilder();
+ Reader rdr = new InputStreamReader(is);
+ try {
+ char[] buf = new char[256];
+ int read;
+ while ((read = rdr.read(buf)) >= 0) {
+ sb.append(buf, 0, read);
+ }
+ } finally {
+ rdr.close();
+ }
+ return sb;
+ } catch (IOException e) {
+ exception = e;
+ return null;
+ }
+ }
+
+
+ @Override
+ public int code() {
+ return respCode;
+ }
+
+ public HttpURLConnection huc() {
+ return huc;
+ }
+
+ public IOException exception() {
+ return exception;
+ }
+
+ public String respMessage() {
+ return respMessage;
+ }
+
+ @Override
+ public String header(String tag) {
+ return huc.getHeaderField(tag);
+ }
+
+ public void close() {
+ if(huc!=null) {
+ huc.disconnect();
+ }
+ }
+ }
+
+ @Override
+ public <T> Future<T> futureCreate(Class<T> t) {
+ return new HFuture<T>(huc) {
+ public boolean evalInfo(HttpURLConnection huc) {
+ return respCode==201;
+ }
+
+ @Override
+ public String body() {
+ if (errContent != null) {
+ return errContent.toString();
+
+ } else if (respMessage != null) {
+ return respMessage;
+ }
+ return "";
+ }
+ };
+ }
+
+ @Override
+ public Future<String> futureReadString() {
+ return new HFuture<String>(huc) {
+ public boolean evalInfo(HttpURLConnection huc) throws IOException {
+ if (respCode == 200) {
+ StringBuilder sb = inputStreamToString(huc.getInputStream());
+ if (sb != null) {
+ value = sb.toString();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String body() {
+ if (value != null) {
+ return value;
+ } else if (errContent != null) {
+ return errContent.toString();
+ } else if (respMessage != null) {
+ return respMessage;
+ }
+ return "";
+ }
+
+ };
+ }
+
+ @Override
+ public <T> Future<T> futureRead(final RosettaDF<T> df, final TYPE type) {
+ return new HFuture<T>(huc) {
+ private Data<T> data;
+
+ public boolean evalInfo(HttpURLConnection huc) throws APIException, IOException {
+ if (respCode == 200) {
+ data = df.newData().in(type).load(huc.getInputStream());
+ value = data.asObject();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String body() {
+ if (data != null) {
+ try {
+ return data.asString();
+ } catch (APIException e) {
+ }
+ } else if (errContent != null) {
+ return errContent.toString();
+ } else if (respMessage != null) {
+ return respMessage;
+ }
+ return "";
+ }
+ };
+ }
+
+ @Override
+ public <T> Future<T> future(final T t) {
+ return new HFuture<T>(huc) {
+ public boolean evalInfo(HttpURLConnection huc) {
+ if (respCode == 200) {
+ value = t;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String body() {
+ if (errContent != null) {
+ return errContent.toString();
+ } else if (respMessage != null) {
+ return respMessage;
+ }
+ return Integer.toString(respCode);
+ }
+ };
+ }
+
+ @Override
+ public Future<Void> future(final HttpServletResponse resp, final int expected) throws APIException {
+ return new HFuture<Void>(huc) {
+ public boolean evalInfo(HttpURLConnection huc) throws IOException, APIException {
+ resp.setStatus(respCode);
+ int read;
+ InputStream is;
+ OutputStream os = resp.getOutputStream();
+ if(respCode==expected) {
+ is = huc.getInputStream();
+ // reuse Buffers
+ Pooled<byte[]> pbuff = Rcli.buffPool.get();
+ try {
+ while((read=is.read(pbuff.content))>=0) {
+ os.write(pbuff.content,0,read);
+ }
+ } finally {
+ pbuff.done();
+ }
+ return true;
+ } else {
+ is = huc.getErrorStream();
+ if(is==null) {
+ is = huc.getInputStream();
+ }
+ if(is!=null) {
+ errContent = new StringBuilder();
+ Pooled<byte[]> pbuff = Rcli.buffPool.get();
+ try {
+ while((read=is.read(pbuff.content))>=0) {
+ os.write(pbuff.content,0,read);
+ }
+ } finally {
+ pbuff.done();
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String body() {
+ return errContent==null?respMessage:errContent.toString();
+ }
+ };
+ }
+
+ private static class Header {
+ public final String tag;
+ public final String value;
+
+ public Header(String t, String v) {
+ this.tag = t;
+ this.value = v;
+ }
+
+ public String toString() {
+ return tag + '=' + value;
+ }
+ }
+
+ public String toString() {
+ return "HttpURLConnection Client configured to " + uri.toString();
+ }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HMangr.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HMangr.java
new file mode 100644
index 00000000..2aa10ac3
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HMangr.java
@@ -0,0 +1,248 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.http;
+
+import java.net.ConnectException;
+import java.net.HttpURLConnection;
+import java.net.SocketException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.misc.env.APIException;
+
+public class HMangr {
+ private String apiVersion;
+ private int readTimeout, connectionTimeout;
+ public final Locator<URI> loc;
+ private Access access;
+
+ public HMangr(Access access, Locator<URI> loc) throws LocatorException {
+ readTimeout = 10000;
+ connectionTimeout=3000;
+ if(loc == null) {
+ throw new LocatorException("Null Locator passed");
+ }
+ this.loc = loc;
+ this.access = access;
+ }
+
+ /**
+ * Reuse the same service. This is helpful for multiple calls that change service side cached data so that
+ * there is not a speed issue.
+ *
+ * If the service goes down, another service will be substituted, if available.
+ *
+ * @param access
+ * @param loc
+ * @param ss
+ * @param item
+ * @param retryable
+ * @return
+ * @throws URISyntaxException
+ * @throws Exception
+ */
+ public<RET> RET same(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable) throws APIException, CadiException, LocatorException {
+ RET ret = null;
+ boolean retry = true;
+ Rcli<HttpURLConnection> client = retryable.lastClient();
+ try {
+ do {
+ Item item;
+ // if no previous state, get the best
+ if(retryable.item()==null) {
+ item = loc.best();
+ if(item==null) {
+ throw new LocatorException("No Services Found for " + loc);
+ }
+ retryable.item(item);
+ retryable.lastClient = null;
+ }
+ if(client==null) {
+ item = retryable.item();
+ URI uri=loc.get(item);
+ if(uri==null) {
+ loc.invalidate(retryable.item());
+ if(loc.hasItems()) {
+ retryable.item(loc.next(retryable.item()));
+ continue;
+ } else {
+ throw new LocatorException("No clients available for " + loc.toString());
+ }
+ }
+ client = new HRcli(this, uri,item,ss)
+ .connectionTimeout(connectionTimeout)
+ .readTimeout(readTimeout)
+ .apiVersion(apiVersion);
+ } else {
+ client.setSecuritySetter(ss);
+ }
+
+ retry = false;
+ try {
+ ret = retryable.code(client);
+ } catch (APIException | CadiException e) {
+ item = retryable.item();
+ loc.invalidate(item);
+ retryable.item(loc.next(item));
+ try {
+ Throwable ec = e.getCause();
+ if(ec instanceof java.net.ConnectException) {
+ if(client!=null && loc.hasItems()) {
+ access.log(Level.WARN,"Connection refused, trying next available service");
+ retry = true;
+ } else {
+ throw new CadiException("Connection refused, no more services to try");
+ }
+ } else if(ec instanceof java.net.SocketException) {
+ if(client!=null && loc.hasItems()) {
+ access.log(Level.WARN,"Socket prematurely closed, trying next available service");
+ retry = true;
+ } else {
+ throw new CadiException("Socket prematurely closed, no more services to try");
+ }
+ } else if(ec instanceof SSLHandshakeException) {
+ retryable.item(null);
+ throw e;
+ } else if(ec instanceof SocketException) {
+ if("java.net.SocketException: Connection reset".equals(ec.getMessage())) {
+ access.log(Level.ERROR, ec.getMessage(), " can mean Certificate Expiration or TLS Protocol issues");
+ }
+ retryable.item(null);
+ throw e;
+ } else {
+ retryable.item(null);
+ throw e;
+ }
+ } finally {
+ client = null;
+ }
+ } catch (ConnectException e) {
+ item = retryable.item();
+ loc.invalidate(item);
+ retryable.item(loc.next(item));
+ }
+ } while(retry);
+ } finally {
+ retryable.lastClient = client;
+ }
+ return ret;
+ }
+
+
+ public<RET> RET best(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable) throws LocatorException, CadiException, APIException {
+ if(loc==null) {
+ throw new LocatorException("No Locator Configured");
+ }
+ retryable.item(loc.best());
+ return same(ss,retryable);
+ }
+ public<RET> RET all(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable) throws LocatorException, CadiException, APIException {
+ return oneOf(ss,retryable,true,null);
+ }
+
+ public<RET> RET all(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable,boolean notify) throws LocatorException, CadiException, APIException {
+ return oneOf(ss,retryable,notify,null);
+ }
+
+ public<RET> RET oneOf(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable,boolean notify,String host) throws LocatorException, CadiException, APIException {
+ RET ret = null;
+ // make sure we have all current references:
+ loc.refresh();
+ for(Item li=loc.first();li!=null;li=loc.next(li)) {
+ URI uri=loc.get(li);
+ if(host!=null && !host.equals(uri.getHost())) {
+ break;
+ }
+ try {
+ ret = retryable.code(new HRcli(this,uri,li,ss));
+ access.log(Level.DEBUG,"Success calling",uri,"during call to all services");
+ } catch (APIException | CadiException e) {
+ Throwable t = e.getCause();
+ if(t!=null && t instanceof ConnectException) {
+ loc.invalidate(li);
+ access.log(Level.ERROR,"Connection to",uri,"refused during call to all services");
+ } else if(t instanceof SSLHandshakeException) {
+ access.log(Level.ERROR,t.getMessage());
+ loc.invalidate(li);
+ } else if(t instanceof SocketException) {
+ if("java.net.SocketException: Connection reset".equals(t.getMessage())) {
+ access.log(Level.ERROR, t.getMessage(), " can mean Certificate Expiration or TLS Protocol issues");
+ }
+ retryable.item(null);
+ throw e;
+ } else {
+ throw e;
+ }
+ } catch (ConnectException e) {
+ loc.invalidate(li);
+ access.log(Level.ERROR,"Connection to",uri,"refused during call to all services");
+ }
+ }
+
+ if(ret == null && notify)
+ throw new LocatorException("No available clients to call");
+ return ret;
+ }
+
+
+ public void close() {
+ // TODO Anything here?
+ }
+
+ public HMangr readTimeout(int timeout) {
+ this.readTimeout = timeout;
+ return this;
+ }
+
+ public int readTimeout() {
+ return readTimeout;
+ }
+
+ public void connectionTimeout(int t) {
+ connectionTimeout = t;
+ }
+
+ public int connectionTimout() {
+ return connectionTimeout;
+ }
+
+ public HMangr apiVersion(String version) {
+ apiVersion = version;
+ return this;
+ }
+
+ public String apiVersion() {
+ return apiVersion;
+ }
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HNoAuthSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HNoAuthSS.java
new file mode 100644
index 00000000..b857f3ad
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HNoAuthSS.java
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.client.AbsAuthentication;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+
+public class HNoAuthSS extends AbsAuthentication<HttpURLConnection> {
+ public HNoAuthSS(SecurityInfoC<HttpURLConnection> si) throws IOException {
+ super(si,"noauth",null);
+ }
+
+ @Override
+ public void setSecurity(HttpURLConnection client) throws CadiException {
+ if(securityInfo!=null && client instanceof HttpsURLConnection) {
+ securityInfo.setSocketFactoryOn((HttpsURLConnection)client);
+ }
+ }
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HRcli.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HRcli.java
new file mode 100644
index 00000000..f289b0e3
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HRcli.java
@@ -0,0 +1,130 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.http;
+
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.client.EClient;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data.TYPE;
+
+/**
+ * Rosetta Client
+ *
+ * JAXB defined JSON or XML over HTTP/S
+ *
+ * @author Jonathan
+ *
+ * @param <T>
+ */
+public class HRcli extends Rcli<HttpURLConnection> {
+ private HMangr hman;
+ private Item item;
+ private SecuritySetter<HttpURLConnection> ss;
+
+ public HRcli(HMangr hman, Item locItem, SecuritySetter<HttpURLConnection> secSet) throws URISyntaxException, LocatorException {
+ item=locItem;
+ uri=hman.loc.get(locItem);
+ this.hman = hman;
+ ss=secSet;
+ type = TYPE.JSON;
+ apiVersion = hman.apiVersion();
+ }
+
+ public HRcli(HMangr hman, URI uri, Item locItem, SecuritySetter<HttpURLConnection> secSet) {
+ locItem=item;
+ this.uri = uri;
+ this.hman = hman;
+ ss=secSet;
+ type = TYPE.JSON;
+ apiVersion = hman.apiVersion();
+ }
+
+ @Override
+ protected HRcli clone(URI uri, SecuritySetter<HttpURLConnection> ss) {
+ return new HRcli(hman,uri,item,ss);
+ }
+
+
+
+ /**
+ *
+ * @return
+ * @throws APIException
+ * @throws DME2Exception
+ */
+ protected EClient<HttpURLConnection> client() throws CadiException {
+ try {
+ if(uri==null) {
+ Item item = hman.loc.best();
+ if(item==null) {
+ throw new CadiException("No service available for " + hman.loc.toString());
+ }
+ uri = hman.loc.get(item);
+ }
+ return new HClient(ss,uri,connectionTimeout);
+ } catch (Exception e) {
+ throw new CadiException(e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.cadi.client.Rcli#setSecuritySetter(org.onap.aaf.cadi.SecuritySetter)
+ */
+ @Override
+ public void setSecuritySetter(SecuritySetter<HttpURLConnection> ss) {
+ this.ss = ss;
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.cadi.client.Rcli#getSecuritySetter()
+ */
+ @Override
+ public SecuritySetter<HttpURLConnection> getSecuritySetter() {
+ return ss;
+ }
+
+ public void invalidate() throws CadiException {
+ try {
+ hman.loc.invalidate(item);
+ } catch (Exception e) {
+ throw new CadiException(e);
+ }
+ }
+
+ public HRcli setManager(HMangr hman) {
+ this.hman = hman;
+ return this;
+ }
+
+ public String toString() {
+ return uri.toString();
+ }
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTokenSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTokenSS.java
new file mode 100644
index 00000000..873e0fe7
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTokenSS.java
@@ -0,0 +1,34 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import org.onap.aaf.cadi.config.SecurityInfoC;
+
+public class HTokenSS extends HAuthorizationHeader {
+ public HTokenSS(final SecurityInfoC<HttpURLConnection> si, final String client_id, final String token) throws IOException {
+ super(si, client_id,"Bearer " + token);
+ }
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTransferSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTransferSS.java
new file mode 100644
index 00000000..d19c42e9
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTransferSS.java
@@ -0,0 +1,64 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.client.AbsTransferSS;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+
+
+public class HTransferSS extends AbsTransferSS<HttpURLConnection> {
+ public HTransferSS(TaggedPrincipal principal, String app) throws IOException {
+ super(principal, app);
+ }
+
+ public HTransferSS(TaggedPrincipal principal, String app, SecurityInfoC<HttpURLConnection> si) {
+ super(principal, app, si);
+ }
+
+ @Override
+ public void setSecurity(HttpURLConnection huc) throws CadiException {
+ if(defSS==null) {
+ throw new CadiException("Need App Credentials to send message");
+ }
+ defSS.setSecurity(huc);
+ if(value!=null) {
+ huc.addRequestProperty(Config.CADI_USER_CHAIN, value);
+ }
+ if(securityInfo!=null) {
+ securityInfo.setSocketFactoryOn((HttpsURLConnection)huc);
+ }
+ }
+
+ @Override
+ public int setLastResponse(int respCode) {
+ return 0;
+ }
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HX509SS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HX509SS.java
new file mode 100644
index 00000000..9d555f62
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HX509SS.java
@@ -0,0 +1,152 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.http;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.X509KeyManager;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.client.AbsAuthentication;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Chrono;
+
+
+public class HX509SS implements SecuritySetter<HttpURLConnection> {
+ private static final byte[] X509 = "x509 ".getBytes();
+ private PrivateKey priv;
+ private byte[] pub;
+ private String cert;
+ private SecurityInfoC<HttpURLConnection> securityInfo;
+ private String algo;
+ private String alias;
+ private static int count = new SecureRandom().nextInt();
+
+ public HX509SS(SecurityInfoC<HttpURLConnection> si) throws APIException, CadiException {
+ this(null,si,false);
+ }
+
+ public HX509SS(SecurityInfoC<HttpURLConnection> si, boolean asDefault) throws APIException, CadiException {
+ this(null,si,asDefault);
+ }
+
+ public HX509SS(final String sendAlias, SecurityInfoC<HttpURLConnection> si) throws APIException, CadiException {
+ this(sendAlias, si, false);
+ }
+
+ public HX509SS(final String sendAlias, SecurityInfoC<HttpURLConnection> si, boolean asDefault) throws APIException, CadiException {
+ securityInfo = si;
+ if((alias=sendAlias) == null) {
+ if(si.default_alias == null) {
+ throw new APIException("JKS Alias is required to use X509SS Security. Use " + Config.CADI_ALIAS +" to set default alias");
+ } else {
+ alias = si.default_alias;
+ }
+ }
+
+ priv=null;
+ X509KeyManager[] xkms = si.getKeyManagers();
+ if(xkms==null || xkms.length==0) {
+ throw new APIException("There are no valid keys available in given Keystores. Wrong Keypass? Expired?");
+ }
+ for(int i=0;priv==null&&i<xkms.length;++i) {
+ priv = xkms[i].getPrivateKey(alias);
+ }
+ try {
+ for(int i=0;cert==null&&i<xkms.length;++i) {
+ X509Certificate[] chain = xkms[i].getCertificateChain(alias);
+ if(chain!=null&&chain.length>0) {
+ algo = chain[0].getSigAlgName();
+ pub = chain[0].getEncoded();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(pub.length*2);
+ ByteArrayInputStream bais = new ByteArrayInputStream(pub);
+ Symm.base64noSplit.encode(bais,baos,X509);
+ cert = baos.toString();
+ }
+ }
+ } catch (CertificateEncodingException | IOException e) {
+ throw new CadiException(e);
+ }
+ if(algo==null) {
+ throw new APIException("X509 Security Setter not configured");
+ }
+ }
+
+ @Override
+ public void setSecurity(HttpURLConnection huc) throws CadiException {
+ if(huc instanceof HttpsURLConnection) {
+ securityInfo.setSocketFactoryOn((HttpsURLConnection)huc);
+ }
+ if(alias==null) { // must be a one-way
+ huc.setRequestProperty(AbsAuthentication.AUTHORIZATION, cert);
+
+ // Test Signed content
+ try {
+ String data = "SignedContent["+ inc() + ']' + Chrono.dateTime();
+ huc.setRequestProperty("Data", data);
+
+ Signature sig = Signature.getInstance(algo);
+ sig.initSign(priv);
+ sig.update(data.getBytes());
+ byte[] signature = sig.sign();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(signature.length*1.3));
+ ByteArrayInputStream bais = new ByteArrayInputStream(signature);
+ Symm.base64noSplit.encode(bais, baos);
+ huc.setRequestProperty("Signature", new String(baos.toByteArray()));
+
+ } catch (Exception e) {
+ throw new CadiException(e);
+ }
+ }
+ }
+
+ private synchronized int inc() {
+ return ++count;
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.cadi.SecuritySetter#getID()
+ */
+ @Override
+ public String getID() {
+ return alias;
+ }
+
+ @Override
+ public int setLastResponse(int respCode) {
+ return 0;
+ }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/DNSLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/DNSLocator.java
new file mode 100644
index 00000000..655a0c22
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/DNSLocator.java
@@ -0,0 +1,210 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.locator;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Access.Level;
+
+public class DNSLocator implements Locator<URI> {
+ private static enum Status {UNTRIED, OK, INVALID, SLOW};
+ private static final int CHECK_TIME = 3000;
+
+ private String host, protocol;
+ private Access access;
+ private Host[] hosts;
+ private int startPort, endPort;
+ private String suffix;
+
+ public DNSLocator(Access access, String protocol, String host, String range) {
+ this.host = host;
+ this.protocol = protocol;
+ this.access = access;
+ int dash = range.indexOf('-');
+ if(dash<0) {
+ startPort = endPort = Integer.parseInt(range);
+ } else {
+ startPort = Integer.parseInt(range.substring(0,dash));
+ endPort = Integer.parseInt(range.substring(dash + 1));
+ }
+ refresh();
+ }
+
+ public DNSLocator(Access access, String aaf_locate) throws LocatorException {
+ this.access = access;
+ if(aaf_locate==null) {
+ throw new LocatorException("Null passed into DNSLocator constructor");
+ }
+ int start, port;
+ if(aaf_locate.startsWith("https:")) {
+ protocol = "https:";
+ start = 9; // https://
+ port = 443;
+ } else if(aaf_locate.startsWith("http:")) {
+ protocol = "http:";
+ start = 8; // http://
+ port = 80;
+ } else {
+ throw new LocatorException("DNSLocator accepts only https or http protocols. (requested URL " + aaf_locate + ')');
+ }
+
+ int colon = aaf_locate.indexOf(':',start);
+ int slash;
+ if(colon>0) {
+ start = colon+1;
+ int left = aaf_locate.indexOf('[',start);
+ if(left>0) {
+ int right = aaf_locate.indexOf(']',left+1);
+ if(right>0) {
+ int dash = aaf_locate.indexOf('-',left+1);
+ if(dash<0) {
+ startPort = endPort = Integer.parseInt(aaf_locate.substring(left+1,right));
+ } else {
+ startPort = Integer.parseInt(aaf_locate.substring(left+1,dash));
+ endPort = Integer.parseInt(aaf_locate.substring(dash + 1,right));
+ }
+ }
+
+ } else {
+ slash = aaf_locate.indexOf('/',colon+1);
+ if(slash<0) {
+ startPort = endPort = Integer.parseInt(aaf_locate.substring(start));
+ } else {
+ startPort = endPort = Integer.parseInt(aaf_locate.substring(start,slash));
+ }
+ }
+ } else {
+ startPort = endPort = port;
+ }
+ }
+
+ @Override
+ public URI get(Item item) throws LocatorException {
+ return hosts[((DLItem)item).cnt].uri;
+ }
+
+ @Override
+ public boolean hasItems() {
+ for(Host h : hosts) {
+ if(h.status==Status.OK) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void invalidate(Item item) {
+ DLItem di = (DLItem)item;
+ hosts[di.cnt].status = Status.INVALID;
+ }
+
+ @Override
+ public Item best() throws LocatorException {
+ // not a good "best"
+ for(int i=0;i<hosts.length;++i) {
+ switch(hosts[i].status) {
+ case OK:
+ return new DLItem(i);
+ case INVALID:
+ break;
+ case SLOW:
+ break;
+ case UNTRIED:
+ try {
+ if(hosts[i].ia.isReachable(CHECK_TIME)) {
+ hosts[i].status = Status.OK;
+ return new DLItem(i);
+ }
+ } catch (IOException e) {
+ throw new LocatorException(e);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ throw new LocatorException("No Available URIs for " + host);
+ }
+
+ @Override
+ public Item first() throws LocatorException {
+ return new DLItem(0);
+ }
+
+ @Override
+ public Item next(Item item) throws LocatorException {
+ DLItem di = (DLItem)item;
+ if(++di.cnt<hosts.length) {
+ return di;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean refresh() {
+ try {
+ InetAddress[] ias = InetAddress.getAllByName(host);
+ Host[] temp = new Host[ias.length * (1 + endPort - startPort)];
+ int cnt = -1;
+ for(int j=startPort; j<=endPort; ++j) {
+ for(int i=0;i<ias.length;++i) {
+ temp[++cnt] = new Host(ias[i], j, suffix);
+ }
+ }
+ hosts = temp;
+ return true;
+ } catch (Exception e) {
+ access.log(Level.ERROR, e);
+ }
+ return false;
+ }
+
+ private class Host {
+ private URI uri;
+ private InetAddress ia;
+ private Status status;
+
+ public Host(InetAddress inetAddress, int port, String suffix) throws URISyntaxException {
+ ia = inetAddress;
+ uri = new URI(protocol,null,inetAddress.getHostAddress(),port,suffix,null,null);
+ status = Status.UNTRIED;
+ }
+ }
+
+ private class DLItem implements Item {
+ public DLItem(int i) {
+ cnt = i;
+ }
+
+ private int cnt;
+ }
+
+ public void destroy() {}
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HClientHotPeerLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HClientHotPeerLocator.java
new file mode 100644
index 00000000..b97768a6
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HClientHotPeerLocator.java
@@ -0,0 +1,60 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.locator;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.http.HClient;
+import org.onap.aaf.cadi.http.HX509SS;
+
+public class HClientHotPeerLocator extends HotPeerLocator<HClient> {
+ private final HX509SS ss;
+
+ public HClientHotPeerLocator(Access access, String urlstr, long invalidateTime, String localLatitude,
+ String localLongitude, HX509SS ss) throws LocatorException {
+ super(access, urlstr, invalidateTime, localLatitude, localLongitude);
+
+ this.ss = ss;
+ }
+
+ @Override
+ protected HClient _newClient(String clientInfo) throws LocatorException {
+ try {
+ int idx = clientInfo.indexOf('/');
+ return new HClient(ss,new URI("https://"+(idx<0?clientInfo:clientInfo.substring(0, idx))),3000);
+ } catch (URISyntaxException e) {
+ throw new LocatorException(e);
+ }
+ }
+
+ @Override
+ protected HClient _invalidate(HClient client) {
+ return null;
+ }
+
+ @Override
+ protected void _destroy(HClient client) {
+ }
+} \ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HotPeerLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HotPeerLocator.java
new file mode 100644
index 00000000..17f9bafb
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HotPeerLocator.java
@@ -0,0 +1,302 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.locator;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.routing.GreatCircle;
+import org.onap.aaf.misc.env.util.Split;
+
+/**
+ * This Locator is to handle Hot Peer load protection, when the Servers are
+ * 1) Static
+ * 2) Well known client URL
+ *
+ * The intention is to change traffic over to the Hot Peer, if a server goes down, and reinstate
+ * when it is back up.
+ *
+ * Example of this kind of Service is a MS Certificate Server
+ *
+ * @author Jonathan
+ *
+ * @param <CLIENT>
+ */
+public abstract class HotPeerLocator<CLIENT> implements Locator<CLIENT> {
+ private final String[] urlstrs;
+ private final CLIENT[] clients;
+ private final long[] failures;
+ private final double[] distances;
+ private int preferred;
+ private long invalidateTime;
+ private Thread refreshThread;
+ protected Access access;
+
+ /**
+ * Construct: Expect one or more Strings in the form:
+ * 192.555.112.223:39/38.88087/-77.30122
+ * separated by commas
+ *
+ * @param trans
+ * @param urlstr
+ * @param invalidateTime
+ * @param localLatitude
+ * @param localLongitude
+ * @throws LocatorException
+ */
+ @SuppressWarnings("unchecked")
+ protected HotPeerLocator(Access access, final String urlstr, final long invalidateTime, final String localLatitude, final String localLongitude) throws LocatorException {
+ this.access = access;
+ urlstrs = Split.split(',', urlstr);
+ clients = (CLIENT[])new Object[urlstrs.length];
+ failures = new long[urlstrs.length];
+ distances= new double[urlstrs.length];
+ this.invalidateTime = invalidateTime;
+
+ double distance = Double.MAX_VALUE;
+ for(int i=0;i<urlstrs.length;++i) {
+ String[] info = Split.split('/', urlstrs[i]);
+ if(info.length<3) {
+ throw new LocatorException("Configuration needs LAT and LONG, i.e. ip:port/lat/long");
+ }
+ try {
+ clients[i] = _newClient(urlstrs[i]);
+ failures[i] = 0L;
+ } catch(LocatorException le) {
+ failures[i] = System.currentTimeMillis()+invalidateTime;
+ }
+
+ double d = GreatCircle.calc(info[1],info[2],localLatitude,localLongitude);
+ distances[i]=d;
+
+ // find preferred server
+ if(d<distance) {
+ preferred = i;
+ distance=d;
+ }
+ }
+
+ access.printf(Level.INIT,"Preferred Client is %s",urlstrs[preferred]);
+ for(int i=0;i<urlstrs.length;++i) {
+ if(i!=preferred) {
+ access.printf(Level.INIT,"Alternate Client is %s",urlstrs[i]);
+ }
+ }
+ }
+
+ protected abstract CLIENT _newClient(String hostInfo) throws LocatorException;
+ /**
+ * If client can reconnect, then return. Otherwise, destroy and return null;
+ * @param client
+ * @return
+ * @throws LocatorException
+ */
+ protected abstract CLIENT _invalidate(CLIENT client);
+
+ protected abstract void _destroy(CLIENT client);
+
+ @Override
+ public Item best() throws LocatorException {
+ if(failures[preferred]==0L) {
+ return new HPItem(preferred);
+ } else {
+ long now = System.currentTimeMillis();
+ double d = Double.MAX_VALUE;
+ int best = -1;
+ boolean tickle = false;
+ // try for best existing client
+ for(int i=0;i<urlstrs.length;++i) {
+ if(failures[i]<now && distances[i]<d) {
+ if(clients[i]!=null) {
+ best = i;
+ break;
+ } else {
+ tickle = true; // There's some failed clients which can be restored
+ }
+ }
+ }
+ if(best<0 && tickle) {
+ tickle=false;
+ if(refresh()) {
+ // try again
+ for(int i=0;i<urlstrs.length;++i) {
+ if(failures[i]==0L && distances[i]<d) {
+ if(clients[i]!=null) {
+ best = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * If a valid client is available, but there are some that can refresh, return the client immediately
+ * but start a Thread to do the background Client setup.
+ */
+ if(tickle) {
+ synchronized(clients) {
+ if(refreshThread==null) {
+ refreshThread = new Thread(new Runnable(){
+ @Override
+ public void run() {
+ refresh();
+ refreshThread = null;
+ }
+ });
+ refreshThread.setDaemon(true);
+ refreshThread.start();
+ }
+ }
+ }
+
+ if(best<0) {
+ throw new LocatorException("No Clients available");
+ }
+
+
+ return new HPItem(best);
+ }
+ }
+
+
+ @Override
+ public CLIENT get(Item item) throws LocatorException {
+ HPItem hpi = (HPItem)item;
+ CLIENT c = clients[hpi.idx];
+ if(c==null) {
+ if(failures[hpi.idx]>System.currentTimeMillis()) {
+ throw new LocatorException("Client requested is invalid");
+ } else {
+ synchronized(clients) {
+ c = _newClient(urlstrs[hpi.idx]);
+ failures[hpi.idx]=0L;
+ }
+ }
+ } else if(failures[hpi.idx]>0){
+ throw new LocatorException("Client requested is invalid");
+ }
+ return c;
+ }
+
+ public String info(Item item) {
+ HPItem hpi = (HPItem)item;
+ if(hpi!=null && hpi.idx<urlstrs.length) {
+ return urlstrs[hpi.idx];
+ } else {
+ return "Invalid Item";
+ }
+ }
+
+ @Override
+ public boolean hasItems() {
+ for(int i=0;i<clients.length;++i) {
+ if(clients[i]!=null && failures[i]==0L) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public synchronized void invalidate(Item item) throws LocatorException {
+ HPItem hpi = (HPItem)item;
+ failures[hpi.idx] = System.currentTimeMillis() + invalidateTime;
+ CLIENT c = clients[hpi.idx];
+ clients[hpi.idx] = _invalidate(c);
+ }
+
+ @Override
+ public Item first() throws LocatorException {
+ return new HPItem(0);
+ }
+
+ @Override
+ public Item next(Item item) throws LocatorException {
+ HPItem hpi = (HPItem)item;
+ if(++hpi.idx>=clients.length) {
+ return null;
+ }
+ return hpi;
+ }
+
+ @Override
+ public boolean refresh() {
+ boolean force = !hasItems(); // If no Items at all, reset
+ boolean rv = true;
+ long now = System.currentTimeMillis();
+ for(int i=0;i<clients.length;++i) {
+ if(failures[i]>0L && (failures[i]<now || force)) { // retry
+ try {
+ synchronized(clients) {
+ if(clients[i]==null) {
+ clients[i]=_newClient(urlstrs[i]);
+ }
+ failures[i]=0L;
+ }
+ } catch (LocatorException e) {
+ failures[i]=now+invalidateTime;
+ rv = false;
+ }
+ }
+ }
+ return rv;
+ }
+
+ @Override
+ public void destroy() {
+ for(int i=0;i<clients.length;++i) {
+ if(clients[i]!=null) {
+ _destroy(clients[i]);
+ clients[i] = null;
+ }
+ }
+ }
+
+ private static class HPItem implements Item {
+ private int idx;
+
+ public HPItem(int i) {
+ idx = i;
+ }
+ }
+
+
+ /*
+ * Convenience Functions
+ */
+ public CLIENT bestClient() throws LocatorException {
+ return get(best());
+ }
+
+ public boolean invalidate(CLIENT client) throws LocatorException {
+ for(int i=0;i<clients.length;++i) {
+ if(clients[i]==client) { // yes, "==" is appropriate here.. Comparing Java Object Reference
+ invalidate(new HPItem(i));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ } \ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/PropertyLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/PropertyLocator.java
new file mode 100644
index 00000000..244a43bd
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/PropertyLocator.java
@@ -0,0 +1,289 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.locator;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.misc.env.util.Split;
+
+public class PropertyLocator implements Locator<URI> {
+ private final URI [] orig;
+ private PLItem[] current;
+ private int end;
+ private final SecureRandom random;
+ private URI[] resolved;
+ private long lastRefreshed;
+ private long minRefresh;
+ private long backgroundRefresh;
+
+ public PropertyLocator(String locList) throws LocatorException {
+ this(locList,10000L, 1000*60*20); // defaults, do not refresh more than once in 10 seconds, Refresh Locator every 20 mins.
+ }
+ /**
+ * comma delimited root url list
+ *
+ * @param locList
+ * @throws LocatorException
+ */
+ public PropertyLocator(String locList, long minRefreshMillis, long backgroundRefreshMillis) throws LocatorException {
+ minRefresh = minRefreshMillis;
+ backgroundRefresh = backgroundRefreshMillis;
+ lastRefreshed=0L;
+ if(locList==null) {
+ throw new LocatorException("No Location List given for PropertyLocator");
+ }
+ String[] locarray = Split.split(',',locList);
+ List<URI> uriList = new ArrayList<URI>();
+
+ random = new SecureRandom();
+
+ for(int i=0;i<locarray.length;++i) {
+ try {
+ int range = locarray[i].indexOf(":[");
+ if(range<0) {
+ uriList.add(new URI(locarray[i]));
+ } else {
+ String mach_colon = locarray[i].substring(0, range+1);
+ int dash = locarray[i].indexOf('-',range+2);
+ int brac = locarray[i].indexOf(']',dash+1);
+ int slash = locarray[i].indexOf('/',brac);
+ int start = Integer.parseInt(locarray[i].substring(range+2, dash));
+ int end = Integer.parseInt(locarray[i].substring(dash+1, brac));
+ for(int port=start;port<=end;++port) {
+ uriList.add(new URI(mach_colon+port + (slash>=0?locarray[i].substring(slash):"")));
+ }
+ }
+ } catch (NumberFormatException nf) {
+ throw new LocatorException("Invalid URI format: " + locarray[i]);
+ } catch (URISyntaxException e) {
+ throw new LocatorException(e);
+ }
+ }
+ orig = new URI[uriList.size()];
+ uriList.toArray(orig);
+
+ refresh();
+ new Timer("PropertyLocator Refresh Timer",true).scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ refresh();
+ }
+ }, backgroundRefresh,backgroundRefresh);
+ }
+
+ @Override
+ public URI get(Item item) throws LocatorException {
+ synchronized(orig) {
+ if(item==null) {
+ return null;
+ } else {
+ return resolved[((PLItem)item).idx];
+ }
+ }
+ }
+
+ @Override
+ public Item first() throws LocatorException {
+ return end>0?current[0]:null;
+ }
+
+ @Override
+ public boolean hasItems() {
+ return end>0;
+ }
+
+ @Override
+ public Item next(Item item) throws LocatorException {
+ if(item==null) {
+ return null;
+ } else {
+ int spot;
+ if((spot=(((PLItem)item).order+1))>=end)return null;
+ return current[spot];
+ }
+ }
+
+ @Override
+ public synchronized void invalidate(Item item) throws LocatorException {
+ if(--end<=0) {
+ refresh();
+ return;
+ }
+ if(item==null) {
+ return;
+ }
+ PLItem pli = (PLItem)item;
+ int i,order;
+ for(i=0;i<end;++i) {
+ if(pli==current[i])break;
+ }
+ order = current[i].order;
+ for(;i<end;++i) {
+ current[i]=current[i+1];
+ current[i].order=order++;
+ }
+ current[end]=pli;
+ }
+
+ @Override
+ public Item best() throws LocatorException {
+ if(current.length==0) {
+ refresh();
+ }
+ switch(current.length) {
+ case 0:
+ return null;
+ case 1:
+ return current[0];
+ default:
+ return current[Math.abs(random.nextInt())%current.length];
+ }
+ }
+
+ @Override
+ public synchronized boolean refresh() {
+ if(System.currentTimeMillis()>lastRefreshed) {
+ // Build up list
+ List<URI> resolve = new ArrayList<URI>();
+ String realname;
+ for(int i = 0; i < orig.length ; ++i) {
+ try {
+ InetAddress ia[] = InetAddress.getAllByName(orig[i].getHost());
+
+ URI o,n;
+ for(int j=0;j<ia.length;++j) {
+ o = orig[i];
+ Socket socket = new Socket();
+ try {
+ realname=ia[j].getHostAddress().equals(ia[j].getHostName())?ia[j].getCanonicalHostName():ia[j].getHostName();
+ int port = o.getPort();
+ if(port<0) { // default
+ port = "https".equalsIgnoreCase(o.getScheme())?443:80;
+ }
+ socket.connect(new InetSocketAddress(realname,port),3000);
+ if(socket.isConnected()) {
+ n = new URI(
+ o.getScheme(),
+ o.getUserInfo(),
+ realname,
+ o.getPort(),
+ o.getPath(),
+ o.getQuery(),
+ o.getFragment()
+ );
+ resolve.add(n);
+ }
+ } catch (IOException e) {
+ } finally {
+ if(!socket.isClosed()) {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ // nothing to do.
+ }
+ }
+ }
+ }
+ } catch (UnknownHostException | URISyntaxException e) {
+ // Note: Orig Name already known as valid, based on constructor
+ }
+ }
+ end=resolve.size();
+ PLItem[] newCurrent;
+ if(current==null || current.length!=end) {
+ newCurrent = new PLItem[end];
+ } else {
+ newCurrent = current;
+ }
+
+ for(int i=0; i< end; ++i) {
+ if(newCurrent[i]==null){
+ newCurrent[i]=new PLItem(i);
+ } else {
+ newCurrent[i].idx=newCurrent[i].order=i;
+ }
+ }
+ synchronized(orig) {
+ resolved = new URI[end];
+ resolve.toArray(resolved);
+ current = newCurrent;
+ }
+ lastRefreshed = System.currentTimeMillis()+minRefresh;
+ return !resolve.isEmpty();
+ } else {
+ return false;
+ }
+ }
+
+ private class PLItem implements Item {
+ public int idx,order;
+
+ public PLItem(int i) {
+ idx = order =i;
+ }
+
+ public String toString() {
+ return "Item: " + idx + " order: " + order;
+ }
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for(URI uri : orig) {
+ boolean isResolved=false;
+ if(uri!=null) {
+ if(first) {
+ first = false;
+ } else {
+ sb.append(", ");
+ }
+ sb.append(uri.toString());
+ sb.append(" [");
+ for(URI u2 : resolved) {
+ if(uri.equals(u2)) {
+ isResolved = true;
+ break;
+ }
+ }
+ sb.append(isResolved?"X]\n":" ]");
+ }
+ }
+ return sb.toString();
+ }
+
+ public void destroy() {
+ }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/routing/GreatCircle.java b/cadi/client/src/main/java/org/onap/aaf/cadi/routing/GreatCircle.java
new file mode 100644
index 00000000..36906188
--- /dev/null
+++ b/cadi/client/src/main/java/org/onap/aaf/cadi/routing/GreatCircle.java
@@ -0,0 +1,188 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.routing;
+
+import org.onap.aaf.misc.env.util.Split;
+
+public class GreatCircle {
+ // Note: multiplying by this constant is faster than calling Math equivalent function
+ private static final double DEGREES_2_RADIANS = Math.PI/180.0;
+
+ public static final double DEGREES_2_NM = 60;
+ public static final double DEGREES_2_KM = DEGREES_2_NM * 1.852; // 1.852 is exact ratio per 1929 Standard Treaty, adopted US 1954
+ public static final double DEGREES_2_MI = DEGREES_2_NM * 1.1507795;
+
+ /**
+ *
+ * Calculate the length of an arc on a perfect sphere based on Latitude and Longitudes of two points
+ * Parameters are in Degrees (i.e. the coordinate system you get from GPS, Mapping WebSites, Phones, etc)
+ *
+ * L1 = Latitude of point A
+ * G1 = Longitude of point A
+ * L2 = Latitude of point B
+ * G2 = Longitude of point B
+ *
+ * d = acos (sin(L1)*sin(L2) + cos(L1)*cos(L2)*cos(G1 - G2))
+ *
+ * Returns answer in Degrees
+ *
+ * Since there are 60 degrees per nautical miles, you can convert to NM by multiplying by 60
+ *
+ * Essential formula from a Princeton website, the "Law of Cosines" method.
+ *
+ * Refactored cleaned up for speed Jonathan 3/8/2013
+ *
+ * @param latA
+ * @param lonA
+ * @param latB
+ * @param lonB
+ * @return
+ */
+ public static double calc(double latA, double lonA, double latB, double lonB) {
+ // Formula requires Radians. Expect Params to be Coordinates (Degrees)
+ // Simple ratio, quicker than calling Math.toRadians()
+ latA *= DEGREES_2_RADIANS;
+ lonA *= DEGREES_2_RADIANS;
+ latB *= DEGREES_2_RADIANS;
+ lonB *= DEGREES_2_RADIANS;
+
+ return Math.acos(
+ Math.sin(latA) * Math.sin(latB) +
+ Math.cos(latA) * Math.cos(latB) * Math.cos(lonA-lonB)
+ )
+ / DEGREES_2_RADIANS;
+ }
+
+ /**
+ * Convert from "Lat,Long Lat,Long" String format
+ * "Lat,Long,Lat,Long" Format
+ * or all four entries "Lat Long Lat Long"
+ *
+ * (Convenience function)
+ *
+ * Since Distance is positive, a "-1" indicates an error in String formatting
+ */
+ public static double calc(String ... coords) {
+ try {
+ String [] array;
+ switch(coords.length) {
+ case 1:
+ array = Split.split(',',coords[0]);
+ if(array.length!=4)return -1;
+ return calc(
+ Double.parseDouble(array[0]),
+ Double.parseDouble(array[1]),
+ Double.parseDouble(array[2]),
+ Double.parseDouble(array[3])
+ );
+ case 2:
+ array = Split.split(',',coords[0]);
+ String [] array2 = Split.split(',',coords[1]);
+ if(array.length!=2 || array2.length!=2)return -1;
+ return calc(
+ Double.parseDouble(array[0]),
+ Double.parseDouble(array[1]),
+ Double.parseDouble(array2[0]),
+ Double.parseDouble(array2[1])
+ );
+ case 4:
+ return calc(
+ Double.parseDouble(coords[0]),
+ Double.parseDouble(coords[1]),
+ Double.parseDouble(coords[2]),
+ Double.parseDouble(coords[3])
+ );
+
+ default:
+ return -1;
+ }
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+
+}
+
+///**
+//* Haverside method, from Princeton
+//*
+//* @param alat
+//* @param alon
+//* @param blat
+//* @param blon
+//* @return
+//*/
+//public static double calc3(double alat, double alon, double blat, double blon) {
+// alat *= DEGREES_2_RADIANS;
+// alon *= DEGREES_2_RADIANS;
+// blat *= DEGREES_2_RADIANS;
+// blon *= DEGREES_2_RADIANS;
+// return 2 * Math.asin(
+// Math.min(1, Math.sqrt(
+// Math.pow(Math.sin((blat-alat)/2), 2) +
+// (Math.cos(alat)*Math.cos(blat)*
+// Math.pow(
+// Math.sin((blon-alon)/2),2)
+// )
+// )
+// )
+// )
+// / DEGREES_2_RADIANS;
+//}
+//
+
+
+
+//This is a MEAN radius. The Earth is not perfectly spherical
+// public static final double EARTH_RADIUS_KM = 6371.0;
+// public static final double EARTH_RADIUS_NM = 3440.07;
+// public static final double KM_2_MILES_RATIO = 0.621371192;
+///**
+//* Code on Internet based on Unknown book. Lat/Long is in Degrees
+//* @param alat
+//* @param alon
+//* @param blat
+//* @param blon
+//* @return
+//*/
+//public static double calc1(double alat, double alon, double blat, double blon) {
+// alat *= DEGREES_2_RADIANS;
+// alon *= DEGREES_2_RADIANS;
+// blat *= DEGREES_2_RADIANS;
+// blon *= DEGREES_2_RADIANS;
+//
+// // Reused values
+// double cosAlat,cosBlat;
+//
+// return Math.acos(
+// ((cosAlat=Math.cos(alat))*Math.cos(alon)*(cosBlat=Math.cos(blat))*Math.cos(blon)) +
+// (cosAlat*Math.sin(alon)*cosBlat*Math.sin(blon)) +
+// (Math.sin(alat)*Math.sin(blat))
+// )/DEGREES_2_RADIANS;
+//
+//}
+
+/*
+* This method was 50% faster than calculation 1, and 75% than the Haverside method
+* Also, since it's based off of Agree standard Degrees of the Earth, etc, the calculations are more exact,
+* at least for Nautical Miles and Kilometers
+*/