diff options
author | 2021-04-08 16:40:06 +0100 | |
---|---|---|
committer | 2021-04-21 13:49:22 +0100 | |
commit | 71d3d0925874247de5e657821638b1c08360f571 (patch) | |
tree | 91bcca2c8bd53c3ee54269c3101538cba70fa588 /dmaap-bc/src/main/java/org/onap | |
parent | 20221353bdb213a1f143a132d819d4e0810e3794 (diff) |
[DMAAP-BC] Consolidate bus controller repos
Migrate BC CSITs to the repo
Fix documentation
Signed-off-by: efiacor <fiachra.corcoran@est.tech>
Change-Id: I874ed61be1b61187e233e9fd0937f6658f0ec0f0
Issue-ID: DMAAP-1544
Diffstat (limited to 'dmaap-bc/src/main/java/org/onap')
100 files changed, 14748 insertions, 0 deletions
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafConnection.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafConnection.java new file mode 100644 index 0000000..934e541 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafConnection.java @@ -0,0 +1,329 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.aaf; + + + + + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.ProtocolException; +import java.net.URL; +import java.net.UnknownHostException; +import java.net.ConnectException; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.apache.commons.codec.binary.Base64; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + + +public class AafConnection extends BaseLoggingClass { + + + + + + private String aafCred; + private String unit_test; + + + private HttpsURLConnection uc; + + + public AafConnection( String cred ) { + aafCred = cred; + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + unit_test = p.getProperty( "UnitTest", "No" ); + + } + + + private boolean makeConnection( String pURL ) { + + try { + URL u = new URL( pURL ); + uc = (HttpsURLConnection) u.openConnection(); + uc.setInstanceFollowRedirects(false); + logger.info( "successful connect to " + pURL ); + return(true); + } catch ( UnknownHostException uhe ) { + errorLogger.error(DmaapbcLogMessageEnum.UNKNOWN_HOST_EXCEPTION, pURL, uhe.getMessage() ); + logger.error("Error", uhe); + return(false); + } catch (Exception e) { + logger.error("Error", e); + errorLogger.error(DmaapbcLogMessageEnum.HTTP_CONNECTION_ERROR, pURL, e.getMessage()); + return(false); + } + + } + + static String bodyToString( InputStream is ) { + StringBuilder sb = new StringBuilder(); + BufferedReader br = new BufferedReader( new InputStreamReader(is)); + String line; + try { + while ((line = br.readLine()) != null ) { + sb.append( line ); + } + } catch (IOException ex ) { + errorLogger.error( DmaapbcLogMessageEnum.IO_EXCEPTION + ex.getMessage(),ex); + } + + return sb.toString(); + } + + + + public int postAaf( AafObject obj, String pURL ) { + logger.info( "entry: postAaf() to " + pURL ); + String auth = "Basic " + Base64.encodeBase64String(aafCred.getBytes()); + int rc = -1; + + + if ( ! makeConnection( pURL ) ) { + return rc; + }; + + + byte[] postData = obj.getBytes(); + //logger.info( "post fields=" + postData ); //byte isn't very readable + String responsemessage = null; + String responseBody = null; + + try { + if (auth != null) { + uc.setRequestProperty("Authorization", auth); + } + uc.setRequestMethod("POST"); + uc.setRequestProperty("Content-Type", "application/json"); + uc.setRequestProperty( "charset", "utf-8"); + uc.setRequestProperty( "Content-Length", Integer.toString( postData.length )); + uc.setUseCaches(false); + uc.setDoOutput(true); + + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + uc.setSSLSocketFactory(sc.getSocketFactory()); + OutputStream os = null; + + + try { + uc.connect(); + os = uc.getOutputStream(); + os.write( postData ); + + } catch (ProtocolException pe) { + logger.error("Error", pe); + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error("Error", e); + } + } catch ( SSLHandshakeException she ) { + logger.error("Error", she); + errorLogger.error( DmaapbcLogMessageEnum.SSL_HANDSHAKE_ERROR, pURL); + } catch ( UnknownHostException uhe ) { + logger.error("Error", uhe); + errorLogger.error(DmaapbcLogMessageEnum.UNKNOWN_HOST_EXCEPTION, pURL, uhe.getMessage() ); + rc = 500; + return rc; + } catch ( ConnectException ce ) { + logger.error("Error", ce); + if ( "Yes".equals(unit_test) ) { + rc = 201; + return rc; + } + errorLogger.error(DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, pURL, ce.getMessage() ); + rc = 500; + return rc; + } + try { + rc = uc.getResponseCode(); + } catch ( SSLHandshakeException she ) { + logger.error("Error", she); + errorLogger.error( DmaapbcLogMessageEnum.SSL_HANDSHAKE_ERROR, pURL); + rc = 500; + return rc; + } + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + if (responsemessage == null) { + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + if ( rc >= 200 && rc < 300 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + } else { + logger.warn( "Unsuccessful response: " + responsemessage ); + } + + } catch (Exception e) { + logger.error("Unable to read response "); + logger.error("Error", e); + } + finally { + try { + uc.disconnect(); + } catch ( Exception e ) { + logger.error("Error", e); + } + } + return rc; + + } + + public int delAaf(AafObject obj, String pURL) { + logger.info( "entry: delAaf() to " + pURL ); + String auth = "Basic " + Base64.encodeBase64String(aafCred.getBytes()); + int rc = -1; + + + if ( ! makeConnection( pURL ) ) { + return rc; + }; + + + byte[] postData = obj.getBytes(); + //logger.info( "post fields=" + postData ); //byte isn't very readable + String responsemessage = null; + String responseBody = null; + + try { + if (auth != null) { + uc.setRequestProperty("Authorization", auth); + } + uc.setRequestMethod("DELETE"); + uc.setRequestProperty("Content-Type", "application/json"); + uc.setRequestProperty( "charset", "utf-8"); + uc.setRequestProperty( "Content-Length", Integer.toString( postData.length )); + uc.setUseCaches(false); + uc.setDoOutput(true); + OutputStream os = null; + + + try { + uc.connect(); + os = uc.getOutputStream(); + os.write( postData ); + + } catch (ProtocolException pe) { + logger.error("Error", pe); + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error("Error", e); + } + } catch ( SSLHandshakeException she ) { + errorLogger.error( DmaapbcLogMessageEnum.SSL_HANDSHAKE_ERROR +"For:- "+pURL,she); + } + try { + rc = uc.getResponseCode(); + } catch ( SSLHandshakeException she ) { + logger.error("Error", she); + errorLogger.error( DmaapbcLogMessageEnum.SSL_HANDSHAKE_ERROR, pURL); + rc = 500; + return rc; + } + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + if (responsemessage == null) { + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + if ( rc >= 200 && rc < 300 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + } else { + logger.warn( "Unsuccessful response: " + responsemessage ); + } + + } catch (Exception e) { + logger.error("Unable to read response "); + logger.error("Error", e); + } + return rc; + + } + + private TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() + { + return null; + } + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) + { + //No need to implement. + } + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) + { + //No need to implement. + } + } + }; + + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafDecrypt.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafDecrypt.java new file mode 100644 index 0000000..bf5ecf2 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafDecrypt.java @@ -0,0 +1,58 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.aaf; + +import java.io.IOException; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +public class AafDecrypt extends BaseLoggingClass { + String dClass; + DecryptionInterface dec = null; + + public AafDecrypt() { + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + dClass = p.getProperty( "AafDecryption.Class", "org.onap.dmaap.dbcapi.aaf.ClearDecrypt"); + try { + dec = (DecryptionInterface) (Class.forName(dClass).newInstance()); + dec.init(p.getProperty("CredentialCodecKeyfile", "LocalKey")); + } catch (Exception ee ) { + logger.error("Error", ee); + errorLogger.error(DmaapbcLogMessageEnum.UNEXPECTED_CONDITION, "attempting to instantiate " + dClass ); + } + } + + public String decrypt( String encPwd ) { + + String pwd = "notDecrypted"; + try { + pwd = dec.decrypt( encPwd ); + } catch( IOException io ) { + logger.error("Error", io); + errorLogger.error(DmaapbcLogMessageEnum.DECRYPT_IO_ERROR, dClass, encPwd ); + } + + return pwd; + + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafEmpty.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafEmpty.java new file mode 100644 index 0000000..87e56c4 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafEmpty.java @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2019 Nokia 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.dmaap.dbcapi.aaf; + +class AafEmpty extends AafObject { + @Override + String toJSON() { + return ""; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafLurService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafLurService.java new file mode 100644 index 0000000..fa49ae8 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafLurService.java @@ -0,0 +1,140 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.aaf; + +import java.io.IOException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn; +import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp; +import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm; +import org.onap.aaf.cadi.principal.UnAuthPrincipal; +import org.onap.aaf.misc.env.APIException; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; + +/* + * this service uses the AAF Lur object to lookup identities and perms + */ +public class AafLurService extends BaseLoggingClass { + + private static AAFConHttp aafcon; + private static AAFLurPerm aafLur; + private static AAFAuthn<?> aafAuthn; + + + /* + * singleton pattern suggested by AAF + */ + private static AafLurService singleton; + private AafLurService() {} + + + + private static void init( Access myAccess ) throws APIException, CadiException, LocatorException { + appLogger.info( "myAccess=" + myAccess ); + try { + aafcon = new AAFConHttp( myAccess ); + } catch ( CadiException | LocatorException e) { + appLogger.error( "Failure of AAFConHttp: " + e.getMessage() ); + errorLogger.error( "Failure of AAFConHttp: " + e.getMessage() ); + errorLogger.error(e.getMessage()); + + throw e; + } + try { + aafLur = aafcon.newLur(); + } catch ( CadiException e) { + appLogger.error( "Failure of newLur(): " + e.getMessage() ); + errorLogger.error( "Failure of newLur(): " + e.getMessage() ); + errorLogger.error(e.getMessage()); + + throw e; + } + aafAuthn = aafcon.newAuthn( aafLur ); + } + + public static synchronized AafLurService getInstance( Access myAccess ) throws APIException, CadiException, LocatorException{ + if ( singleton == null ) { + singleton = new AafLurService(); + try { + init( myAccess ); + } catch (APIException | CadiException | LocatorException e) { + errorLogger.error(e.getMessage()); + throw e; + } + + } + return singleton; + } + + + public boolean checkPerm(String ns, String fqi, String pwd, DmaapPerm p) throws IOException, CadiException { + + boolean rc = false; + + if ( aafAuthn == null ) { + appLogger.error( "AafLurService: aafAuthn not set as expected."); + return rc; + } + + String ok = aafAuthn.validate( fqi, pwd ); + if ( ok != null ) { + appLogger.info( "FAILED validation of fqi=" + fqi + "with response:" + ok ); + return rc; + } + + Principal principal = new UnAuthPrincipal( fqi ); + // if we pass ns as first arg to AAFPermission constructor it gets prpended to the instance... + // as in ns|instance|type|action. we don't want that. + Permission aafPerm = new AAFPermission( null, p.getPermission(), p.getPtype(), p.getAction()); + if ( aafLur == null ) { + appLogger.error( "AafLurService: aafLur not set as expected."); + return rc; + } + rc = aafLur.fish( principal, aafPerm ); + boolean flag = true; + if (rc == flag ) { + return rc; + } + + List<Permission> perms = new ArrayList<>(); + aafLur.fishAll( principal, perms); + String key = aafPerm.getKey(); + for ( Permission prm: perms ) { + if ( prm.getKey().equals( key )) { + appLogger.info( principal + " has MATCHING perm " + prm.getKey() ); + } else { + appLogger.info( principal + " has non-matching perm " + prm.getKey() ); + } + } + + return rc; + + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java new file mode 100644 index 0000000..b6da523 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java @@ -0,0 +1,114 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.aaf; + +import java.util.ArrayList; +import java.util.Objects; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + + +public class AafNamespace extends AafObject { + + private String name; + private ArrayList<String> admin; + private ArrayList<String> responsible; + + // in some environments, an AAF Namespace must be owned by a human. + // So, when needed, this var can be set via a property + private static String NsOwnerIdentity; + + public AafNamespace(String ns, String identity ) { + super(); + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + NsOwnerIdentity = p.getProperty( "aaf.NsOwnerIdentity", ""); + this.admin = new ArrayList<>(); + this.responsible = new ArrayList<>(); + + this.name = ns; + this.admin.add( identity ); + this.responsible.add( NsOwnerIdentity ); + } + public void setName( String ns ) { + this.name = ns; + } + public String getName() { + return name; + } + public ArrayList<String> getAdmin() { + return admin; + } + public void setAdmin(ArrayList<String> admin) { + this.admin = admin; + } + public ArrayList<String> getResponsible() { + return responsible; + } + public void setResponsible(ArrayList<String> responsible) { + this.responsible = responsible; + } + + + // given an Array of Strings, return a String that is a separated list of quoted strings. + // e.g. input [ a, b, c ] + // output "a", "b", "c" + private String separatedList( ArrayList<String> list, String sep ) { + if (list.isEmpty()) return null; + String aList = ""; + String delim = ""; + for( String item: list) { + if( ! item.isEmpty()) { + aList += String.format( "%s\"%s\"", delim, item ); + delim = sep; + } + } + return aList; + } + + public String toJSON() { + + String postJSON = String.format(" { \"name\": \"%s\", \"admin\": [", + this.getName() + ); + postJSON += separatedList( this.getAdmin(), "," ); + postJSON += "], \"responsible\":["; + postJSON += separatedList( this.getResponsible(), ","); + postJSON += "]}"; + logger.info( "returning JSON: " + postJSON); + + return postJSON; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AafNamespace that = (AafNamespace) o; + return Objects.equals(name, that.name) && + Objects.equals(admin, that.admin) && + Objects.equals(responsible, that.responsible); + } + + @Override + public int hashCode() { + return Objects.hash(name, admin, responsible); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafObject.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafObject.java new file mode 100644 index 0000000..9c8f99e --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafObject.java @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.aaf; + +import java.nio.charset.StandardCharsets; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; + +public abstract class AafObject extends BaseLoggingClass { + + abstract String toJSON(); + + public byte[] getBytes() { + return toJSON().getBytes(StandardCharsets.UTF_8); + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java new file mode 100644 index 0000000..0997dd4 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java @@ -0,0 +1,74 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.aaf; + +import java.util.Objects; + +public class AafRole extends AafObject { + + private String namespace; + private String role; + + public AafRole(String ns, String role) { + super(); + this.namespace = ns; + this.role = role; + } + public void setNamespace( String ns ) { + this.namespace = ns; + } + public String getNamespace() { + return namespace; + } + public void setRole(String role) { + this.role = role; + } + public String getRole() { + return role; + } + public String getFullyQualifiedRole() { + return namespace + "." + role; + } + + public String toJSON() { + + String postJSON = String.format(" { \"name\": \"%s.%s\"}", + this.getNamespace(), + this.getRole() ); + logger.info( "returning JSON: " + postJSON); + + return postJSON; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AafRole aafRole = (AafRole) o; + return Objects.equals(namespace, aafRole.namespace) && + Objects.equals(role, aafRole.role); + } + + @Override + public int hashCode() { + return Objects.hash(namespace, role); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafService.java new file mode 100644 index 0000000..3f009f8 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafService.java @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.aaf; + +/* + * this service uses the AAF REST API endpoints to provision values in AAF + */ +public interface AafService { + enum ServiceType { + AAF_Admin, + AAF_TopicMgr + } + + String getIdentity(); + + int addPerm(DmaapPerm perm); + + int delPerm(DmaapPerm perm, boolean force); + + int addGrant(DmaapGrant grant); + + int addUserRole(AafUserRole ur); + + int addRole(AafRole role); + + int addNamespace(AafNamespace ns); + + int delNamespace(AafNamespace ns, boolean force); +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceFactory.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceFactory.java new file mode 100644 index 0000000..cfde19b --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceFactory.java @@ -0,0 +1,86 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2019 Nokia 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.dmaap.dbcapi.aaf; + +import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +public class AafServiceFactory extends BaseLoggingClass { + + private final DmaapConfig dmaapConfig; + + public AafServiceFactory() { + this((DmaapConfig) DmaapConfig.getConfig()); + } + + AafServiceFactory(DmaapConfig dmaapConfig) { + this.dmaapConfig = dmaapConfig; + } + + public AafService initAafService(ServiceType serviceType) { + boolean useAaf = "true".equalsIgnoreCase(dmaapConfig.getProperty("UseAAF", "false")); + String aafUrl = dmaapConfig.getProperty("aaf.URL", "https://authentication.domain.netset.com:8100/proxy/"); + logger.info("AafService initAafService: useAaf={}, aafUrl={}", useAaf, aafUrl); + + AafCred cred = getCred(serviceType); + return new AafServiceImpl(useAaf, aafUrl, cred.getIdentity(), new AafConnection(cred.toString())); + } + + AafCred getCred(ServiceType ctype) { + String mechIdProperty; + String secretProperty; + AafDecrypt decryptor = new AafDecrypt(); + + if (ctype == ServiceType.AAF_Admin) { + mechIdProperty = "aaf.AdminUser"; + secretProperty = "aaf.AdminPassword"; + } else if (ctype == ServiceType.AAF_TopicMgr) { + mechIdProperty = "aaf.TopicMgrUser"; + secretProperty = "aaf.TopicMgrPassword"; + } else { + logger.error("Unexpected case for AAF credential type: " + ctype); + return null; + } + String identity = dmaapConfig.getProperty(mechIdProperty, "noMechId@domain.netset.com"); + String pwd = decryptor.decrypt(dmaapConfig.getProperty(secretProperty, "notSet")); + + return new AafCred(identity, pwd); + } + + class AafCred { + private final String identity; + private final String pwd; + + AafCred(String identity, String pwd) { + this.identity = identity; + this.pwd = pwd; + } + + public String getIdentity() { + return identity; + } + + public String toString() { + return identity + ":" + pwd; + } + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceImpl.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceImpl.java new file mode 100644 index 0000000..1491818 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceImpl.java @@ -0,0 +1,163 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.aaf; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; + +import static java.lang.String.format; + +public class AafServiceImpl extends BaseLoggingClass implements AafService { + + private static final int CREATED = 201; + private static final int OK = 200; + private static final String FORCE = "?force=true"; + private final String aafUrl; + private final String identity; + private final boolean useAAF; + private final AafConnection aafConnection; + + AafServiceImpl(boolean useAaf, String aafUrl, String identity, AafConnection aafConnection) { + this.useAAF = useAaf; + this.aafUrl = aafUrl; + this.identity = identity; + this.aafConnection = aafConnection; + } + + @Override + public String getIdentity() { + return identity; + } + + @Override + public int addPerm(DmaapPerm perm) { + logger.info("entry: addPerm() "); + return doPost(perm, "authz/perm", CREATED); + } + + @Override + public int delPerm(DmaapPerm perm, boolean force) { + logger.info("entry: delPerm()"); + return doDelete(new AafEmpty(), format( + "authz/perm/%s/%s/%s%s", + perm.getPermission(), perm.getPtype(), perm.getAction(), force ? FORCE : ""), OK); + } + + @Override + public int addGrant(DmaapGrant grant) { + logger.info("entry: addGrant() "); + return doPost(grant, "authz/role/perm", CREATED); + } + + @Override + public int addUserRole(AafUserRole ur) { + logger.info("entry: addUserRole() "); + return doPost(ur, "authz/userRole", CREATED); + } + + @Override + public int addRole(AafRole role) { + logger.info("entry: addRole() "); + return doPost(role, "authz/role", CREATED); + } + + @Override + public int addNamespace(AafNamespace ns) { + logger.info("entry: addNamespace() "); + return doPost(ns, "authz/ns", CREATED); + } + + @Override + public int delNamespace(AafNamespace ns, boolean force) { + logger.info("entry: delNamespace()"); + return doDelete(new AafEmpty(), format( + "authz/ns/%s%s", + ns.getName(), force ? FORCE : ""), OK); + } + + private int doPost(AafObject obj, String uri, int expect) { + int rc; + logger.info("entry: doPost() "); + String pURL = aafUrl + uri; + logger.info("doPost: useAAF=" + useAAF); + if (useAAF) { + logger.info("doPost: " + obj.toJSON()); + rc = aafConnection.postAaf(obj, pURL); + } else { + rc = expect; + } + switch (rc) { + case 401: + case 403: + errorLogger.error(DmaapbcLogMessageEnum.AAF_CREDENTIAL_ERROR, identity); + break; + case 409: + logger.warn("Object for " + uri + " already exists. Possible conflict."); + break; + default: + if (rc == expect) { + logger.info("expected response: " + rc); + } else { + logger.error("Unexpected response: " + rc); + } + break; + } + + return rc; + } + + private int doDelete(AafObject obj, String uri, int expect) { + int rc; + String pURL = aafUrl + uri; + if (useAAF) { + logger.info("doDelete: " + obj.toJSON()); + rc = aafConnection.delAaf(obj, pURL); + } else { + rc = expect; + } + switch (rc) { + case 401: + case 403: + errorLogger.error(DmaapbcLogMessageEnum.AAF_CREDENTIAL_ERROR, identity); + break; + case 404: + logger.warn("Object not found...ignore"); + break; + case OK: + logger.info("expected response"); + break; + default: + logger.error("Unexpected response: " + rc); + break; + } + + return rc; + } + + String getAafUrl() { + return aafUrl; + } + + boolean isUseAAF() { + return useAAF; + } + +}
\ No newline at end of file diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafUserRole.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafUserRole.java new file mode 100644 index 0000000..b948e61 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafUserRole.java @@ -0,0 +1,80 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.aaf; + +import java.util.Objects; + + +public class AafUserRole extends AafObject { + + private String identity; + private String role; + + + + public AafUserRole(String identity, String role ) { + super(); + this.identity = identity; + this.role = role; + } + + public void setRole(String role) { + this.role = role; + } + public String getRole() { + return role; + } + + public String getIdentity() { + return identity; + } + + public void setIdentity(String identity) { + this.identity = identity; + } + + public String toJSON() { + + String postJSON = String.format(" { \"user\": \"%s\", \"role\": \"%s\" }", + this.getIdentity(), + this.getRole() + ); + logger.info( "returning JSON: " + postJSON); + + return postJSON; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AafUserRole that = (AafUserRole) o; + return Objects.equals(identity, that.identity) && + Objects.equals(role, that.role); + } + + @Override + public int hashCode() { + + return Objects.hash(identity, role); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/ClearDecrypt.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/ClearDecrypt.java new file mode 100644 index 0000000..fe3e3e7 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/ClearDecrypt.java @@ -0,0 +1,42 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.aaf; + +import java.io.IOException; + +public class ClearDecrypt implements DecryptionInterface { + + @Override + public String decrypt(String enc) throws IOException { + return enc; + } + + @Override + public String valueOf(String s) { + return s; + } + + @Override + public boolean init(String codecFname) { + return false; + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DecryptionInterface.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DecryptionInterface.java new file mode 100644 index 0000000..eda6465 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DecryptionInterface.java @@ -0,0 +1,30 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.aaf; + +import java.io.IOException; + +public interface DecryptionInterface { + public boolean init( String codecFname ); + public String decrypt(String enc) throws IOException; + public String valueOf( String s); + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapGrant.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapGrant.java new file mode 100644 index 0000000..e171b88 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapGrant.java @@ -0,0 +1,79 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.aaf; + +import java.util.Objects; + +public class DmaapGrant extends AafObject { + + private DmaapPerm perm; + private String role; + + public DmaapGrant() { + + } + + public DmaapGrant(DmaapPerm p, String r) { + this.perm = p; + this.role = r; + } + + public DmaapPerm getPerm() { + return perm; + } + + public void setPerm(DmaapPerm perm) { + this.perm = perm; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public String toJSON() { + + String postJSON = String.format(" { \"perm\": %s, \"role\": \"%s\"}", + this.perm.toJSON(), + this.getRole()); + logger.info("returning JSON: " + postJSON); + + return postJSON; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DmaapGrant that = (DmaapGrant) o; + return Objects.equals(perm, that.perm) && + Objects.equals(role, that.role); + } + + @Override + public int hashCode() { + + return Objects.hash(perm, role); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapPerm.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapPerm.java new file mode 100644 index 0000000..2f1765d --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapPerm.java @@ -0,0 +1,89 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.aaf; + +import java.util.Objects; + + +public class DmaapPerm extends AafObject { + + private String permission; + private String ptype; + private String action; + + public DmaapPerm(String permission, String ptype, String action) { + super(); + this.permission = permission; + this.ptype = ptype; + this.action = action; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public String getPtype() { + return ptype; + } + + public void setPtype(String ptype) { + this.ptype = ptype; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public String toJSON() { + + String postJSON = String.format(" { \"type\": \"%s\", \"instance\": \"%s\", \"action\": \"%s\"}", + this.getPermission(), + this.getPtype(), + this.getAction()); + logger.info("returning JSON: " + postJSON); + + return postJSON; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DmaapPerm dmaapPerm = (DmaapPerm) o; + return Objects.equals(permission, dmaapPerm.permission) && + Objects.equals(ptype, dmaapPerm.ptype) && + Objects.equals(action, dmaapPerm.action); + } + + @Override + public int hashCode() { + return Objects.hash(permission, ptype, action); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFish.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFish.java new file mode 100644 index 0000000..825b711 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFish.java @@ -0,0 +1,104 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.authentication; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.misc.env.APIException; +import org.onap.dmaap.dbcapi.aaf.AafLurService; +import org.onap.dmaap.dbcapi.aaf.DmaapPerm; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + + +public class AafLurAndFish extends BaseLoggingClass implements ApiAuthorizationCheckInterface { + private AafLurService svc; + private static String apiNamespace; + private static final String ERROR="Error"; + + AafLurAndFish() throws AuthenticationErrorException { + + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + apiNamespace = p.getProperty( "ApiNamespace", "org.onap.dmaap-bc.api"); + + String cadiprop = p.getProperty( "cadi.properties", "/opt/app/osaaf/local/org.onap.dmaap-bc.props"); + logger.info( "cadiprops in " + cadiprop ); + Properties props = new Properties(); + try { + FileInputStream fis = new FileInputStream( cadiprop ); + try { + props.load( fis ); + } finally { + fis.close(); + } + } catch ( IOException e ) { + errorLogger.error( "Unable to load " + cadiprop ); + errorLogger.error(ERROR, e); + throw new AuthenticationErrorException( ); + } + try { + PropAccess myAccess = new PropAccess( props ); + + svc = AafLurService.getInstance(myAccess); + } catch (APIException | CadiException | LocatorException e ) { + errorLogger.error(ERROR, e); + errorLogger.error( e.toString() ); + throw new AuthenticationErrorException(); + } + + } + + public void check( String mechid, String pwd, DmaapPerm p ) throws AuthenticationErrorException { + + try { + if (mechid.isEmpty() || pwd.isEmpty()) { + throw new AuthenticationErrorException("No basic authorization value provided"); + } + + if (!svc.checkPerm( apiNamespace, mechid, pwd, p )) { + throw new AuthenticationErrorException(); + } + } catch ( IOException | CadiException e ) { + errorLogger.error(ERROR, e); + errorLogger.error( e.toString() ); + throw new AuthenticationErrorException(); + } + + } + + public static void main(String[] args) throws Exception { + AafLurAndFish alaf = new AafLurAndFish(); + DmaapPerm p = new DmaapPerm( "org.onap.dmaap-bc.api.dmaap", "boot", "GET"); + + try { + alaf.check("mmanager@people.osaaf.org", "demo123456!", p); + } catch (AuthenticationErrorException aee ) { + errorLogger.error(aee.getMessage()); + errorLogger.error( "Check failed for: " + p.toJSON()); + System.exit(-1); + } + errorLogger.info("Check succeeded for: " + p.toJSON()); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AllowAll.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AllowAll.java new file mode 100644 index 0000000..2e83e6c --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AllowAll.java @@ -0,0 +1,29 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2019 Nokia 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.dmaap.dbcapi.authentication; + +import org.onap.dmaap.dbcapi.aaf.DmaapPerm; + +public class AllowAll implements ApiAuthorizationCheckInterface { + @Override + public void check(String mechid, String pwd, DmaapPerm p) { + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiAuthorizationCheckInterface.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiAuthorizationCheckInterface.java new file mode 100644 index 0000000..1fef09d --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiAuthorizationCheckInterface.java @@ -0,0 +1,29 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.authentication; + +import org.onap.dmaap.dbcapi.aaf.DmaapPerm; + +@FunctionalInterface +public interface ApiAuthorizationCheckInterface { + public void check( String mechid, String pwd, DmaapPerm p ) throws AuthenticationErrorException; + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPerms.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPerms.java new file mode 100644 index 0000000..b082102 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPerms.java @@ -0,0 +1,180 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2018 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.authentication; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import org.onap.dmaap.dbcapi.aaf.AafService; +import org.onap.dmaap.dbcapi.aaf.AafServiceFactory; +import org.onap.dmaap.dbcapi.aaf.DmaapGrant; +import org.onap.dmaap.dbcapi.aaf.DmaapPerm; +import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.model.Dmaap; +import org.onap.dmaap.dbcapi.service.DmaapService; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +public class ApiPerms extends BaseLoggingClass { + static String topic = "topics"; + static String mrClusters = "mr_clusters"; + static String mrClients = "mr_clients"; + static String feed = "feeds"; + static String drSubs = "dr_subs"; + static String drPubs = "dr_pubs"; + static String drNodes = "dr_nodes"; + static String dcaeLocations = "dcaeLocations"; + static String inventory = "Inventory"; + static String portalUser = "PortalUser"; + static String orchestrator = "Orchestrator"; + static String delete = "DELETE"; + static String dmaap = "dmaap"; + static String controller = "Controller"; + + private static class PermissionMap { + static final EELFLogger logger = EELFManager.getInstance().getLogger( PermissionMap.class ); + static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger(); + String uri; + String action; + String[] roles; + + private PermissionMap( String u, String a, String[] r ) { + this.setUri(u); + this.setAction(a); + this.setRoles(r); + } + + public String getUri() { + return uri; + } + public void setUri(String uri) { + this.uri = uri; + } + public String getAction() { + return action; + } + public void setAction(String action) { + this.action = action; + } + + public String[] getRoles() { + return roles; + } + public void setRoles(String[] roles) { + this.roles = roles; + } + + public static void initMap( PermissionMap[] pmap, String instance ) { + + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + String api = p.getProperty("ApiNamespace", "apiNamespace.not.set"); + + AafService aaf = new AafServiceFactory().initAafService(ServiceType.AAF_Admin); + + for ( int i = 0; i < pmap.length ; i++ ) { + String uri = new String( api + "." + pmap[i].getUri()); + DmaapPerm perm = new DmaapPerm( uri, instance, pmap[i].getAction() ); + int rc = aaf.addPerm( perm ); + if ( rc != 201 && rc != 409 ) { + errorLogger.error( DmaapbcLogMessageEnum.AAF_UNEXPECTED_RESPONSE, Integer.toString(rc), "add perm", perm.toString() ); + + } + for( String r: pmap[i].getRoles()) { + String fr = new String( api + "." + r ); + logger.debug( "i:" + i + " granting perm " + perm.toString()+ " to role=" + fr ); + DmaapGrant grant = new DmaapGrant( perm, fr ); + rc = aaf.addGrant( grant ); + if ( rc != 201 && rc != 409 ) { + errorLogger.error( DmaapbcLogMessageEnum.AAF_UNEXPECTED_RESPONSE, Integer.toString(rc), "grant perm", perm.toString() ); + } + } + + } + } + } + + static PermissionMap[] bootMap = { + new PermissionMap( dmaap, "GET", new String[] { controller }), + new PermissionMap( dmaap, "POST", new String[] { controller }), + new PermissionMap( dmaap, "PUT", new String[] { controller }), + new PermissionMap( dmaap, delete, new String[] { controller }) + + }; + + static PermissionMap[] envMap = { + new PermissionMap( dmaap, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }), + new PermissionMap( dmaap, "POST", new String[] { controller } ), + new PermissionMap( dmaap, "PUT", new String[] { controller }), + new PermissionMap( dmaap, delete, new String[] { controller }), + new PermissionMap( "bridge", "GET", new String[] { "Metrics" }), + //new PermissionMap( "bridge", "POST", new String[] { "Metrics" } ), + //new PermissionMap( "bridge", "PUT", new String[] { "Metrics" }), + //new PermissionMap( "bridge", delete, new String[] { "Metrics" }), + new PermissionMap( dcaeLocations, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }), + new PermissionMap( dcaeLocations, "POST", new String[] { controller } ), + new PermissionMap( dcaeLocations, "PUT", new String[] { controller }), + new PermissionMap( dcaeLocations, delete, new String[] { controller }), + new PermissionMap( drNodes, "GET", new String[] { controller, orchestrator, inventory, portalUser }), + new PermissionMap( drNodes, "POST", new String[] { controller } ), + new PermissionMap( drNodes, "PUT", new String[] { controller }), + new PermissionMap( drNodes, delete, new String[] { controller }), + new PermissionMap( drPubs, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }), + new PermissionMap( drPubs, "POST", new String[] { controller, orchestrator,portalUser } ), + new PermissionMap( drPubs, "PUT", new String[] { controller, orchestrator,portalUser }), + new PermissionMap( drPubs, delete, new String[] { controller, orchestrator,portalUser }), + new PermissionMap( drSubs, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }), + new PermissionMap( drSubs, "POST", new String[] { controller, orchestrator,portalUser } ), + new PermissionMap( drSubs, "PUT", new String[] { controller, orchestrator,portalUser }), + new PermissionMap( drSubs, delete, new String[] { controller, orchestrator,portalUser }), + new PermissionMap( feed, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }), + new PermissionMap( feed, "POST", new String[] { controller, orchestrator,portalUser } ), + new PermissionMap( feed, "PUT", new String[] { controller, orchestrator, portalUser }), + new PermissionMap( feed, delete, new String[] { controller, portalUser }), + new PermissionMap( mrClients, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }), + new PermissionMap( mrClients, "POST", new String[] { controller,orchestrator, portalUser } ), + new PermissionMap( mrClients, "PUT", new String[] { controller, orchestrator,portalUser }), + new PermissionMap( mrClients, delete, new String[] { controller,orchestrator, portalUser }), + new PermissionMap( mrClusters, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }), + new PermissionMap( mrClusters, "POST", new String[] { controller } ), + new PermissionMap( mrClusters, "PUT", new String[] { controller }), + new PermissionMap( mrClusters, delete, new String[] { controller }), + new PermissionMap( topic, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }), + new PermissionMap( topic, "POST", new String[] { controller, orchestrator } ), + new PermissionMap( topic, "PUT", new String[] { controller, orchestrator }), + new PermissionMap( topic, delete, new String[] { controller, orchestrator }) + }; + + public void setBootMap() { + String instance = "boot"; + PermissionMap.initMap( bootMap, instance ); + } + + public void setEnvMap() { + Dmaap dmaapVar = new DmaapService().getDmaap(); + String dmaapName = dmaapVar.getDmaapName(); + PermissionMap.initMap( envMap, dmaapName ); + } + + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPolicy.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPolicy.java new file mode 100644 index 0000000..16d0367 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPolicy.java @@ -0,0 +1,63 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.authentication; + +import org.onap.dmaap.dbcapi.aaf.DmaapPerm; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +import java.util.Properties; + +public class ApiPolicy extends BaseLoggingClass { + + private boolean permissionClassSet = true; + private ApiAuthorizationCheckInterface perm = null; + + public ApiPolicy() { + this(DmaapConfig.getConfig()); + } + + ApiPolicy(Properties p) { + String dClass = p.getProperty("ApiPermission.Class"); + logger.info("ApiPolicy implements " + dClass); + logger.info("dClass=" + dClass + " permissionClassSet=" + permissionClassSet); + + try { + perm = (ApiAuthorizationCheckInterface) (Class.forName(dClass).newInstance()); + } catch (Exception ee) { + errorLogger.error(DmaapbcLogMessageEnum.UNEXPECTED_CONDITION, "attempting to instantiate " + dClass); + errorLogger.error("trace is: " + ee); + permissionClassSet = false; + } + } + + public void check(String mechid, String pwd, DmaapPerm p) throws AuthenticationErrorException { + perm.check(mechid, pwd, p); + } + + public boolean isPermissionClassSet() { + return permissionClassSet; + } + + ApiAuthorizationCheckInterface getPerm() { + return perm; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AuthenticationErrorException.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AuthenticationErrorException.java new file mode 100644 index 0000000..01200f7 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AuthenticationErrorException.java @@ -0,0 +1,35 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.authentication; + +public class AuthenticationErrorException extends Exception { + /** + * + */ + private static final long serialVersionUID = 1L; + + public AuthenticationErrorException() { + } + + public AuthenticationErrorException(String s) { + super(s); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/DrProvConnection.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/DrProvConnection.java new file mode 100644 index 0000000..dffe830 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/DrProvConnection.java @@ -0,0 +1,1101 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.client; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.ConnectException; +import java.net.ProtocolException; +import java.net.SocketException; +import java.net.URL; +import java.util.Arrays; +import javax.net.ssl.HttpsURLConnection; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DR_Sub; +import org.onap.dmaap.dbcapi.model.Feed; +import org.onap.dmaap.dbcapi.service.DmaapService; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + + + +public class DrProvConnection extends BaseLoggingClass { + + + private String provURL; + private String provApi; + private String behalfHeader; + private String feedContentType; + private String subContentType; + private String unit_test; + private String provURI; + + private HttpsURLConnection uc; + + + public DrProvConnection() { + provURL = new DmaapService().getDmaap().getDrProvUrl(); + if ( provURL.length() < 1 ) { + errorLogger.error( DmaapbcLogMessageEnum.PREREQ_DMAAP_OBJECT, "DmaapService().getDmaap().getDrProvUrl()"); + } + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + provApi = p.getProperty( "DR.provApi", "ONAP" ); + behalfHeader = p.getProperty( "DR.onBehalfHeader", "X-DMAAP-DR-ON-BEHALF-OF"); + feedContentType = p.getProperty( "DR.feedContentType", "application/vnd.dmaap-dr.feed"); + subContentType = p.getProperty( "DR.subContentType", "application/vnd.dmaap-dr.subscription"); + provURI = p.getProperty( "DR.ProvisioningURI", "/internal/prov"); + logger.info( "provURL=" + provURL + " provApi=" + provApi + " behalfHeader=" + behalfHeader + + " feedContentType=" + feedContentType + " subContentType=" + subContentType ); + unit_test = p.getProperty( "UnitTest", "No" ); + + } + + public boolean makeFeedConnection() { + return makeConnection( provURL ); + } + public boolean makeFeedConnection(String feedId) { + return makeConnection( provURL + "/feed/" + feedId ); + } + public boolean makeSubPostConnection( String subURL ) { + String[] parts = subURL.split("/"); + String revisedURL = provURL + "/" + parts[3] + "/" + parts[4]; + logger.info( "mapping " + subURL + " to " + revisedURL ); + return makeConnection( revisedURL ); + } + public boolean makeSubPutConnection( String subId ) { + String revisedURL = provURL + "/subs/" + subId; + logger.info( "mapping " + subId + " to " + revisedURL ); + return makeConnection( revisedURL ); + } + + public boolean makeIngressConnection( String feed, String user, String subnet, String nodep ) { + String uri = String.format("/internal/route/ingress/?feed=%s&user=%s&subnet=%s&nodepatt=%s", + feed, user, subnet, nodep ); + return makeConnection( provURL + uri ); + } + public boolean makeEgressConnection( String sub, String nodep ) { + String uri = String.format("/internal/route/egress/?sub=%s&node=%s", + sub, nodep ); + return makeConnection( provURL + uri ); + } + public boolean makeDumpConnection() { + String url = provURL + provURI; + return makeConnection( url ); + } + public boolean makeNodesConnection( String varName ) { + + String uri = String.format("/internal/api/%s", varName); + return makeConnection( provURL + uri ); + } + + public boolean makeNodesConnection( String varName, String val ) { + + if ( val == null ) { + return false; + } + String cv = val.replaceAll("\\|", "%7C"); + String uri = String.format( "/internal/api/%s?val=%s", varName, cv ); + + return makeConnection( provURL + uri ); + } + + private boolean makeConnection( String pURL ) { + + try { + URL u = new URL( pURL ); + uc = (HttpsURLConnection) u.openConnection(); + uc.setInstanceFollowRedirects(false); + logger.info( "successful connect to " + pURL ); + uc.setSSLSocketFactory(DmaapConfig.getSSLSocketFactory()); + return(true); + } catch (Exception e) { + errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_ERROR, pURL, e.getMessage() ); + return(false); + } + } + + public String bodyToString( InputStream is ) { + logger.info( "is=" + is ); + StringBuilder sb = new StringBuilder(); + BufferedReader br = new BufferedReader( new InputStreamReader(is)); + String line; + try { + while ((line = br.readLine()) != null ) { + sb.append( line ); + } + } catch (IOException ex ) { + errorLogger.error( DmaapbcLogMessageEnum.IO_EXCEPTION, ex.getMessage()); + } + + return sb.toString(); + } + + + public String doPostFeed( Feed postFeed, ApiError err ) { + + byte[] postData = postFeed.getBytes(); + logger.info( "post fields=" + Arrays.toString(postData) ); + String responsemessage = null; + String responseBody = null; + int rc = -1; + + try { + logger.info( "uc=" + uc ); + uc.setRequestMethod("POST"); + uc.setRequestProperty("Content-Type", feedContentType); + uc.setRequestProperty( "charset", "utf-8"); + uc.setRequestProperty( behalfHeader, postFeed.getOwner() ); + uc.setRequestProperty( "Content-Length", Integer.toString( postData.length )); + uc.setUseCaches(false); + uc.setDoOutput(true); + OutputStream os = null; + + try { + uc.connect(); + os = uc.getOutputStream(); + os.write( postData ); + + } catch (ProtocolException pe) { + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + } + } catch (Exception e) { + logger.info( "Exception: " + e.getMessage() ); + e.printStackTrace(); + } + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + if (responsemessage == null) { + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + if (rc == 201 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + + } else { + err.setCode( rc ); + err.setMessage(responsemessage); + } + + } catch (ConnectException ce) { + errorLogger.error(DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage() ); + err.setCode( 500 ); + err.setMessage("Backend connection refused"); + } catch (SocketException se) { + errorLogger.error( DmaapbcLogMessageEnum.SOCKET_EXCEPTION, se.getMessage(), "response from prov server" ); + err.setCode( 500 ); + err.setMessage( "Unable to read response from DR"); + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doPostFeed because unit_test =" + unit_test ); + } else { + logger.warn("Unable to read response " ); + errorLogger.error("Unable to read response ", e.getMessage()); + try { + err.setCode( uc.getResponseCode()); + err.setMessage(uc.getResponseMessage()); + } catch (Exception e2) { + err.setCode( 500 ); + err.setMessage("Unable to determine response message"); + } + } + } + finally { + try { + uc.disconnect(); + } catch ( Exception e ) { + logger.error(e.getMessage(), e); + } + } + return responseBody; + + } + + + // the POST for /internal/route/ingress doesn't return any data, so needs a different function + // the POST for /internal/route/egress doesn't return any data, so needs a different function + public int doXgressPost( ApiError err ) { + + String responsemessage = null; + int rc = -1; + + try { + uc.setRequestMethod("POST"); + + + try { + uc.connect(); + + } catch (ProtocolException pe) { + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } catch (Exception e) { + logger.info( "Exception: " + e.getMessage() ); + e.printStackTrace(); + } + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + + + if (rc < 200 || rc >= 300 ) { + err.setCode( rc ); + err.setMessage(responsemessage); + } + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doXgressPost because unit_test =" + unit_test ); + } else { + logger.error("Unable to read response " ); + logger.error(e.getMessage(), e); + } + } + finally { + try { + uc.disconnect(); + } catch ( Exception e ) { + logger.error(e.getMessage(), e); + } + } + + return rc; + + } + + public String doPostDr_Sub( DR_Sub postSub, ApiError err ) { + logger.info( "entry: doPostDr_Sub() " ); + byte[] postData = postSub.getBytes(provApi ); + logger.info( "post fields=" + postData ); + String responsemessage = null; + String responseBody = null; + + try { + + uc.setRequestMethod("POST"); + + uc.setRequestProperty("Content-Type", subContentType ); + uc.setRequestProperty( "charset", "utf-8"); + uc.setRequestProperty( behalfHeader, "DGL" ); + uc.setRequestProperty( "Content-Length", Integer.toString( postData.length )); + uc.setUseCaches(false); + uc.setDoOutput(true); + OutputStream os = null; + int rc = -1; + + try { + uc.connect(); + os = uc.getOutputStream(); + os.write( postData ); + + } catch (ProtocolException pe) { + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } catch (Exception e) { + logger.info( "Exception: " + e.getMessage() ); + e.printStackTrace(); + } + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + + if (responsemessage == null) { + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + if (rc == 201 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + + } else { + err.setCode(rc); + err.setMessage(responsemessage); + } + + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doPostDr_Sub because unit_test =" + unit_test ); + } else { + logger.error("Unable to read response ", e.getMessage()); + } + } + finally { + try { + uc.disconnect(); + } catch ( Exception e ) { + logger.error(e.getMessage(), e); + } + } + return responseBody; + + } + + + public String doPutFeed(Feed putFeed, ApiError err) { + byte[] postData = putFeed.getBytes(); + logger.info( "post fields=" + Arrays.toString(postData) ); + String responsemessage = null; + String responseBody = null; + + try { + logger.info( "uc=" + uc ); + uc.setRequestMethod("PUT"); + uc.setRequestProperty("Content-Type", feedContentType ); + uc.setRequestProperty( "charset", "utf-8"); + uc.setRequestProperty( behalfHeader, putFeed.getOwner() ); + uc.setRequestProperty( "Content-Length", Integer.toString( postData.length )); + uc.setUseCaches(false); + uc.setDoOutput(true); + OutputStream os = null; + int rc = -1; + + try { + uc.connect(); + os = uc.getOutputStream(); + os.write( postData ); + + } catch (ProtocolException pe) { + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } catch (Exception e) { + logger.info( "Exception: " + e.getMessage() ); + e.printStackTrace(); + } + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + + if (responsemessage == null) { + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + if (rc >= 200 && rc < 300 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + err.setCode( rc ); + } else if ( rc == 404 ) { + err.setCode( rc ); + err.setFields( "feedid"); + String message = "FeedId " + putFeed.getFeedId() + " not found on DR to update. Out-of-sync condition?"; + err.setMessage( message ); + errorLogger.error( DmaapbcLogMessageEnum.PROV_OUT_OF_SYNC, "Feed", putFeed.getFeedId() ); + + } else { + err.setCode( rc ); + err.setMessage(responsemessage); + } + + } catch (ConnectException ce) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doPutFeed because unit_test =" + unit_test ); + } else { + errorLogger.error(DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage()); + err.setCode(500); + err.setMessage("Backend connection refused"); + } + } catch (SocketException se) { + errorLogger.error( DmaapbcLogMessageEnum.SOCKET_EXCEPTION, se.getMessage(), "response from Prov server" ); + err.setCode( 500 ); + err.setMessage( "Unable to read response from DR"); + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doPutFeed because unit_test =" + unit_test ); + } else { + logger.warn("Unable to read response " ); + logger.error(e.getMessage(), e); + } + try { + err.setCode( uc.getResponseCode()); + err.setMessage(uc.getResponseMessage()); + } catch (Exception e2) { + err.setCode( 500 ); + err.setMessage("Unable to determine response message"); + logger.error(e2.getMessage(), e2); + } + } finally { + try { + uc.disconnect(); + } catch ( Exception e ) { + logger.error(e.getMessage(), e); + } + } + return responseBody; + } + public String doPutDr_Sub(DR_Sub postSub, ApiError err) { + logger.info( "entry: doPutDr_Sub() " ); + byte[] postData = postSub.getBytes(provApi); + logger.info( "post fields=" + postData ); + String responsemessage = null; + String responseBody = null; + + try { + + uc.setRequestMethod("PUT"); + + uc.setRequestProperty("Content-Type", subContentType ); + uc.setRequestProperty( "charset", "utf-8"); + uc.setRequestProperty( behalfHeader, "DGL" ); + uc.setRequestProperty( "Content-Length", Integer.toString( postData.length )); + uc.setUseCaches(false); + uc.setDoOutput(true); + OutputStream os = null; + int rc = -1; + + try { + uc.connect(); + os = uc.getOutputStream(); + os.write( postData ); + + } catch (ProtocolException pe) { + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } catch (Exception e) { + logger.info( "Exception: " + e.getMessage() ); + e.printStackTrace(); + } + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + + if (responsemessage == null) { + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + if (rc == 200 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + + } else { + err.setCode(rc); + err.setMessage(responsemessage); + } + + } catch (ConnectException ce) { + errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage() ); + err.setCode( 500 ); + err.setMessage("Backend connection refused"); + logger.error(ce.getMessage(), ce); + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doPutDr_Sub because unit_test =" + unit_test ); + } else { + logger.error("Unable to read response " ); + logger.error(e.getMessage(), e); + } + } finally { + if(null != uc){ + uc.disconnect(); + } + } + return responseBody; + + } + + public String doGetNodes( ApiError err ) { + logger.info( "entry: doGetNodes() " ); + //byte[] postData = postSub.getBytes(); + //logger.info( "get fields=" + postData ); + String responsemessage = null; + String responseBody = null; + + try { + + uc.setRequestMethod("GET"); + int rc = -1; + + + try { + uc.connect(); + + + } catch (ProtocolException pe) { + + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } catch (Exception e) { + logger.info( "Exception: " + e.getMessage() ); + e.printStackTrace(); + } + + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + if (responsemessage == null) { + + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + + err.setCode(rc); // may not really be an error, but we save rc + if (rc == 200 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + } else { + err.setMessage(responsemessage); + } + + + } catch (ConnectException ce) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doGetNodes because unit_test =" + unit_test ); + } else { + errorLogger.error(DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage()); + err.setCode(500); + err.setMessage("Backend connection refused"); + logger.error(ce.getMessage(), ce); + } + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doGetNodes because unit_test =" + unit_test ); + } else { + logger.error("Unable to read response ", e.getMessage()); + } + } finally { + + if ( uc != null ) uc.disconnect(); + } + + return responseBody; + + } + public String doPutNodes( ApiError err ) { + logger.info( "entry: doPutNodes() " ); + String responsemessage = null; + String responseBody = null; + + try { + uc.setRequestMethod("PUT"); + uc.setUseCaches(false); + int rc = -1; + + try { + uc.connect(); + } catch (ProtocolException pe) { + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + } + } catch (Exception e) { + logger.info( "Exception: " + e.getMessage() ); + e.printStackTrace(); + } + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + + if (responsemessage == null) { + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + err.setCode(rc); + if (rc == 200 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + + } else { + + err.setMessage(responsemessage); + } + + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doPutNodes because unit_test =" + unit_test ); + } else { + logger.error("Unable to read response ", e.getMessage()); + } + } finally { + if ( uc != null ) { + uc.disconnect(); + } + } + return responseBody; + + } + + public String doDeleteFeed(Feed putFeed, ApiError err) { + String responsemessage = null; + String responseBody = null; + + try { + logger.info( "uc=" + uc ); + uc.setRequestMethod("DELETE"); + uc.setRequestProperty("Content-Type", feedContentType ); + uc.setRequestProperty( "charset", "utf-8"); + uc.setRequestProperty( behalfHeader, putFeed.getOwner() ); + uc.setUseCaches(false); + uc.setDoOutput(true); + OutputStream os = null; + int rc = -1; + + try { + uc.connect(); + os = uc.getOutputStream(); + //os.write( postData ); + + } catch (ProtocolException pe) { + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } catch (Exception e) { + logger.info( "Exception: " + e.getMessage() ); + e.printStackTrace(); + } + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + + if (responsemessage == null) { + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + if (rc >= 200 && rc < 300 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + + } else if ( rc == 404 ) { + err.setCode( rc ); + err.setFields( "feedid"); + String message = "FeedId " + putFeed.getFeedId() + " not found on DR to update. Out-of-sync condition?"; + err.setMessage( message ); + errorLogger.error( DmaapbcLogMessageEnum.PROV_OUT_OF_SYNC, "Feed", putFeed.getFeedId() ); + + } else { + err.setCode( rc ); + err.setMessage(responsemessage); + } + + } catch (ConnectException ce) { + errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage() ); + err.setCode( 500 ); + err.setMessage("Backend connection refused"); + logger.error(ce.getMessage(), ce); + } catch (SocketException se) { + errorLogger.error( DmaapbcLogMessageEnum.SOCKET_EXCEPTION, se.getMessage(), "response from Prov server" ); + err.setCode( 500 ); + err.setMessage( "Unable to read response from DR"); + logger.error(se.getMessage(), se); + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doDeleteFeed because unit_test =" + unit_test ); + } else { + logger.warn("Unable to read response " ); + logger.error(e.getMessage(), e); + try { + err.setCode( uc.getResponseCode()); + err.setMessage(uc.getResponseMessage()); + } catch (Exception e2) { + err.setCode( 500 ); + err.setMessage("Unable to determine response message"); + logger.error(e2.getMessage(), e2); + } + } + } finally { + try { + if(uc != null) { + uc.disconnect(); + } + } catch ( Exception e ) { + logger.error(e.getMessage(), e); + } + } + return responseBody; + } + + public String doDeleteDr_Sub(DR_Sub delSub, ApiError err) { + logger.info( "entry: doDeleteDr_Sub() " ); + byte[] postData = delSub.getBytes(provApi); + logger.info( "post fields=" + Arrays.toString(postData)); + String responsemessage = null; + String responseBody = null; + + try { + + uc.setRequestMethod("DELETE"); + + uc.setRequestProperty("Content-Type", subContentType); + uc.setRequestProperty( "charset", "utf-8"); + uc.setRequestProperty( behalfHeader, "DGL" ); + uc.setUseCaches(false); + uc.setDoOutput(true); + OutputStream os = null; + int rc = -1; + + try { + uc.connect(); + os = uc.getOutputStream(); + //os.write( postData ); + + } catch (ProtocolException pe) { + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } catch (Exception e) { + logger.info( "Exception: " + e.getMessage() ); + e.printStackTrace(); + } + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + + if (responsemessage == null) { + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + err.setCode(rc); + if (rc == 204 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + } else { + err.setMessage(responsemessage); + } + + } catch (ConnectException ce) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doDeleteDr_Sub because unit_test =" + unit_test ); + } else { + errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage() ); + err.setCode( 500 ); + err.setMessage("Backend connection refused"); + } + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doDeleteDr_Sub because unit_test =" + unit_test ); + } else { + logger.error("Unable to read response ", e.getMessage()); + } + } finally { + if(uc != null){ + uc.disconnect(); + } + } + return responseBody; + + } + + // add double-quotes around a value + // hope his is easier to read than in-line escaping... + private String dq( String v ) { + return ( "\"" + v + "\""); + } + private String dq( String k, String v) { + return( dq(k) + ":" + dq(v)); + } + private String dqc( String k, String v) { + return( dq(k) + ":" + dq(v) + ","); + } + + private String dumpSimulation() { + logger.info( "enter dumpSimulation()"); + String responseBody = + "{" + + dq("feeds") + ":[" + + "{" + dq( "suspend") + ":false," + + dq( "groupid") + ":0," + + dqc( "description", "Some description" ) + + dqc( "version", "m1.1") + + dq( "authorization") + ":" + + "{" + dq( "endpoint_addrs" ) + ":[]," + + dq( "classification", "unclassified") + + dq( "endpoint_ids") + ":[{" + + dqc( "password", "dradmin" ) + + dq( "id", "dradmin") + + "}]}" + + dq( "last_mod") + ":1553738110000," + + dq( "deleted") + ":false," + + dq( "feedid") + ":1," + + dqc( "name", "Default PM Feed") + + dq( "business_description") + ":\"\"," + + dqc( "publisher", "onap") + + dq( "links") + ":{" + + dqc( "subscribe", "https://dmaap-dr-prov/subscribe/1") + + dqc( "log", "https://dmaap-dr-prov/feedlog/1") + + dqc( "publish", "https://dmaap-dr-prov/publish/1") + + dq( "self", "https:/dmaap-dr-prov/feed/1") + + "}" + + dq( "created_date") + ":1553738110000 }" + + "]," + + dq( "groups") + ":[" + + "]," + + dq( "subscriptions") + ":[" + + "]," + + dq( "ingress") + ":[" + + "]," + + dq( "egress") + ":{" + + "}," + + dq( "routing") + ":[" + + "]," + + "}"; + return responseBody; + } + + public String doGetDump( ApiError err ) { + logger.info( "entry: doGetDump() " ); + + String responsemessage = null; + String responseBody = null; + + try { + + uc.setRequestMethod("GET"); + int rc = -1; + + + try { + uc.connect(); + + + } catch (ProtocolException pe) { + + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } catch (Exception e) { + logger.info( "Exception: " + e.getMessage() ); + e.printStackTrace(); + } + + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + + + if (responsemessage == null) { + + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + + err.setCode(rc); // may not really be an error, but we save rc + if (rc == 200 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + } else { + err.setMessage(responsemessage); + } + + + } catch (ConnectException ce) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doGetNodes because unit_test =" + unit_test ); + responseBody = dumpSimulation(); + + } else { + errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage() ); + err.setCode( 500 ); + err.setMessage("Backend connection refused"); + logger.error(ce.getMessage(), ce); + } + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doGetNodes because unit_test =" + unit_test ); + responseBody = dumpSimulation(); + + } else { + logger.error("Unable to read response ", e.getMessage()); + } + } finally { + + if ( uc != null ) uc.disconnect(); + } + + return responseBody; + + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrProvConnection.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrProvConnection.java new file mode 100644 index 0000000..9c3fa4e --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrProvConnection.java @@ -0,0 +1,271 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.client; + +import org.apache.commons.codec.binary.Base64; +import org.onap.dmaap.dbcapi.aaf.AafDecrypt; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.MR_Cluster; +import org.onap.dmaap.dbcapi.model.Topic; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSession; + +import java.io.*; +import java.net.*; +import java.util.Arrays; + +public class MrProvConnection extends BaseLoggingClass{ + + private String provURL; + + private HttpURLConnection uc; + + + private String topicMgrCred; + private String authMethod; + private String user; + private String encPwd; + private String unit_test; + private boolean hostnameVerify; + + public MrProvConnection() { + String mechIdProperty = "aaf.TopicMgrUser"; + String pwdProperty = "aaf.TopicMgrPassword"; + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + user = p.getProperty( mechIdProperty, "noMechId@domain.netset.com" ); + encPwd = p.getProperty( pwdProperty, "notSet" ); + authMethod = p.getProperty("MR.authentication", "none"); + topicMgrCred = getCred(); + hostnameVerify= "true".equalsIgnoreCase(p.getProperty("MR.hostnameVerify", "true")); + unit_test = p.getProperty( "UnitTest", "No" ); + + } + + private String getCred( ) { + + + String pwd = ""; + AafDecrypt decryptor = new AafDecrypt(); + pwd = decryptor.decrypt(encPwd); + return user + ":" + pwd; + } + + + public boolean makeTopicConnection( MR_Cluster cluster ) { + boolean rc = false; + logger.info( "connect to cluster: " + cluster.getDcaeLocationName()); + + + provURL = cluster.getTopicProtocol() + "://" + cluster.getFqdn() + ":" + cluster.getTopicPort() + "/topics/create"; + + if ( cluster.getTopicProtocol().equals( "https" ) ) { + rc = makeSecureConnection( provURL ); + } else { + rc = makeConnection( provURL ); + } + if ( rc && unit_test.equals( "Yes" ) ) { + // set timeouts low so we don't hold up unit tests in build process + uc.setReadTimeout(5); + uc.setConnectTimeout(5); + } + return rc; + + } + + private boolean makeSecureConnection( String pURL ) { + logger.info( "makeConnection to " + pURL ); + + try { + + HostnameVerifier hostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify( String hostname, SSLSession session ) { + return true; + } + + }; + URL u = new URL( pURL ); + uc = (HttpsURLConnection) u.openConnection(); + uc.setInstanceFollowRedirects(false); + if ( ! hostnameVerify ) { + HttpsURLConnection ucs = (HttpsURLConnection) uc; + ucs.setHostnameVerifier(hostnameVerifier); + } + logger.info( "open secure connect to " + pURL ); + return(true); + } catch( UnknownHostException uhe ){ + logger.error( "Caught UnknownHostException for " + pURL); + return(false); + } catch (Exception e) { + logger.error("Unexpected error during openConnection of " + pURL ); + logger.error("Unexpected error during openConnection of ",e ); + return(false); + } + + } + private boolean makeConnection( String pURL ) { + logger.info( "makeConnection to " + pURL ); + + try { + URL u = new URL( pURL ); + uc = (HttpURLConnection) u.openConnection(); + uc.setInstanceFollowRedirects(false); + + logger.info( "open connect to " + pURL ); + return(true); + } catch( UnknownHostException uhe ){ + logger.error( "Caught UnknownHostException for " + pURL); + return(false); + } catch (Exception e) { + logger.error("Unexpected error during openConnection of " + pURL ); + logger.error("Unexpected error during openConnection of ",e ); + return(false); + } + + } + + static String bodyToString( InputStream is ) { + StringBuilder sb = new StringBuilder(); + BufferedReader br = new BufferedReader( new InputStreamReader(is)); + String line; + try { + while ((line = br.readLine()) != null ) { + sb.append( line ); + } + } catch (IOException ex ) { + errorLogger.error( "IOexception:" + ex); + } + + return sb.toString(); + } + + public String doPostTopic( Topic postTopic, ApiError err ) { + String auth = "Basic " + Base64.encodeBase64String(topicMgrCred.getBytes()); + + + String responsemessage = null; + int rc = -1; + + + try { + byte[] postData = postTopic.getBytes(); + logger.info( "post fields=" + Arrays.toString(postData)); + + if ( authMethod.equalsIgnoreCase("basicAuth") ) { + uc.setRequestProperty("Authorization", auth); + logger.info( "Authenticating with " + auth ); + } else if ( authMethod.equalsIgnoreCase("cert")) { + logger.error( "MR.authentication set for client certificate. Not supported yet."); + } + uc.setRequestMethod("POST"); + uc.setRequestProperty("Content-Type", "application/json"); + uc.setRequestProperty( "charset", "utf-8"); + uc.setRequestProperty( "Content-Length", Integer.toString( postData.length )); + uc.setUseCaches(false); + uc.setDoOutput(true); + OutputStream os = null; + + + try { + uc.connect(); + os = uc.getOutputStream(); + os.write( postData ); + + } catch (ProtocolException pe) { + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + } + } catch ( UnknownHostException uhe ) { + errorLogger.error( DmaapbcLogMessageEnum.UNKNOWN_HOST_EXCEPTION , "Unknown Host Exception" , provURL ); + err.setCode(500); + err.setMessage("Unknown Host Exception"); + err.setFields( uc.getURL().getHost()); + return new String( "500: " + uhe.getMessage()); + }catch ( ConnectException ce ) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doPostMessage because unit_test =" + unit_test ); + } else { + errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, "HTTP Connection Exception" ); + err.setCode(500); + err.setMessage("HTTP Connection Exception"); + err.setFields( uc.getURL().getHost()); + return new String( "500: " + ce.getMessage()); + } + } + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + err.setCode(rc); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + err.setMessage(responsemessage); + + + if (responsemessage == null) { + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + if (rc >= 200 && rc < 300 ) { + String responseBody = null; + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + return responseBody; + + } + + } catch (Exception e) { + errorLogger.error("Unable to read response: " + e.getMessage() ); + + } + finally { + try { + uc.disconnect(); + } catch ( Exception e ) { + errorLogger.error("Unable to disconnect"); + } + } + return new String( rc +": " + responsemessage ); + + } + + + + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrTopicConnection.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrTopicConnection.java new file mode 100644 index 0000000..b3f713f --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrTopicConnection.java @@ -0,0 +1,239 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.client; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.ProtocolException; +import java.net.URL; +import java.net.HttpURLConnection; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; + +import org.apache.commons.codec.binary.Base64; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.MR_Cluster; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +public class MrTopicConnection extends BaseLoggingClass { + private String topicURL; + + private HttpURLConnection uc; + + + private String mmProvCred; + private String unit_test; + private String authMethod; + private boolean hostnameVerify; + + public MrTopicConnection(String user, String pwd ) { + mmProvCred = new String( user + ":" + pwd ); + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + unit_test = p.getProperty( "UnitTest", "No" ); + authMethod = p.getProperty("MR.authentication", "none"); + hostnameVerify= "true".equalsIgnoreCase(p.getProperty("MR.hostnameVerify", "true")); + } + + public boolean makeTopicConnection( MR_Cluster cluster, String topic, String overrideFqdn ) { + String fqdn = overrideFqdn != null ? overrideFqdn : cluster.getFqdn(); + logger.info( "connect to cluster: " + fqdn + " for topic: " + topic ); + + + topicURL = cluster.getTopicProtocol() + "://" + fqdn + ":" + cluster.getTopicPort() + "/events/" + topic ; + + if ( "https".equals(cluster.getTopicProtocol())) { + return makeSecureConnection( topicURL ); + } + return makeConnection( topicURL ); + } + + + private boolean makeSecureConnection( String pURL ) { + logger.info( "makeConnection to " + pURL ); + + try { + HostnameVerifier hostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify( String hostname, SSLSession session ) { + return true; + } + + }; + + + URL u = new URL( pURL ); + uc = (HttpsURLConnection) u.openConnection(); + uc.setInstanceFollowRedirects(false); + if ( ! hostnameVerify ) { + HttpsURLConnection ucs = (HttpsURLConnection) uc; + ucs.setHostnameVerifier(hostnameVerifier); + } + + logger.info( "open connection to " + pURL ); + return(true); + } catch (Exception e) { + logger.error("Unexpected error during openConnection of " + pURL ); + logger.error("Error", e);; + return(false); + } + + } + private boolean makeConnection( String pURL ) { + logger.info( "makeConnection to " + pURL ); + + try { + URL u = new URL( pURL ); + uc = (HttpURLConnection) u.openConnection(); + uc.setInstanceFollowRedirects(false); + logger.info( "open connection to " + pURL ); + return(true); + } catch (Exception e) { + logger.error("Unexpected error during openConnection of " + pURL ); + logger.error("error", e); + return(false); + } + + } + + static String bodyToString( InputStream is ) { + StringBuilder sb = new StringBuilder(); + BufferedReader br = new BufferedReader( new InputStreamReader(is)); + String line; + try { + while ((line = br.readLine()) != null ) { + sb.append( line ); + } + } catch (IOException ex ) { + errorLogger.error( "IOexception:" + ex); + } + + return sb.toString(); + } + + public ApiError doPostMessage( String postMessage ) { + ApiError response = new ApiError(); + String auth = "Basic " + Base64.encodeBase64String(mmProvCred.getBytes()); + + + + try { + byte[] postData = postMessage.getBytes(); + logger.info( "post fields=" + postMessage ); + if ( authMethod.equalsIgnoreCase("basicAuth") ) { + uc.setRequestProperty("Authorization", auth); + logger.info( "Authenticating with " + auth ); + } else if ( authMethod.equalsIgnoreCase("cert")) { + logger.error( "MR.authentication set for client certificate. Not supported yet."); + } + uc.setRequestMethod("POST"); + uc.setRequestProperty("Content-Type", "application/json"); + uc.setRequestProperty( "charset", "utf-8"); + uc.setRequestProperty( "Content-Length", Integer.toString( postData.length )); + uc.setUseCaches(false); + uc.setDoOutput(true); + OutputStream os = null; + + + try { + uc.connect(); + os = uc.getOutputStream(); + os.write( postData ); + + } catch (ProtocolException pe) { + // Rcvd error instead of 100-Continue + callSetDoOutputOnError(); + + } catch ( SSLException se ) { + logger.error("Error", se); + response.setCode(500); + response.setMessage( se.getMessage()); + return response; + + } + response.setCode( uc.getResponseCode()); + logger.info( "http response code:" + response.getCode()); + response.setMessage( uc.getResponseMessage() ); + logger.info( "response message=" + response.getMessage() ); + + + if ( response.getMessage() == null) { + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + response.setMessage( h0.substring(j + 1) ); + } + } + } + if ( response.is2xx() ) { + response.setFields( bodyToString( uc.getInputStream() ) ); + logger.info( "responseBody=" + response.getFields() ); + return response; + + } + + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + response.setCode(201); + response.setMessage( "simulated response"); + logger.info( "artificial 201 response from doPostMessage because unit_test =" + unit_test ); + } else { + + response.setCode(500); + response.setMessage( "Unable to read response"); + logger.warn( response.getMessage() ); + logger.error("Error", e); + } + } + finally { + try { + uc.disconnect(); + } catch ( Exception e ) { + logger.error("Error", e); + } + } + return response; + + } + + public void callSetDoOutputOnError() { + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error("Error", e); + } + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnWrapper.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnWrapper.java new file mode 100644 index 0000000..2317fe4 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnWrapper.java @@ -0,0 +1,83 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.database; + +import java.sql.*; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + + +public abstract class ConnWrapper<T, U> { + EELFLogger logger = EELFManager.getInstance().getLogger( ConnWrapper.class ); + protected Connection c; + protected PreparedStatement ps; + protected ResultSet rs; + protected abstract T run(U u) throws Exception; + public T protect(ConnectionFactory cf, U u) { + try { + try { + return(attempt(cf, u, false)); + } catch (SQLException sqle) { + logger.error("Error", sqle); + return(attempt(cf, u, true)); + } + } catch (RuntimeException rte) { + throw rte; + } catch (Exception e) { + throw new DBException(e); + } + } + private T attempt(ConnectionFactory cf, U u, boolean fresh) throws Exception { + c = null; + ps = null; + rs = null; + try { + c = cf.get(fresh); + T ret = run(u); + cf.release(c); + c = null; + return(ret); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception e) { + logger.error("Error", e); + }} + rs = null; + if (ps != null) { + try { + ps.close(); + } catch (Exception e) { + logger.error("Error", e); + }} + ps = null; + if (c != null) { + try { + c.close(); + } catch (Exception e) { + logger.error("Error", e); + }} + c = null; + } + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnectionFactory.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnectionFactory.java new file mode 100644 index 0000000..6004d9b --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnectionFactory.java @@ -0,0 +1,117 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.database; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; +import java.util.concurrent.TimeUnit; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +public class ConnectionFactory extends BaseLoggingClass { + + static final int PREPARE_PSQL_CONNECTION_ATTEMPTS = 5; + + static { + try { + Class.forName("org.postgresql.Driver"); + } catch (Exception e) { + errorLogger.error("Unable to load postgres driver " + e, e); + } + } + private static ConnectionFactory instance = new ConnectionFactory(); + private String host; + private String dbname; + private String dbuser; + private String dbcr; + private String schema; + + public ConnectionFactory() { + Properties p = DmaapConfig.getConfig(); + host = p.getProperty("DB.host", "dcae-pstg-write-ftl.domain.notset.com"); + dbname = p.getProperty("DB.name", "dmaap"); + dbuser = getValue(p, "DB.user", "dmaap_admin"); + dbcr = getValue(p, "DB.cred", "test234-ftl"); + schema = p.getProperty("DB.schema", "dmaap_admin"); + } + + private static String getValue(final Properties props, final String value, final String defaultValue) { + String prop = props.getProperty(value, defaultValue); + if (prop != null && prop.matches("[$][{].*[}]$")) { + return System.getenv(prop.substring(2, prop.length() - 1)); + } + return prop; + } + + public static ConnectionFactory getDefaultInstance() { + return(instance); + } + private Connection[] pool = new Connection[5]; + private int cur; + public Connection get(boolean fresh) throws SQLException { + if (!fresh) { + synchronized(this) { + if (cur > 0) { + return(pool[--cur]); + } + } + } + Properties p = new Properties(); + p.put("user", dbuser); + p.put("password", dbcr); + for (int i=1; i<PREPARE_PSQL_CONNECTION_ATTEMPTS; i++){ + try{ + return(DriverManager.getConnection("jdbc:postgresql://" + host + "/" + dbname, p)); + }catch(SQLException e){ + errorLogger.error("Unable to connect to the postgres server. " + i + "attempt failed. ", e); + waitFor(1); + } + } + return(DriverManager.getConnection("jdbc:postgresql://" + host + "/" + dbname, p)); + } + public String getSchema() { + return(schema); + } + public void release(Connection c) { + synchronized(this) { + if (cur < pool.length) { + pool[cur++] = c; + return; + } + } + try { + c.close(); + } catch (Exception e) { + errorLogger.error("Error", e); + } + } + private void waitFor(long seconds){ + try { + TimeUnit.SECONDS.sleep(seconds); + } catch (InterruptedException e) { + debugLogger.debug("Waiting interrupted. ", e); + Thread.currentThread().interrupt(); + } + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBException.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBException.java new file mode 100644 index 0000000..6538167 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBException.java @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.database; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; + +public class DBException extends RuntimeException { + static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger(); + public DBException(Exception e) { + super(e); + errorLogger.error(DmaapbcLogMessageEnum.DB_ACCESS_ERROR, e.getMessage()); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBFieldHandler.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBFieldHandler.java new file mode 100644 index 0000000..618932e --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBFieldHandler.java @@ -0,0 +1,205 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.database; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import java.lang.reflect.Method; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.HashMap; +import java.util.Map; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; + + +public class DBFieldHandler { + static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger(); + + public DBFieldHandler(Class<?> c, String fieldname, int fieldnum) throws Exception { + this(c, fieldname, fieldnum, null); + } + public DBFieldHandler(Class<?> c, String fieldname, int fieldnum, SqlOp op) throws Exception { + this.fieldnum = fieldnum; + StringBuilder sb = new StringBuilder(); + for (String s: fieldname.split("_")) { + sb.append(s.substring(0, 1).toUpperCase()).append(s.substring(1)); + } + String camelcase = sb.toString(); + try { + objget = c.getMethod("is" + camelcase); + } catch (Exception e) { + errorLogger.warn("No 'is' method for " + c.getName() + " so trying 'get' method"); + objget = c.getMethod("get" + camelcase); + } + objset = c.getMethod("set" + camelcase, objget.getReturnType()); + sqlop = op; + if (sqlop != null) { + return; + } + Class<?> x = objget.getReturnType(); + if (x.isEnum()) { + sqlop = new EnumSql(x); + return; + } + sqlop = sqltypes.get(x.getName()); + if (sqlop != null) { + return; + } + errorLogger.error(DmaapbcLogMessageEnum.DB_NO_FIELD_HANDLER, c.getName(), fieldname, Integer.toString(fieldnum), x.getName()); + } + + public static interface SqlOp { + public Object get(ResultSet rs, int index) throws Exception; + public void set(PreparedStatement ps, int index, Object value) throws Exception; + } + private static class AofString implements SqlOp { + public Object get(ResultSet rs, int index) throws Exception { + String val = rs.getString(index); + if (val == null) { + return(null); + } + String[] ret = val.split(","); + for (int i = 0; i < ret.length; i++) { + ret[i] = funesc(ret[i]); + } + return(ret); + } + public void set(PreparedStatement ps, int index, Object x) throws Exception { + String[] val = (String[])x; + if (val == null) { + ps.setString(index, null); + return; + } + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (String s: val) { + sb.append(sep).append(fesc(s)); + sep = ","; + } + ps.setString(index, sb.toString()); + } + } + private static class EnumSql implements SqlOp { + private Class enclass; + public EnumSql(Class enclass) { + this.enclass = enclass; + } + @SuppressWarnings("unchecked") + public Object get(ResultSet rs, int index) throws Exception { + String val = rs.getString(index); + if (val == null) { + return(null); + } else { + return(Enum.valueOf(enclass, val)); + } + } + public void set(PreparedStatement ps, int index, Object value) throws Exception { + if (value == null) { + ps.setString(index, null); + } else { + ps.setString(index, value.toString()); + } + } + } + private static class SqlDate implements SqlOp { + public Object get(ResultSet rs, int index) throws Exception { + return(rs.getTimestamp(index)); + } + public void set(PreparedStatement ps, int index, Object val) throws Exception { + if (val instanceof java.util.Date && !(val instanceof java.sql.Timestamp)) { + val = new java.sql.Timestamp(((java.util.Date)val).getTime()); + } + ps.setTimestamp(index, (java.sql.Timestamp)val); + } + } + private static class SqlType implements SqlOp { + private Method sqlget; + private Method sqlset; + private SqlType(String tag) throws Exception { + sqlget = ResultSet.class.getMethod("get" + tag, Integer.TYPE); + sqlset = PreparedStatement.class.getMethod("set" + tag, Integer.TYPE, sqlget.getReturnType()); + sqltypes.put(sqlget.getReturnType().getName(), this); + } + public Object get(ResultSet rs, int index) throws Exception { + return(sqlget.invoke(rs, index)); + } + public void set(PreparedStatement ps, int index, Object val) throws Exception { + try { + sqlset.invoke(ps, index, val); + } catch (Exception e) { + errorLogger.error(DmaapbcLogMessageEnum.DB_FIELD_INIT_ERROR, Integer.toString(index), val.toString(), ps.toString()); + throw e; + } + } + } + private static Map<String, SqlOp> sqltypes; + static { + sqltypes = new HashMap<>(); + sqltypes.put("[Ljava.lang.String;", new AofString()); + sqltypes.put("java.util.Date", new SqlDate()); + try { + new SqlType("Boolean"); + new SqlType("Timestamp"); + new SqlType("Double"); + new SqlType("Float"); + new SqlType("Int"); + new SqlType("Long"); + new SqlType("Short"); + new SqlType("String"); + } catch (Exception e) { + errorLogger.error("Error", e); + errorLogger.error(DmaapbcLogMessageEnum.DB_ACCESS_INIT_ERROR, e.getMessage() ); + } + } + private Method objget; + private Method objset; + private SqlOp sqlop; + private int fieldnum; + public void copy(Object from, Object to) throws Exception { + objset.invoke(to, objget.invoke(from)); + } + public void setKey(Object o, String key) throws Exception { + objset.invoke(o, key); + } + public String getKey(Object o) throws Exception { + return((String)objget.invoke(o)); + } + public void toSQL(Object o, PreparedStatement ps) throws Exception { + sqlop.set(ps, fieldnum, objget.invoke(o)); + } + public void fromSQL(ResultSet r, Object o) throws Exception { + objset.invoke(o, sqlop.get(r, fieldnum)); + } + public static String fesc(String s) { + if (s == null) { + return(s); + } + return(s.replaceAll("@", "@a").replaceAll(";", "@s").replaceAll(",", "@c")); + } + public static String funesc(String s) { + if (s == null) { + return(s); + } + return(s.replaceAll("@c", ",").replaceAll("@s", ";").replaceAll("@a", "@")); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBMap.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBMap.java new file mode 100644 index 0000000..b3282ed --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBMap.java @@ -0,0 +1,137 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.database; + +import java.sql.*; +import java.util.*; + +public class DBMap<C> extends TableHandler<C> implements Map<String, C> { + public DBMap(Class<C> cls, String tabname, String keyfield) throws Exception { + this(ConnectionFactory.getDefaultInstance(), cls, tabname, keyfield); + } + public DBMap(ConnectionFactory cf, Class<C> cls, String tabname, String keyfield) throws Exception { + super(cf, cls, tabname, keyfield); + } + public void clear() { + throw new UnsupportedOperationException(); + } + public boolean containsKey(Object key) { + return(get(key) != null); + } + public boolean containsValue(Object value) { + throw new UnsupportedOperationException(); + } + public boolean isEmpty() { + return(false); + } + public Set<Map.Entry<String, C>> entrySet() { + return(list()); + } + public Set<String> keySet() { + Set<String> ret = new HashSet<>(); + for (Map.Entry<String, C> x: list()) { + ret.add(x.getKey()); + } + return(ret); + } + public void putAll(Map<? extends String, ? extends C> m) { + throw new UnsupportedOperationException(); + } + public int size() { + return(2); + } + public Collection<C> values() { + Collection<C> ret = new ArrayList<>(); + for (Map.Entry<String, C> x: list()) { + ret.add(x.getValue()); + } + return(ret); + } + public C get(Object key) { + if (!(key instanceof String)) { + return(null); + } + return((new ConnWrapper<C, String>() { + protected C run(String key) throws Exception { + ps = c.prepareStatement(getstmt); + ps.setString(1, (String)key); + rs = ps.executeQuery(); + if (!rs.next()) { + return(null); + } + C ret = cls.newInstance(); + for (DBFieldHandler f: fields) { + f.fromSQL(rs, ret); + } + return(ret); + } + }).protect(cf, (String)key)); + } + public Set<Map.Entry<String, C>> list() { + return((new ConnWrapper<Set<Map.Entry<String, C>>, Object>() { + protected Set<Map.Entry<String, C>> run(Object junk) throws Exception { + DBFieldHandler keyfield = fields[fields.length - 1]; + ps = c.prepareStatement(liststmt); + rs = ps.executeQuery(); + Set<Map.Entry<String, C>> ret = new HashSet<>(); + while (rs.next()) { + C val = cls.newInstance(); + for (DBFieldHandler f: fields) { + f.fromSQL(rs, val); + } + String key = keyfield.getKey(val); + ret.add(new AbstractMap.SimpleEntry<String, C>(key, val)); + } + return(ret); + } + }).protect(cf, null)); + } + public C put(String key, C val) { + try { + fields[fields.length - 1].setKey(val, key); + } catch (Exception e) { + throw new DBException(e); + } + return((new ConnWrapper<C, C>() { + protected C run(C val) throws Exception { + ps = c.prepareStatement(insorreplstmt); + for (DBFieldHandler f: fields) { + f.toSQL(val, ps); + } + ps.executeUpdate(); + return(null); + } + }).protect(cf, val)); + } + public C remove(Object key) { + if (!(key instanceof String)) { + return(null); + } + return((new ConnWrapper<C, String>() { + protected C run(String key) throws Exception { + ps = c.prepareStatement(delstmt); + ps.setString(1, key); + ps.executeUpdate(); + return(null); + } + }).protect(cf, (String)key)); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBSingleton.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBSingleton.java new file mode 100644 index 0000000..2633d70 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBSingleton.java @@ -0,0 +1,98 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.database; + +import java.sql.*; +import java.util.*; + +import org.onap.dmaap.dbcapi.util.Singleton; + +public class DBSingleton<C> extends TableHandler<C> implements Singleton<C> { + private C singleton; + public DBSingleton(Class<C> cls, String tabname) throws Exception { + this(ConnectionFactory.getDefaultInstance(), cls, tabname); + } + public DBSingleton(ConnectionFactory cf, Class<C> cls, String tabname) throws Exception { + super(cf, cls, tabname, null); + singleton = cls.newInstance(); + } + public C get() { + return((new ConnWrapper<C, Object>() { + protected C run(Object junk) throws Exception { + ps = c.prepareStatement(getstmt); + rs = ps.executeQuery(); + if (!rs.next()) { + return(null); + } + for (DBFieldHandler f: fields) { + f.fromSQL(rs, singleton); + } + return(singleton); + } + }).protect(cf, null)); + } + public void init(C val) { + if (get() != null) { + return; + } + (new ConnWrapper<Void, C>() { + protected Void run(C val) throws Exception { + ps = c.prepareStatement(initstmt); + for (DBFieldHandler f: fields) { + f.toSQL(val, ps); + } + ps.executeUpdate(); + if (val != singleton) { + for (DBFieldHandler f: fields) { + f.copy(val, singleton); + } + } + return(null); + } + }).protect(cf, val); + } + public void update(C val) { + (new ConnWrapper<Void, C>() { + protected Void run(C val) throws Exception { + ps = c.prepareStatement(insorreplstmt); + for (DBFieldHandler f: fields) { + f.toSQL(val, ps); + } + ps.executeUpdate(); + if (val != singleton) { + for (DBFieldHandler f: fields) { + f.copy(val, singleton); + } + } + return(null); + } + }).protect(cf, val); + } + public void remove() throws DBException { + (new ConnWrapper<Void, Object>() { + protected Void run(Object junk) throws Exception { + ps = c.prepareStatement(delstmt); + ps.executeUpdate(); + return(null); + } + }).protect(cf, null); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DatabaseClass.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DatabaseClass.java new file mode 100644 index 0000000..c82e964 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DatabaseClass.java @@ -0,0 +1,286 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.database; + +import java.util.*; +import java.sql.*; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.model.*; +import org.onap.dmaap.dbcapi.util.DmaapConfig; +import org.onap.dmaap.dbcapi.util.Singleton; + + +public class DatabaseClass extends BaseLoggingClass { + + private static Singleton<Dmaap> dmaap; + private static Map<String, DcaeLocation> dcaeLocations; + private static Map<String, DR_Node> dr_nodes; + private static Map<String, DR_Pub> dr_pubs; + private static Map<String, DR_Sub> dr_subs; + private static Map<String, MR_Client> mr_clients; + private static Map<String, MR_Cluster> mr_clusters; + private static Map<String, Feed> feeds; + private static Map<String, Topic> topics; + private static Map<String, MirrorMaker> mirrors; + + private static long lastTime = 0L; + private static DBType databaseType; + + private enum DBType { + PGSQL, MEMORY + } + + public static Singleton<Dmaap> getDmaap() { + return dmaap; + } + + + public static Map<String, DcaeLocation> getDcaeLocations() { + return dcaeLocations; + } + + public static Map<String, DR_Node> getDr_nodes() { + return dr_nodes; + } + + public static Map<String, DR_Sub> getDr_subs() { + return dr_subs; + } + + public static Map<String, DR_Pub> getDr_pubs() { + return dr_pubs; + } + + public static Map<String, MR_Client> getMr_clients() { + return mr_clients; + } + + + public static Map<String, MR_Cluster> getMr_clusters() { + return mr_clusters; + } + + public static Map<String, Feed> getFeeds() { + return feeds; + } + + public static Map<String, Topic> getTopics() { + return topics; + } + + public static Map<String, MirrorMaker> getMirrorMakers() { + return mirrors; + } + + static { + try { + appLogger.info("begin static initialization"); + appLogger.info("initializing dmaap"); + determineDatabaseType(); + + switch (databaseType) { + case PGSQL: + databaseResourceInit(); + break; + case MEMORY: + inMemoryResourceInit(); + break; + } + + dmaap.init(new Dmaap.DmaapBuilder().setVer("0").setTnr("").setDn("").setDpu("").setLu("").setBat("").setNk("").setAko("").createDmaap()); + // force initial read from DB, if it exists + @SuppressWarnings("unused") + Dmaap dmx = dmaap.get(); + + // old code in this spot would read from properties file as part of init. + // but all those properties are now set via /dmaap API + + } catch (Exception e) { + errorLogger.error("Error", e); + errorLogger.error(DmaapbcLogMessageEnum.DB_UPDATE_ERROR, e.getMessage()); + } + + } + + public static synchronized String getNextClientId() { + + long id = System.currentTimeMillis(); + if (id <= lastTime) { + id = lastTime + 1; + } + lastTime = id; + return Long.toString(id); + } + + public static synchronized void clearDatabase() { + switch (databaseType) { + case PGSQL: + try { + initDatabase(); + } catch (Exception e) { + errorLogger.error("Error initializing database access " + e, e); + } + break; + case MEMORY: + initMemoryDatabase(); + break; + } + } + + private static void inMemoryResourceInit() { + appLogger.info("Data from memory"); + dmaap = new Singleton<Dmaap>() { + private Dmaap dmaap; + + public void remove() { + dmaap = null; + } + + public void init(Dmaap val) { + if (dmaap == null) { + dmaap = val; + } else { + update(val); + } + } + + public Dmaap get() { + return (dmaap); + } + + public void update(Dmaap nd) { + dmaap.setVersion(nd.getVersion()); + dmaap.setTopicNsRoot(nd.getTopicNsRoot()); + dmaap.setDmaapName(nd.getDmaapName()); + dmaap.setDrProvUrl(nd.getDrProvUrl()); + dmaap.setBridgeAdminTopic(nd.getBridgeAdminTopic()); + dmaap.setLoggingUrl(nd.getLoggingUrl()); + dmaap.setNodeKey(nd.getNodeKey()); + dmaap.setAccessKeyOwner(nd.getAccessKeyOwner()); + } + }; + initMemoryDatabase(); + } + + private static void databaseResourceInit() { + appLogger.info("Data from database"); + try { + LoadSchema.loadSchema(); + } catch (Exception e) { + appLogger.warn("Problem updating DB schema", e); + } + try { + Thread.sleep(5000); + dmaap = new DBSingleton<>(Dmaap.class, "dmaap"); + TableHandler.setSpecialCase("topic", "replication_case", new TopicReplicationTypeHandler()); + TableHandler.setSpecialCase("mirror_maker", "topics", new MirrorTopicsHandler()); + initDatabase(); + } catch (Exception e) { + errorLogger.error("Error initializing database access " + e, e); + System.exit(1); + } + } + + private static class MirrorTopicsHandler implements DBFieldHandler.SqlOp { + + public Object get(ResultSet rs, int index) throws Exception { + String val = rs.getString(index); + if (val == null) { + return (null); + } + List<String> rv = new ArrayList<>(); + for (String s : val.split(",")) { + rv.add(new String(s)); + } + return (rv); + } + + public void set(PreparedStatement ps, int index, Object val) throws Exception { + if (val == null) { + ps.setString(index, null); + return; + } + @SuppressWarnings("unchecked") + List<String> xv = (List<String>) val; + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (Object o : xv) { + String rv = (String) o; + sb.append(sep).append(DBFieldHandler.fesc(rv)); + sep = ","; + } + ps.setString(index, sb.toString()); + } + } + + private static class TopicReplicationTypeHandler implements DBFieldHandler.SqlOp { + + public Object get(ResultSet rs, int index) throws Exception { + int val = rs.getInt(index); + + return (ReplicationType.valueOf(val)); + } + + public void set(PreparedStatement ps, int index, Object val) throws Exception { + if (val == null) { + ps.setInt(index, 0); + return; + } + @SuppressWarnings("unchecked") + ReplicationType rep = (ReplicationType) val; + ps.setInt(index, rep.getValue()); + } + } + + private static void initMemoryDatabase() { + dcaeLocations = new HashMap<>(); + dr_nodes = new HashMap<>(); + dr_pubs = new HashMap<>(); + dr_subs = new HashMap<>(); + mr_clients = new HashMap<>(); + mr_clusters = new HashMap<>(); + feeds = new HashMap<>(); + topics = new HashMap<>(); + mirrors = new HashMap<>(); + } + + private static void initDatabase() throws Exception { + dcaeLocations = new DBMap<>(DcaeLocation.class, "dcae_location", "dcae_location_name"); + dr_nodes = new DBMap<>(DR_Node.class, "dr_node", "fqdn"); + dr_pubs = new DBMap<>(DR_Pub.class, "dr_pub", "pub_id"); + dr_subs = new DBMap<>(DR_Sub.class, "dr_sub", "sub_id"); + mr_clients = new DBMap<>(MR_Client.class, "mr_client", "mr_client_id"); + mr_clusters = new DBMap<>(MR_Cluster.class, "mr_cluster", "dcae_location_name"); + feeds = new DBMap<>(Feed.class, "feed", "feed_id"); + topics = new DBMap<>(Topic.class, "topic", "fqtn"); + mirrors = new DBMap<>(MirrorMaker.class, "mirror_maker", "mm_name"); + } + + private static void determineDatabaseType() { + DmaapConfig dmaapConfig = (DmaapConfig) DmaapConfig.getConfig(); + String isPgSQLset = dmaapConfig.getProperty("UsePGSQL", "false"); + databaseType = isPgSQLset.equalsIgnoreCase("true") ? DBType.PGSQL : DBType.MEMORY; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/LoadSchema.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/LoadSchema.java new file mode 100644 index 0000000..97bea4d --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/LoadSchema.java @@ -0,0 +1,57 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.database; + +import java.io.FileReader; +import java.io.LineNumberReader; +import java.sql.Connection; +import java.sql.Statement; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; + +public class LoadSchema extends BaseLoggingClass { + + private LoadSchema(){} + + static void loadSchema() { + ConnectionFactory cf = ConnectionFactory.getDefaultInstance(); + try (LineNumberReader lineReader = new LineNumberReader(new FileReader("/opt/app/dmaapbc/misc/schema_all.sql")); + Connection c = cf.get(true); + Statement stmt = c.createStatement()) { + StringBuilder strBuilder = new StringBuilder(); + String line; + while ((line = lineReader.readLine()) != null) { + if (!line.startsWith("--")) { + line = line.trim(); + strBuilder.append(line); + if (line.endsWith(";")) { + String sql = strBuilder.toString(); + strBuilder.setLength(0); + stmt.execute(sql); + appLogger.debug("SQL EXECUTE SUCCESS: " + sql); + } + } + } + strBuilder.setLength(0); + } catch (Exception e) { + errorLogger.error("Error when initializing table: " + e.getMessage(), e); + } + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/TableHandler.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/TableHandler.java new file mode 100644 index 0000000..a85bdae --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/TableHandler.java @@ -0,0 +1,126 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.database; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; + +class TableHandler<C> extends BaseLoggingClass { + protected ConnectionFactory cf; + protected boolean haskey; + protected String delstmt; + protected String insorreplstmt; + protected String getstmt; + protected String liststmt; + protected String initstmt; + protected Class<C> cls; + protected DBFieldHandler[] fields; + private static Map<String, Map<String, DBFieldHandler.SqlOp>> exceptions = new HashMap<>(); + private String select = "SELECT "; + private String from = " FROM "; + + protected TableHandler(Class<C> cls, String tabname, String keyname) throws Exception { + this(ConnectionFactory.getDefaultInstance(), cls, tabname, keyname); + } + protected TableHandler(ConnectionFactory cf, Class<C> cls, String tabname, String keyname) throws Exception { + this.cf = cf; + Connection c = null; + try { + c = cf.get(false); + setup(c.getMetaData(), cls, tabname, keyname); + } finally { + if (c != null) { + cf.release(c); + } + } + } + + public static void setSpecialCase(String dbtabname, String dbfldname, DBFieldHandler.SqlOp handler) { + Map<String, DBFieldHandler.SqlOp> m = exceptions.get(dbtabname); + if (m == null) { + m = new HashMap<>(); + exceptions.put(dbtabname, m); + } + m.put(dbfldname, handler); + } + public static DBFieldHandler.SqlOp getSpecialCase(String dbtabname, String dbfldname) { + Map<String, DBFieldHandler.SqlOp> m = exceptions.get(dbtabname); + if (m != null) { + return(m.get(dbfldname)); + } + return(null); + } + + private void setup(DatabaseMetaData dmd, Class<C> cls, String tabname, String keyname) throws Exception { + this.cls = cls; + Vector<DBFieldHandler> h = new Vector<>(); + String qualifiedTableName = String.format( "%s.%s", cf.getSchema(), tabname ); + ResultSet rs = dmd.getColumns("", cf.getSchema(), tabname, null); + StringBuilder sb1 = new StringBuilder(); + StringBuilder sb2 = new StringBuilder(); + StringBuilder sb3 = new StringBuilder(); + int count = 0; + while (rs.next()) { + if (!rs.getString(3).equals(tabname)) { + continue; + } + String cname = rs.getString(4); + if (cname.equals(keyname)) { + haskey = true; + continue; + } + sb1.append(", ").append(cname); + sb2.append(", ?"); + sb3.append(", EXCLUDED.").append(cname); + count++; + h.add(new DBFieldHandler(cls, cname, count, getSpecialCase(tabname, cname))); + } + if (count == 0) { + throw new SQLException("Table " + tabname + " not found"); + } + String clist = sb1.substring(2); + String qlist = sb2.substring(2); + String elist = sb3.substring(2); + if (keyname != null && !haskey) { + throw new SQLException("Table " + tabname + " does not have key column " + keyname + " not found"); + } + if (haskey) { + count++; + h.add(new DBFieldHandler(cls, keyname, count, getSpecialCase(tabname, keyname))); + delstmt = "DELETE FROM " + qualifiedTableName + " WHERE " + keyname + " = ?"; + insorreplstmt = "INSERT INTO " + qualifiedTableName + " (" + clist + ", " + keyname + ") VALUES (" + qlist + ", ?) ON CONFLICT(" + keyname + ") DO UPDATE SET (" + clist + ") = (" + elist + ")"; + getstmt = select + clist + ", " + keyname + from + qualifiedTableName + " WHERE " + keyname + " = ?"; + liststmt = select + clist + ", " + keyname + from + qualifiedTableName; + } else { + delstmt = "DELETE FROM " + qualifiedTableName; + initstmt = "INSERT INTO " + qualifiedTableName + " (" + clist + ") VALUES (" + qlist + ")"; + insorreplstmt = "UPDATE " + qualifiedTableName + " SET (" + clist + ") = (" + qlist + ")"; + getstmt = select + clist + ", " + keyname + from + qualifiedTableName; + } + fields = h.toArray(new DBFieldHandler[h.size()]); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/BaseLoggingClass.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/BaseLoggingClass.java new file mode 100644 index 0000000..4bde17a --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/BaseLoggingClass.java @@ -0,0 +1,36 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.logging; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +public abstract class BaseLoggingClass { + protected EELFLogger logger = EELFManager.getInstance().getLogger( super.getClass()); + protected static final EELFLogger appLogger = EELFManager.getInstance().getApplicationLogger(); + protected static final EELFLogger auditLogger = EELFManager.getInstance().getAuditLogger(); + protected static final EELFLogger debugLogger = EELFManager.getInstance().getDebugLogger(); + protected static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger(); + protected static final EELFLogger metricsLogger = EELFManager.getInstance().getMetricsLogger(); + protected static final EELFLogger serverLogger = EELFManager.getInstance().getServerLogger(); + + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/DmaapbcLogMessageEnum.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/DmaapbcLogMessageEnum.java new file mode 100644 index 0000000..86c5fe0 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/DmaapbcLogMessageEnum.java @@ -0,0 +1,81 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.logging; + +import com.att.eelf.i18n.EELFResourceManager; +import com.att.eelf.i18n.EELFResolvableErrorEnum; + +public enum DmaapbcLogMessageEnum implements EELFResolvableErrorEnum { +//0xx sample stock messages + MESSAGE_SAMPLE_NOARGS, + MESSAGE_SAMPLE_ONE_ARG, + MESSAGE_SAMPLE_TWO_ARGS, + +// 1xx Permission Errors + AAF_CREDENTIAL_ERROR, + CODEC_CREDENTIAL_ERROR, + PE_AUTHENTICATION_ERROR, + DR_PROV_AUTHORIZATION, + +// 2xx Availability Errors/Timeouts + DRIVER_UNAVAILABLE, + HTTP_CONNECTION_ERROR, + HTTP_CONNECTION_EXCEPTION, + UNKNOWN_HOST_EXCEPTION, + + +// 3xx Data Errors + IO_EXCEPTION, + SSL_HANDSHAKE_ERROR, + AAF_UNEXPECTED_RESPONSE, + PE_EXCEPTION, + SOCKET_EXCEPTION, + JSON_PARSING_ERROR, + DECRYPT_IO_ERROR, + +//4xx Schema Errors + DB_UPGRADE_ERROR, + DB_INIT_ERROR, + DB_UPDATE_ERROR, + DB_ACCESS_ERROR, + DB_FIELD_INIT_ERROR, + DB_ACCESS_INIT_ERROR, + DB_NO_FIELD_HANDLER, + + +// 5xx Business Process Errors + PREREQ_DMAAP_OBJECT, + PROV_OUT_OF_SYNC, + MM_CIRCULAR_REF, + TOPIC_CREATE_ERROR, + INGRESS_CREATE_ERROR, + FEED_PUB_PROV_ERROR, + FEED_SUB_PROV_ERROR, + MM_PUBLISH_ERROR, + EGRESS_CREATE_ERROR, + +// 900 Unknown Errors + UNEXPECTED_CONDITION; + + static { + EELFResourceManager.loadMessageBundle("logmsg"); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/logmsg.properties b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/logmsg.properties new file mode 100644 index 0000000..6548433 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/logmsg.properties @@ -0,0 +1,240 @@ +### +# ============LICENSE_START======================================================= +# org.onap.dmaap +# ================================================================================ +# Copyright (C) 2017 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========================================================= +### + +# 0xx sample stock messages +MESSAGE_SAMPLE_NOARGS=\ + 01|\ + Ignore: demo msg with no arg|\ + No resolution|\ + An example of a message with no args + +MESSAGE_SAMPLE_ONE_ARG=\ + 02|\ + Ignore: demo msg with 1 arg {0}|\ + No resolution|\ + An example of a message with 1 arg + +MESSAGE_SAMPLE_TWO_ARGS=\ + 03|\ + Ignore: demo msg with arg1 {0} and arg2 {1}|\ + No resolution|\ + An example of a message with 2 args + + + +# 1xx Permission Errors +AAF_CREDENTIAL_ERROR=\ + 101|\ + Service credentials ({0}) are not valid for AAF connection|\ + Check credentials are valid in appropriate AAF environment.|\ + Connection to AAF was not allowed for the specified credentials. + +CODEC_CREDENTIAL_ERROR=\ + 102|\ + Failed to read CredentialCodecKeyfile {0} with error {1}|\ + Check if CredentialCodecKeyfile has been corrupted.|\ + CredentialCodecKeyfile is not in sync with application + +PE_AUTHENTICATION_ERROR=\ + 103|\ + User {0} perms {1} caught PolicyEngineException {1}|\ + User needs to be granted perm before access.|\ + Identified user was not authorized for the specific perm. + +DR_PROV_AUTHORIZATION=\ + 104|\ + Not authorized for API {0}|\ + Bus Controller host needs to be provisioned as a Node and an AUTHORIZED_HOST.|\ + DR Prov indicates that Bus Controller host is not authorized for the specified API. + +# 2xx Availability Errors/Timeouts +DRIVER_UNAVAILABLE=\ + 201|\ + Unable to load driver {0}. Error {1}|\ + Check that specified driver is installed and accessible to application.|\ + The software attempted to load a driver and was not successful. + +HTTP_CONNECTION_ERROR=\ + 202|\ + Exception during openConnection to {0} failed with {1}|\ + Confirm syntax of URL is correct and network access from this host is allowed.|\ + An attempt to URL.openConnection failed + +HTTP_CONNECTION_EXCEPTION=\ + 203|\ + Connection to {0} refused because {1}|\ + Check if this is the proper server.|\ + Application caught a ConnectionException + +UNKNOWN_HOST_EXCEPTION=\ + 204|\ + Caught exception {0} attempting to access {1}|\ + Confirm that host is in DNS|\ + Caught UnknownHostException when connecting to the designated host name. + +# 3xx Data Errors +IO_EXCEPTION=\ + 301|\ + IOexception {0}|\ + No resolution.|\ + Generic IO Exception condition + +SSL_HANDSHAKE_ERROR=\ + 302|\ + SSLHandshakeException from URL {0}|\ + Confirm that target host has proper SSL certificate for DNS value used to access it.|\ + SSLHandshake exception thrown on HttpsURLConnection method + +AAF_UNEXPECTED_RESPONSE=\ + 303|\ + rc= {0} :unable to {1} for {2}|\ + Check configuration for this AAF instance.|\ + Unexpected response from AAF for the intended action + +PE_EXCEPTION=\ + 304|\ + Trying to read {0} and caught PolicyEngineException {1}|\ + Check config file exists and has proper settings.|\ + An unexpected exception from PE was caught. + +SOCKET_EXCEPTION=\ + 305|\ + Caught exception {0} while {1}|\ + No comment.|\ + An unexpected socket exception was caught while performing the specified action. + +JSON_PARSING_ERROR=\ + 306|\ + ParsingException for object {0} using data:{1}|\ + No comment.|\ + The JSON data provided to the object was not in the expected format + + DECRYPT_IO_ERROR=\ + 307|\ + IO Error attempting using {0} to decrypt value {1}|\ + Check permissions of file set for property CredentialCodecKeyfile.|\ + Error using codec file for decryption. + +# 4xx Schema Errors + +DB_UPGRADE_ERROR=\ + 401|\ + Problem updating DB schema. {0}|\ + Examine stack trace for clues.|\ + The software was not able to process the sql file resources in the jar file. + +DB_INIT_ERROR=\ + 402|\ + Error initializing database access: {0}|\ + Correct configuration based on detail.|\ + The software was not able initialize objects from the DB. + +DB_UPDATE_ERROR=\ + 403|\ + Error while updating DB: {0}|\ + Correct configuration based on detail.|\ + The software was not able to update record(s) in the DB. + +DB_ACCESS_ERROR=\ + 404|\ + Database access problem: {0}|\ + Correct configuration based on detail.|\ + An exception related to DB access was caught and logged. + +DB_FIELD_INIT_ERROR=\ + 405|\ + Problem setting field {0} to {1} statement is {2}|\ + DB schema may be out of sync with code.|\ + SQLDate.set() failed to set field value. + +DB_ACCESS_INIT_ERROR=\ + 406|\ + Problem initializing sql access methods {0} |\ + No comment.|\ + Error encountered while initializing basic field types. + +DB_NO_FIELD_HANDLER=\ + 407|\ + No field handler for class {0} field {1} index {2} type {3}|\ + No comment.|\ + Missing field handler for specified code. + + +# 5xx Business Process Errors +PREREQ_DMAAP_OBJECT=\ + 501|\ + Attempt to access {0} before dmaap object resource is available.|\ + No remediation.|\ + The dmaap object needs to be defined before attempting the desired access + +PROV_OUT_OF_SYNC=\ + 502|\ + Resource {0} with id {1} was not in sync with DR Prov.\ + May need manual sync steps.\ + The Bus Controller view of a resource does not match what was found on DR Prov + +MM_CIRCULAR_REF=\ + 503|\ + Trying to add edge from source {0} into Map belonging to {1}|\ + May indicate a provisioning error.|\ + Some error in logic is attempting to add an edge to a Map that is an edge. + +TOPIC_CREATE_ERROR=\ + 504|\ + Unable to create topic for {0} err={1} fields={2} msg={3}|\ + No comment.|\ + Reporting an error caught while creating a topic + +INGRESS_CREATE_ERROR=\ + 505|\ + rc={0} unable to create ingress rule for {1} on feed {2} to {3}|\ + No comment.|\ + Unexpected response while creating ingress rule + +FEED_PUB_PROV_ERROR=\ + 506|\ + For feed {0} resulting set of publishers do not match requested set of publishers {1} vs {2}|\ + No comment.|\ + The number of publishers on a feed do not match after provisioning request. + +FEED_SUB_PROV_ERROR=\ + 507|\ + For feed {0} i={1} url={2} err={3}|\ + No comment.|\ + An error occurred when provisioning subs on a feed. + +MM_PUBLISH_ERROR=\ + 508|\ + Unable to publish {0} provisioning message. rc={1} msg={2}|\ + No comment.|\ + An error occurred when publishing a message to MM + +EGRESS_CREATE_ERROR=\ + 509|\ + rc={0} unable to create egress rule for {1} on feed {2} to {3}|\ + No comment.|\ + Unexpected response while creating egress rule + +# 900 Unknown Errors +UNEXPECTED_CONDITION=\ + 901|\ + Unexpected exception encountered {0}|\ + No resolution|\ + An error to catch unexpected conditions. Hopefully a clue in the stack trace. diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ApiError.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ApiError.java new file mode 100644 index 0000000..c67e55b --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ApiError.java @@ -0,0 +1,92 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; +import java.util.Objects; + +@XmlRootElement +public class ApiError implements Serializable { + private int code; + private String message; + private String fields; + + public ApiError() { + this(0, null, null); + } + + public ApiError(int code, String message) { + this(code, message, null); + } + + public ApiError(int code, String message, String fields) { + this.code = code; + this.message = message; + this.fields = fields; + } + + public int getCode() { + return code; + } + public void setCode(int rc) { + this.code = rc; + } + public String getMessage() { + return message; + } + public void setMessage(String message) { + this.message = message; + } + public String getFields() { + return fields; + } + public void setFields(String fields) { + this.fields = fields; + } + public String toString() { + return String.format( "code=%d msg=%s fields=%s", this.code, this.message, this.fields ); + } + public boolean is2xx() { + + return code >= 200 && code < 300; + } + public void reset() { + code = 0; + message = null; + fields = null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ApiError apiError = (ApiError) o; + return code == apiError.code && + Objects.equals(message, apiError.message) && + Objects.equals(fields, apiError.fields); + } + + @Override + public int hashCode() { + return Objects.hash(code, message, fields); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/BrTopic.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/BrTopic.java new file mode 100644 index 0000000..ba050c3 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/BrTopic.java @@ -0,0 +1,71 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public class BrTopic { + + private String brSource; + private String brTarget; + private String mmAgentName; + private int topicCount; + + // no-op constructor used by framework + public BrTopic() { + } + + public String getBrSource() { + return brSource; + } + + public void setBrSource(String brSource) { + this.brSource = brSource; + } + + public String getBrTarget() { + return brTarget; + } + + public void setBrTarget(String brTarget) { + this.brTarget = brTarget; + } + + public int getTopicCount() { + return topicCount; + } + + public void setTopicCount(int topicCount) { + this.topicCount = topicCount; + } + + public String getMmAgentName() { + return mmAgentName; + } + + public void setMmAgentName(String mmAgentName) { + this.mmAgentName = mmAgentName; + } + + + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Node.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Node.java new file mode 100644 index 0000000..4b2ef90 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Node.java @@ -0,0 +1,94 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import javax.xml.bind.annotation.XmlRootElement; +import java.util.Objects; + +@XmlRootElement +public class DR_Node extends DmaapObject { + private String fqdn; + private String dcaeLocationName; + private String hostName; + private String version; + + public DR_Node() { + + } + + public DR_Node( String f, + String dLN, + String hN, + String v ) { + this.fqdn = f; + this.dcaeLocationName = dLN; + this.hostName = hN; + this.version = v; + } + + public String getFqdn() { + return fqdn; + } + + public void setFqdn(String fqdn) { + this.fqdn = fqdn; + } + + public String getDcaeLocationName() { + return dcaeLocationName; + } + + public void setDcaeLocationName(String dcaeLocationName) { + this.dcaeLocationName = dcaeLocationName; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DR_Node dr_node = (DR_Node) o; + return Objects.equals(fqdn, dr_node.fqdn) && + Objects.equals(dcaeLocationName, dr_node.dcaeLocationName) && + Objects.equals(hostName, dr_node.hostName) && + Objects.equals(version, dr_node.version); + } + + @Override + public int hashCode() { + return Objects.hash(fqdn, dcaeLocationName, hostName, version); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Pub.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Pub.java new file mode 100644 index 0000000..4e64089 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Pub.java @@ -0,0 +1,187 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import javax.xml.bind.annotation.XmlRootElement; + +import org.onap.dmaap.dbcapi.util.RandomString; + +import java.util.Objects; + +@XmlRootElement +public class DR_Pub extends DmaapObject { + + private String dcaeLocationName; + private String username; + private String userpwd; + private String feedId; + private String pubId; + + // NOTE: the following fields are optional in the API but not stored in the DB + private String feedName; + private String feedVersion; + + + public DR_Pub() { + status = DmaapObject_Status.EMPTY; + + } + + public DR_Pub( String dLN ) { + this.dcaeLocationName = dLN; + this.status = DmaapObject_Status.STAGED; + } + + public DR_Pub( String dLN, + String uN, + String uP, + String fI, + String pI ) { + this.dcaeLocationName = dLN; + this.username = uN; + this.userpwd = uP; + this.feedId = fI; + this.pubId = pI; + this.status = DmaapObject_Status.VALID; + } + + + public DR_Pub( String dLN, + String uN, + String uP, + String fI ) { + this.dcaeLocationName = dLN; + this.username = uN; + this.userpwd = uP; + this.feedId = fI; + this.pubId = fI + "." + DR_Pub.nextKey(); + this.status = DmaapObject_Status.VALID; + } + + + public String getDcaeLocationName() { + return dcaeLocationName; + } + + public void setDcaeLocationName(String dcaeLocationName) { + this.dcaeLocationName = dcaeLocationName; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getUserpwd() { + return userpwd; + } + + public void setUserpwd(String userpwd) { + this.userpwd = userpwd; + } + + public String getFeedId() { + return feedId; + } + + public void setFeedId(String feedId) { + this.feedId = feedId; + } + + public String getPubId() { + return pubId; + } + + public void setPubId(String pubId) { + this.pubId = pubId; + } + + public void setNextPubId() { + this.pubId = this.feedId + "." + DR_Pub.nextKey(); + } + + public String getFeedName() { + return feedName; + } + + public void setFeedName(String feedName) { + this.feedName = feedName; + } + + public String getFeedVersion() { + return feedVersion; + } + + public void setFeedVersion(String feedVersion) { + this.feedVersion = feedVersion; + } + + public DR_Pub setRandomUserName() { + RandomString r = new RandomString(15); + this.username = "tmp_" + r.nextString(); + return this; + } + public DR_Pub setRandomPassword() { + RandomString r = new RandomString(15); + this.userpwd = r.nextString(); + return this; + } + + public static String nextKey() { + RandomString ri = new RandomString(5); + return ri.nextString(); + + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DR_Pub dr_pub = (DR_Pub) o; + return Objects.equals(dcaeLocationName, dr_pub.dcaeLocationName) && + Objects.equals(username, dr_pub.username) && + Objects.equals(userpwd, dr_pub.userpwd) && + Objects.equals(feedId, dr_pub.feedId) && + Objects.equals(pubId, dr_pub.pubId); + } + + @Override + public int hashCode() { + + return Objects.hash(dcaeLocationName, username, userpwd, feedId, pubId); + } + + @Override + public String toString() { + return "DR_Pub{" + + "dcaeLocationName='" + dcaeLocationName + '\'' + + ", username='" + username + '\'' + + ", userpwd='" + userpwd + '\'' + + ", feedId='" + feedId + '\'' + + ", pubId='" + pubId + '\'' + + ", feedName='" + feedName + '\'' + + ", feedVersion='" + feedVersion + '\'' + + '}'; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Sub.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Sub.java new file mode 100644 index 0000000..90da956 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Sub.java @@ -0,0 +1,391 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import java.nio.charset.StandardCharsets; + +import javax.xml.bind.annotation.XmlRootElement; + +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; + +@XmlRootElement +public class DR_Sub extends DmaapObject { + + private String dcaeLocationName; + private String username; + private String userpwd; + private String feedId; + private String deliveryURL; + private String logURL; + private String subId; + private boolean use100; + private boolean suspended; + private String owner; + private boolean guaranteedDelivery; + private boolean guaranteedSequence; + private boolean privilegedSubscriber; + private boolean decompress; + + // NOTE: the following fields are optional in the API but not stored in the DB + + private String feedName; + private String feedVersion; + public DR_Sub() { + + } + + public DR_Sub( String dLN, + String uN, + String uP, + String fI, + String dU, + String lU, + boolean u100 ) { + this.dcaeLocationName = dLN; + this.username = uN; + this.userpwd = uP; + this.feedId = fI; + this.deliveryURL = dU; + this.logURL = lU; + this.use100 = u100; + this.setStatus( DmaapObject_Status.NEW ); + this.subId = "0"; + } + + public DR_Sub ( String json ) { + logger.info( "DR_Sub:" + json ); + JSONParser parser = new JSONParser(); + JSONObject jsonObj; + + try { + jsonObj = (JSONObject) parser.parse( json ); + } catch ( ParseException pe ) { + errorLogger.error( DmaapbcLogMessageEnum.JSON_PARSING_ERROR, "DR_Sub", json ); + this.setStatus( DmaapObject_Status.INVALID ); + return; + } + + this.setOwner( (String) jsonObj.get("subscriber")); + this.setSuspended( (boolean) jsonObj.get("suspend")); + + try { + JSONObject links = (JSONObject) jsonObj.get("links"); + String url = (String) links.get("feed"); + this.setFeedId( url.substring( url.lastIndexOf('/')+1, url.length() )); + url = (String) links.get("self"); + this.setSubId( url.substring( url.lastIndexOf('/')+1, url.length() )); + logger.info( "feedid="+ this.getFeedId() ); + this.setLogURL( (String) links.get("log") ); + } catch (NullPointerException npe ) { + + } + try { + this.setGuaranteedDelivery( (boolean) jsonObj.get("guaranteed_delivery")); + } catch( NullPointerException npe ) { + this.setGuaranteedDelivery(false); + } + try { + this.setGuaranteedSequence( (boolean) jsonObj.get("guaranteed_sequence")); + } catch( NullPointerException npe ) { + this.setGuaranteedSequence(false); + } + try { + this.setPrivilegedSubscriber((boolean) jsonObj.get("privilegedSubscriber")); + } catch( NullPointerException npe ) { + this.setPrivilegedSubscriber(false); + } + try { + this.setDecompress((boolean) jsonObj.get("decompress")); + } catch( NullPointerException npe ) { + this.setDecompress(false); + } + + JSONObject del = (JSONObject) jsonObj.get("delivery"); + this.setDeliveryURL( (String) del.get("url") ); + this.setUsername( (String) del.get("user")); + this.setUserpwd( (String) del.get( "password")); + this.setUse100((boolean) del.get( "use100")); + + this.setStatus( DmaapObject_Status.VALID ); + + logger.info( "new DR_Sub returning"); + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public boolean isSuspended() { + return suspended; + } + + public void setSuspended(boolean suspended) { + this.suspended = suspended; + } + + + + public boolean isUse100() { + return use100; + } + + public void setUse100(boolean use100) { + this.use100 = use100; + } + + public String getDcaeLocationName() { + return dcaeLocationName; + } + + public void setDcaeLocationName(String dcaeLocationName) { + this.dcaeLocationName = dcaeLocationName; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getUserpwd() { + return userpwd; + } + + public void setUserpwd(String userpwd) { + this.userpwd = userpwd; + } + + public String getFeedId() { + return feedId; + } + + public void setFeedId(String feedId) { + this.feedId = feedId; + } + + public String getDeliveryURL() { + return deliveryURL; + } + + public void setDeliveryURL(String deliveryURL) { + this.deliveryURL = deliveryURL; + } + + public String getLogURL() { + return logURL; + } + + public void setLogURL(String logURL) { + this.logURL = logURL; + } + + public String getSubId() { + return subId; + } + + public void setSubId(String subId) { + this.subId = subId; + } + + + public boolean isGuaranteedDelivery() { + return guaranteedDelivery; + } + + public void setGuaranteedDelivery(boolean guaranteedDelivery) { + this.guaranteedDelivery = guaranteedDelivery; + } + + public boolean isGuaranteedSequence() { + return guaranteedSequence; + } + + public void setGuaranteedSequence(boolean guaranteedSequence) { + this.guaranteedSequence = guaranteedSequence; + } + + public boolean isPrivilegedSubscriber() { + return privilegedSubscriber; + } + + public void setPrivilegedSubscriber(boolean privilegedSubscriber) { + this.privilegedSubscriber = privilegedSubscriber; + } + + public boolean isDecompress() { + return decompress; + } + + public void setDecompress(boolean decompressData) { + this.decompress = decompressData; + } + + public String getFeedName() { + return feedName; + } + + public void setFeedName(String feedName) { + this.feedName = feedName; + } + + public String getFeedVersion() { + return feedVersion; + } + + public void setFeedVersion(String feedVersion) { + this.feedVersion = feedVersion; + } + + public byte[] getBytes(String provApi) { + if ( "AT&T".equals(provApi)) { + return toProvJSONforATT().getBytes(StandardCharsets.UTF_8); + } + return toProvJSON().getBytes(StandardCharsets.UTF_8); + } + // returns the DR_Sub object in JSON that conforms to ONAP DR Prov Server expectations + public String toProvJSON() { + // this is the original DR API that was contributed to ONAP + String postJSON = String.format("{\"suspend\": %s, \"delivery\":" + + "{\"url\": \"%s\", \"user\": \"%s\", \"password\": \"%s\", \"use100\": %s }" + + ", \"metadataOnly\": %s, \"groupid\": \"%s\", \"follow_redirect\": %s " + + ", \"privilegedSubscriber\": %s, \"decompress\": %s " + + "}" + ,this.suspended + ,this.getDeliveryURL() + ,this.getUsername() + ,this.getUserpwd() + ,this.isUse100() + ,"false" + ,"0" + ,"true" + ,this.isPrivilegedSubscriber() + ,this.isDecompress() + ); + + logger.info( postJSON ); + return postJSON; + } + // returns the DR_Sub object in JSON that conforms to AT&T DR Prov Server expectations + // In Jan, 2019, the DR API used internally at AT&T diverged, so this function can be used in + // that runtime environment + public String toProvJSONforATT() { + // in DR 3.0, API v2.1 a new groupid field is added. We are not using this required field so just set it to 0. + // we send this regardless of DR Release because older versions of DR seem to safely ignore it + // and soon those versions won't be around anyway... + // Similarly, in the 1704 Release, a new subscriber attribute "follow_redirect" was introduced. + // We are setting it to "true" because that is the general behavior desired in OpenDCAE. + // But it is really a no-op for OpenDCAE because we've deployed DR with the SYSTEM-level parameter for FOLLOW_REDIRECTS set to true. + // In the event we abandon that, then setting the sub attribute to true will be a good thing. + // Update Jan, 2019: added guaranteed_delivery and guaranteed_sequence with value false for + // backwards compatibility + // TODO: + // - introduce Bus Controller API support for these attributes + // - store the default values in the DB + String postJSON = String.format("{\"suspend\": %s, \"delivery\":" + + "{\"url\": \"%s\", \"user\": \"%s\", \"password\": \"%s\", \"use100\": %s}" + + ", \"metadataOnly\": %s, \"groupid\": \"%s\", \"follow_redirect\": %s " + + ", \"guaranteed_delivery\": %s, \"guaranteed_sequence\": %s" + + "}" + ,this.suspended + ,this.getDeliveryURL() + ,this.getUsername() + ,this.getUserpwd() + ,this.isUse100() + ,"false" + ,"0" + ,"true" + ,this.isGuaranteedDelivery() + ,this.isGuaranteedSequence() + ); + + logger.info( postJSON ); + return postJSON; + } + + @Override + public String toString() { + return String.format ( "DR_Sub: {dcaeLocationName=%s username=%s userpwd=%s feedId=%s deliveryURL=%s logURL=%s subid=%s use100=%s suspended=%s owner=%s}", + dcaeLocationName, + username, + userpwd, + feedId, + deliveryURL, + logURL, + subId, + use100, + suspended, + owner + ); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DR_Sub drSub = (DR_Sub) o; + + if (use100 != drSub.use100) { + return false; + } + if (suspended != drSub.suspended) { + return false; + } + if (!dcaeLocationName.equals(drSub.dcaeLocationName)) { + return false; + } + if (!username.equals(drSub.username)) { + return false; + } + if (!userpwd.equals(drSub.userpwd)) { + return false; + } + if (!feedId.equals(drSub.feedId)) { + return false; + } + return subId.equals(drSub.subId); + } + + @Override + public int hashCode() { + int result = dcaeLocationName.hashCode(); + result = 31 * result + username.hashCode(); + result = 31 * result + userpwd.hashCode(); + result = 31 * result + feedId.hashCode(); + result = 31 * result + subId.hashCode(); + result = 31 * result + (use100 ? 1 : 0); + result = 31 * result + (suspended ? 1 : 0); + return result; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DcaeLocation.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DcaeLocation.java new file mode 100644 index 0000000..f459c6c --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DcaeLocation.java @@ -0,0 +1,119 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import javax.xml.bind.annotation.XmlRootElement; + +import java.util.Objects; + +@XmlRootElement +public class DcaeLocation extends DmaapObject { + private String clli; + private String dcaeLayer; + private String dcaeLocationName; + private String openStackAvailabilityZone; + private String subnet; + + + + public DcaeLocation() { + + } + + public DcaeLocation( String c, + String dL, + String dLN, + String oSAZ, + String s ) { + + this.clli = c; + this.dcaeLayer = dL; + this.dcaeLocationName = dLN; + this.openStackAvailabilityZone = oSAZ; + this.subnet = s; + } + + public String getClli() { + return clli; + } + + public void setClli(String clli) { + this.clli = clli; + } + + public String getDcaeLayer() { + return dcaeLayer; + } + + public void setDcaeLayer(String dcaeLayer) { + this.dcaeLayer = dcaeLayer; + } + public boolean isCentral() { + return dcaeLayer != null && dcaeLayer.contains("central"); + } + public boolean isLocal() { + return dcaeLayer != null && dcaeLayer.contains("local"); + } + + public String getDcaeLocationName() { + return dcaeLocationName; + } + + public void setDcaeLocationName(String dcaeLocationName) { + this.dcaeLocationName = dcaeLocationName; + } + + + + public String getOpenStackAvailabilityZone() { + return openStackAvailabilityZone; + } + + public void setOpenStackAvailabilityZone(String openStackAvailabilityZone) { + this.openStackAvailabilityZone = openStackAvailabilityZone; + } + + public String getSubnet() { + return subnet; + } + + public void setSubnet(String subnet) { + this.subnet = subnet; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DcaeLocation that = (DcaeLocation) o; + return Objects.equals(clli, that.clli) && + Objects.equals(dcaeLayer, that.dcaeLayer) && + Objects.equals(dcaeLocationName, that.dcaeLocationName) && + Objects.equals(openStackAvailabilityZone, that.openStackAvailabilityZone) && + Objects.equals(subnet, that.subnet); + } + + @Override + public int hashCode() { + + return Objects.hash(clli, dcaeLayer, dcaeLocationName, openStackAvailabilityZone, subnet); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Dmaap.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Dmaap.java new file mode 100644 index 0000000..96248f3 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Dmaap.java @@ -0,0 +1,177 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public class Dmaap extends DmaapObject { + + private String version; + private String topicNsRoot; + private String dmaapName; + private String drProvUrl; + private String bridgeAdminTopic; + private String loggingUrl; + private String nodeKey; + private String accessKeyOwner; + + + // no-op constructor used by framework + public Dmaap() { + + } + + public Dmaap( DmaapBuilder builder ) { + this.version = builder.ver; + this.topicNsRoot = builder.tnr; + this.dmaapName = builder.dn; + this.drProvUrl = builder.dpu; + this.bridgeAdminTopic = builder.bat; + this.loggingUrl = builder.lu; + this.nodeKey = builder.nk; + this.accessKeyOwner = builder.ako; + this.setStatus( DmaapObject_Status.NEW ); + + } + + public static class DmaapBuilder { + private String ver; + private String tnr; + private String dn; + private String dpu; + private String lu; + private String bat; + private String nk; + private String ako; + + public DmaapBuilder setVer(String ver) { + this.ver = ver; + return this; + } + + public DmaapBuilder setTnr(String tnr) { + this.tnr = tnr; + return this; + } + + public DmaapBuilder setDn(String dn) { + this.dn = dn; + return this; + } + + public DmaapBuilder setDpu(String dpu) { + this.dpu = dpu; + return this; + } + + public DmaapBuilder setLu(String lu) { + this.lu = lu; + return this; + } + + public DmaapBuilder setBat(String bat) { + this.bat = bat; + return this; + } + + public DmaapBuilder setNk(String nk) { + this.nk = nk; + return this; + } + + public DmaapBuilder setAko(String ako) { + this.ako = ako; + return this; + } + + public Dmaap createDmaap() { + return new Dmaap(this); + } + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getTopicNsRoot() { + return topicNsRoot; + } + + public void setTopicNsRoot(String topicNsRoot) { + this.topicNsRoot = topicNsRoot; + } + + public String getDmaapName() { + return dmaapName; + } + + public void setDmaapName(String dmaapName) { + this.dmaapName = dmaapName; + } + + public String getDrProvUrl() { + return drProvUrl; + } + + public void setDrProvUrl(String drProvUrl) { + this.drProvUrl = drProvUrl; + } + + + public String getNodeKey() { + return nodeKey; + } + + public void setNodeKey(String nodeKey) { + this.nodeKey = nodeKey; + } + + public String getAccessKeyOwner() { + return accessKeyOwner; + } + + public void setAccessKeyOwner(String accessKeyOwner) { + this.accessKeyOwner = accessKeyOwner; + } + + + public String getBridgeAdminTopic() { + return bridgeAdminTopic; + } + + public void setBridgeAdminTopic(String bridgeAdminTopic) { + this.bridgeAdminTopic = bridgeAdminTopic; + } + + public String getLoggingUrl() { + return loggingUrl; + } + + public void setLoggingUrl(String loggingUrl) { + this.loggingUrl = loggingUrl; + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DmaapObject.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DmaapObject.java new file mode 100644 index 0000000..4ff2eec --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DmaapObject.java @@ -0,0 +1,144 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import io.swagger.annotations.ApiModelProperty; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; +import javax.xml.bind.annotation.XmlRootElement; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; + +@XmlRootElement +public abstract class DmaapObject extends BaseLoggingClass { + @ApiModelProperty( value = "datestamp for last update to this object") + protected Date lastMod; + protected DmaapObject_Status status; + + public Date getLastMod() { + return lastMod; + } + + public void setLastMod(Date lastMod) { + this.lastMod = lastMod; + } + + public void setLastMod() { + this.lastMod = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTime(); + } + + public enum DmaapObject_Status { + EMPTY, + NEW, + STAGED, + VALID, + INVALID, + DELETED + } + public DmaapObject_Status getStatus() { + return status; + } + + public void setStatus(DmaapObject_Status status) { + this.status = status; + } + + public void setStatus( String val ) { + if ( val == null || val.isEmpty() ) { + this.status = DmaapObject_Status.EMPTY; + } else if (val.compareToIgnoreCase("new") == 0 ) { + this.status = DmaapObject_Status.NEW; + } else if ( val.compareToIgnoreCase("staged" ) == 0) { + this.status = DmaapObject_Status.STAGED; + } else if ( val.compareToIgnoreCase("valid") == 0) { + this.status = DmaapObject_Status.VALID; + } else if ( val.compareToIgnoreCase("invalid") == 0) { + this.status = DmaapObject_Status.INVALID; + } else if ( val.compareToIgnoreCase("deleted") == 0) { + this.status = DmaapObject_Status.DELETED; + } else { + this.status = DmaapObject_Status.INVALID; + } + } + + @ApiModelProperty( hidden=true ) + public boolean isStatusValid() { + if ( this.status == DmaapObject_Status.VALID ) { + return true; + } + return false; + } + + /* + * TODO: get this working so arrays and sub-class within an Object can be logged + * + public String toString() { + return classToString( this ); + } + + private String classToString( Object obj ) { + Field[] fields = obj.getClass().getDeclaredFields(); + StringBuilder res = new StringBuilder( "{"); + boolean first = true; + for ( Field field: fields ) { + logger.info( field.getName() + " toString=" + field.toString() + " toGenericString=" + field.toGenericString()); + if ( first ) { + first = false; + } else { + res.append( ", "); + } + + + field.setAccessible(true); // avoid IllegalAccessException + + + Class<?> t = field.getType(); + + if ( t == String.class ) { + res.append( "\"" ).append( field.getName() ).append( "\": \""); + + try { + res.append(field.get(this)); + } catch ( IllegalAccessException iae) { + res.append( "UNK(iae)"); + } catch (IllegalArgumentException iae2 ) { + res.append( "UNK(iae2)"); + } catch ( NullPointerException npe ) { + res.append( "UNK(npe)"); + } catch ( ExceptionInInitializerError eie ) { + res.append( "UNK(eie)"); + } + res.append( "\""); + } else if ( t == ArrayList.class ){ + res.append( "["); + res.append( classToString( field )); + res.append( "]"); + + } + } + res.append( "}"); + return( res.toString()); + + + } + */ + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Feed.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Feed.java new file mode 100644 index 0000000..b3c7332 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Feed.java @@ -0,0 +1,294 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.model; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; + +import javax.xml.bind.annotation.XmlRootElement; + +import org.json.simple.*; +import org.json.simple.parser.*; +import org.onap.dmaap.dbcapi.service.DmaapService; + +@XmlRootElement +public class Feed extends DmaapObject { + + private String feedId; + + private String feedName; + private String feedVersion; + private String feedDescription; + private String owner; + private String asprClassification; + private String publishURL; + private String subscribeURL; + private boolean suspended; + private String logURL; + private String formatUuid; + + private ArrayList<DR_Pub> pubs; + private ArrayList<DR_Sub> subs; + + + public Feed() { + this.pubs = new ArrayList<>(); + this.subs = new ArrayList<>(); + this.setStatus( DmaapObject_Status.EMPTY ); + + } + + public Feed( String name, + String version, + String description, + String owner, + String aspr) { + this.feedName = name; + this.feedVersion = version; + this.feedDescription = description; + this.owner = owner; + this.asprClassification = aspr; + this.pubs = new ArrayList<>(); + this.subs = new ArrayList<>(); + this.setStatus( DmaapObject_Status.NEW ); + + } + + // expects a String in JSON format, with known fields to populate Feed object + public Feed ( String json ) { + JSONParser parser = new JSONParser(); + JSONObject jsonObj; + try { + jsonObj = (JSONObject) parser.parse( json ); + } catch ( ParseException pe ) { + logger.error( "Error parsing provisioning data: " + json ); + this.setStatus( DmaapObject_Status.INVALID ); + return; + } + this.setFeedName( (String) jsonObj.get("name")); + + this.setFeedVersion( (String) jsonObj.get("version")); + this.setFeedDescription( (String) jsonObj.get("description")); + this.setOwner( (String) jsonObj.get("publisher")); + + this.setSuspended( (boolean) jsonObj.get("suspend")); + JSONObject links = (JSONObject) jsonObj.get("links"); + String url = (String) links.get("publish"); + this.setPublishURL( url ); + this.setFeedId( url.substring( url.lastIndexOf('/')+1, url.length() )); + logger.info( "feedid="+ this.getFeedId() ); + this.setSubscribeURL( (String) links.get("subscribe") ); + this.setLogURL( (String) links.get("log") ); + JSONObject auth = (JSONObject) jsonObj.get("authorization"); + this.setAsprClassification( (String) auth.get("classification")); + JSONArray pubs = (JSONArray) auth.get( "endpoint_ids"); + int i; + ArrayList<DR_Pub> dr_pub = new ArrayList<>(); + this.subs = new ArrayList<>(); + + for( i = 0; i < pubs.size(); i++ ) { + JSONObject entry = (JSONObject) pubs.get(i); + dr_pub.add( new DR_Pub( "someLocation", + (String) entry.get("id"), + (String) entry.get("password"), + this.getFeedId(), + this.getFeedId() + "." + DR_Pub.nextKey() )); + + } + this.setPubs( dr_pub ); + + this.setStatus( DmaapObject_Status.VALID ); + + } + + + + public boolean isSuspended() { + return suspended; + } + + public void setSuspended(boolean suspended) { + this.suspended = suspended; + } + + public String getSubscribeURL() { + return subscribeURL; + } + + public void setSubscribeURL(String subscribeURL) { + this.subscribeURL = subscribeURL; + } + + public String getFeedId() { + return feedId; + } + + public void setFeedId(String feedId) { + this.feedId = feedId; + } + + public String getFeedName() { + return feedName; + } + + public void setFeedName(String feedName) { + this.feedName = feedName; + } + + public String getFeedVersion() { + return feedVersion; + } + + public void setFeedVersion(String feedVersion) { + this.feedVersion = feedVersion; + } + + public String getFeedDescription() { + return feedDescription; + } + + public void setFeedDescription(String feedDescription) { + this.feedDescription = feedDescription; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getAsprClassification() { + return asprClassification; + } + + public void setAsprClassification(String asprClassification) { + this.asprClassification = asprClassification; + } + + public String getPublishURL() { + return publishURL; + } + + public void setPublishURL(String publishURL) { + this.publishURL = publishURL; + } + + public String getLogURL() { + return logURL; + } + + public void setLogURL(String logURL) { + this.logURL = logURL; + } + + + + public String getFormatUuid() { + return formatUuid; + } + + public void setFormatUuid(String formatUuid) { + this.formatUuid = formatUuid; + } + + // returns the Feed object in JSON that conforms to DR Prov Server expectations + public String toProvJSON() { + + String postJSON = String.format("{\"name\": \"%s\", \"version\": \"%s\", \"description\": \"%s\", \"suspend\": %s, \"authorization\": { \"classification\": \"%s\", ", + this.getFeedName(), + this.getFeedVersion(), + this.getFeedDescription(), + this.isSuspended() , + this.getAsprClassification() + ); + int i; + postJSON += "\"endpoint_addrs\": [],\"endpoint_ids\": ["; + String comma = ""; + for( i = 0 ; i < pubs.size(); i++) { + postJSON += String.format(" %s{\"id\": \"%s\",\"password\": \"%s\"}", + comma, + pubs.get(i).getUsername(), + pubs.get(i).getUserpwd() + ) ; + comma = ","; + } + postJSON += "]}}"; + + logger.info( "postJSON=" + postJSON); + return postJSON; + } + + public ArrayList<DR_Pub> getPubs() { + return pubs; + } + + public void setPubs( ArrayList<DR_Pub> pubs) { + this.pubs = pubs; + } + + public ArrayList<DR_Sub> getSubs() { + return subs; + } + + public void setSubs( ArrayList<DR_Sub> subs) { + this.subs = subs; + } + + public byte[] getBytes() { + return toProvJSON().getBytes(StandardCharsets.UTF_8); + } + + public static String getSubProvURL( String feedId ) { + return new DmaapService().getDmaap().getDrProvUrl() + "/subscribe/" + feedId; + } + + @Override + public String toString() { + String rc = String.format ( "Feed: {feedId=%s feedName=%s feedVersion=%s feedDescription=%s owner=%s asprClassification=%s publishURL=%s subscriberURL=%s suspended=%s logURL=%s formatUuid=%s}", + feedId, + feedName, + feedVersion, + feedDescription, + owner, + asprClassification, + publishURL, + subscribeURL, + suspended, + logURL, + formatUuid + + + ); + + for( DR_Pub pub: pubs) { + rc += "\n" + pub.toString(); + } + + for( DR_Sub sub: subs ) { + rc += "\n" + sub.toString(); + } + return rc; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/FqtnType.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/FqtnType.java new file mode 100644 index 0000000..697e9b8 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/FqtnType.java @@ -0,0 +1,68 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import java.util.HashMap; +import java.util.Map; + +import javax.xml.bind.annotation.XmlRootElement; + + +@XmlRootElement +public enum FqtnType { + FQTN_NOT_SPECIFIED(0), + FQTN_LEGACY_FORMAT(1), + FQTN_PROJECTID_FORMAT(2), + FQTN_PROJECTID_VERSION_FORMAT(3); + + + private int value; + private static Map map = new HashMap<>(); + + private FqtnType(int value) { + this.value = value; + } + + static { + for (FqtnType repType : FqtnType.values()) { + map.put(repType.value, repType); + } + } + + public static FqtnType valueOf(int repType) { + return (FqtnType) map.get(repType); + } + + public int getValue() { + return value; + } + + static public FqtnType Validator( String input ){ + + FqtnType t; + try { + t = FqtnType.valueOf( input ); + } catch ( IllegalArgumentException e ) { + t = FQTN_NOT_SPECIFIED; + } + return t; + } + +}
\ No newline at end of file diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Client.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Client.java new file mode 100644 index 0000000..0631f07 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Client.java @@ -0,0 +1,162 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import java.util.Date; + +import javax.xml.bind.annotation.XmlRootElement; + +import org.onap.dmaap.dbcapi.database.DatabaseClass; + +import io.swagger.annotations.ApiModelProperty; + +@XmlRootElement +public class MR_Client extends DmaapObject { + + @ApiModelProperty( value="a tag indicating a logical deployment site") + private String dcaeLocationName; + @ApiModelProperty( value="the URL for a MR instance - typically in the same dcaeLocation - that this client should use to access the topic") + private String topicURL; + @ApiModelProperty( value="Fully Qualified Topic Name constructed by dbcapi") + private String fqtn; + @ApiModelProperty( value="an AAF Role to be granted an appropriate Permission. If specified, takes precedence over clientIdentity, for backwards compatibility.") + private String clientRole; + @ApiModelProperty( value="one or more actions from the set (\"pub\", \"sub\", \"view\") for which this client needs Permission") + private String[] action; + @ApiModelProperty( value="a unique identifier generated by dbcapi for this client") + private String mrClientId; + @ApiModelProperty( value="an AAF identity to be associated to an appropriate topic Role") + private String clientIdentity; + + + public MR_Client() { + this.mrClientId = DatabaseClass.getNextClientId(); + this.lastMod = new Date(); + this.setLastMod(); + debugLogger.debug( "MR_Client constructor " + this.lastMod ); + + } + + public MR_Client( String dLN, + String f, + String cR, + String[] a ) { + this.dcaeLocationName = dLN; + this.fqtn = f; + this.clientRole = cR; + int i = 0; + + if (a != null) { + this.action = new String[a.length]; + for (String aa : a) { + this.action[i++] = new String(aa); + } + } + this.setStatus( DmaapObject_Status.NEW ); + this.mrClientId = DatabaseClass.getNextClientId(); + this.setLastMod(); + debugLogger.debug( "MR_Client constructor w initialization " + this.lastMod ); + } + + public String getDcaeLocationName() { + return dcaeLocationName; + } + + public void setDcaeLocationName(String dcaeLocationName) { + this.dcaeLocationName = dcaeLocationName; + } + + public String getFqtn() { + return fqtn; + } + + public void setFqtn(String fqtn) { + this.fqtn = fqtn; + } + + public String getClientRole() { + return clientRole; + } + + public void setClientRole(String clientRole) { + this.clientRole = clientRole; + } + + public String[] getAction() { + return action; + } + + public void setAction(String[] action) { + this.action = action; + } + + @ApiModelProperty( hidden=true ) + public boolean isPublisher() { + return hasAction( "pub"); + } + @ApiModelProperty( hidden=true ) + public boolean isSubscriber() { + return hasAction( "sub"); + } + + public boolean hasAction( String val ) { + for (String s: this.action) { + if ( s!= null && s.equals(val)) { + return true; + } + } + return false; + } + public String getMrClientId() { + return mrClientId; + } + + public void setMrClientId(String mrClientId) { + this.mrClientId = mrClientId; + } + + + + public String getTopicURL() { + return topicURL; + } + + public void setTopicURL(String topicURL) { + this.topicURL = topicURL; + } + + public String getClientIdentity() { + return clientIdentity; + } + + public void setClientIdentity(String clientIdentity) { + this.clientIdentity = clientIdentity; + } + public boolean hasClientIdentity() { + if ( this.clientIdentity == null || this.clientIdentity.isEmpty() ) { + return false; + } else { + return true; + } + } + + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Cluster.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Cluster.java new file mode 100644 index 0000000..06b6194 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Cluster.java @@ -0,0 +1,235 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import javax.xml.bind.annotation.XmlRootElement; + +import org.onap.dmaap.dbcapi.util.DmaapConfig; +import org.onap.dmaap.dbcapi.util.DmaapTimestamp; + + + +@XmlRootElement +public class MR_Cluster extends DmaapObject { + + private String dcaeLocationName; + private String fqdn; + private DmaapTimestamp lastMod; + private String topicProtocol; + private String topicPort; + private String replicationGroup; + private String sourceReplicationPort; + private String targetReplicationPort; + + + // TODO: make this a system property + private static String defaultTopicProtocol; + private static String defaultTopicPort; + private static String defaultReplicationGroup; + private static String defaultSourceReplicationPort; + private static String defaultTargetReplicationPort; + + private static void setDefaults() { + /* boolean been_here = false; + if ( been_here ) { + return; + } */ + DmaapConfig dc = (DmaapConfig)DmaapConfig.getConfig(); + defaultTopicProtocol = dc.getProperty("MR.TopicProtocol", "https"); + defaultTopicPort = dc.getProperty( "MR.TopicPort", "3905"); + defaultReplicationGroup = dc.getProperty( "MR.ReplicationGroup", "" ); + defaultSourceReplicationPort = dc.getProperty( "MR.SourceReplicationPort", "2181"); + defaultTargetReplicationPort = dc.getProperty( "MR.TargetReplicationPort", "9092"); + // been_here = true; + } + + + public MR_Cluster() { + setDefaults(); + this.topicProtocol = defaultTopicProtocol; + this.topicPort = defaultTopicPort; + this.replicationGroup = null; + this.sourceReplicationPort = defaultSourceReplicationPort; + this.targetReplicationPort = defaultTargetReplicationPort; + this.lastMod = new DmaapTimestamp(); + this.lastMod.mark(); + + debugLogger.debug( "MR_Cluster constructor " + this.lastMod ); + + } + + // new style constructor + public MR_Cluster( String dLN, + String f, + String prot, + String port) { + setDefaults(); + this.dcaeLocationName = dLN; + this.fqdn = f; + + if ( prot == null || prot.isEmpty() ) { + this.topicProtocol = defaultTopicProtocol; + } else { + this.topicProtocol = prot; + } + if ( port == null || port.isEmpty() ) { + this.topicPort = defaultTopicPort; + } else { + this.topicPort = port; + } + + this.replicationGroup = defaultReplicationGroup; + this.sourceReplicationPort = defaultSourceReplicationPort; + this.targetReplicationPort = defaultTargetReplicationPort; + + this.lastMod = new DmaapTimestamp(); + this.lastMod.mark(); + + debugLogger.debug( "MR_Cluster constructor w initialization complete" + this.lastMod.getVal() ); + } + + public MR_Cluster( String dLN, + String f, + String prot, + String port, + String repGroup, + String sourceRepPort, + String targetRepPort ) { + setDefaults(); + this.dcaeLocationName = dLN; + this.fqdn = f; + + if ( prot == null || prot.isEmpty() ) { + this.topicProtocol = defaultTopicProtocol; + } else { + this.topicProtocol = prot; + } + if ( port == null || port.isEmpty() ) { + this.topicPort = defaultTopicPort; + } else { + this.topicPort = port; + } + if ( repGroup == null || repGroup.isEmpty() ) { + this.replicationGroup = defaultReplicationGroup; + } else { + this.replicationGroup = repGroup; + } + if ( sourceRepPort == null || sourceRepPort.isEmpty()) { + this.sourceReplicationPort = defaultSourceReplicationPort; + } else { + this.sourceReplicationPort = sourceRepPort; + } + if ( targetRepPort == null || targetRepPort.isEmpty()) { + this.targetReplicationPort = defaultTargetReplicationPort; + } else { + this.targetReplicationPort = targetRepPort; + } + + this.lastMod = new DmaapTimestamp(); + this.lastMod.mark(); + + debugLogger.debug( "MR_Cluster constructor w initialization complete" + this.lastMod.getVal() ); + } + public String getDcaeLocationName() { + return dcaeLocationName; + } + + public void setDcaeLocationName(String dcaeLocationName) { + this.dcaeLocationName = dcaeLocationName; + } + + public String getFqdn() { + return fqdn; + } + + public void setFqdn(String fqdn) { + this.fqdn = fqdn; + } + + + public String getTopicProtocol() { + return topicProtocol; + } + + public void setTopicProtocol(String topicProtocol) { + this.topicProtocol = topicProtocol; + } + + public String getTopicPort() { + return topicPort; + } + + public void setTopicPort(String topicPort) { + this.topicPort = topicPort; + } + + public String getReplicationGroup() { + return replicationGroup; + } + + public void setReplicationGroup(String replicationGroup) { + this.replicationGroup = replicationGroup; + } + + + + + public String getSourceReplicationPort() { + return sourceReplicationPort; + } + + + + public void setSourceReplicationPort(String sourceReplicationPort) { + this.sourceReplicationPort = sourceReplicationPort; + } + + + + public String getTargetReplicationPort() { + return targetReplicationPort; + } + + + + public void setTargetReplicationPort(String targetReplicationPort) { + this.targetReplicationPort = targetReplicationPort; + } + + + + public String genTopicURL(String overideFqdn, String topic) { + + StringBuilder str = new StringBuilder( topicProtocol ); + str.append("://") + .append( overideFqdn != null ? overideFqdn : fqdn) + .append(":") + .append(topicPort) + .append("/events/") + .append(topic); + + return str.toString(); + + + } + + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MirrorMaker.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MirrorMaker.java new file mode 100644 index 0000000..098524c --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MirrorMaker.java @@ -0,0 +1,165 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import org.onap.dmaap.dbcapi.service.MirrorMakerService; + +import java.util.ArrayList; + +public class MirrorMaker extends DmaapObject { + + private String sourceCluster; + private String targetCluster; + private String mmName; + private ArrayList<String> topics; //re-using this var name for backwards DB compatibility + + public MirrorMaker(){ + + } + + public MirrorMaker(String source, String target, int i) { + initMM( source, target ); + // original mm names did not have any index, so leave off index 0 for + // backwards compatibility + if ( i != 0 ) { + String n = this.getMmName() + "_" + i; + this.setMmName(n); + } + } + + public MirrorMaker(String source, String target) { + initMM( source, target ); + } + + private void initMM(String source, String target) { + sourceCluster = source; + targetCluster = target; + mmName = genKey(source, target); + topics = new ArrayList<>(); + + } + + public String getMmName() { + return mmName; + } + + public void setMmName(String mmName) { + this.mmName = mmName; + } + + // returns the JSON for MM message containing which Topics to replicate + /* + * example: + * + { + "messageID":"12349", + "updateWhiteList": + { + "name":"Global1ToGlobal3", + "whitelist":"org.openecomp.dcae.topic1,org.openecomp.dcae.topic2" + } + } + */ + public String getWhitelistUpdateJSON() { + StringBuilder str = new StringBuilder( "{ \"messageID\": \"" + MirrorMakerService.genTransactionId() + "\", \"updateWhiteList\": {" ); + str.append( " \"name\": \"" + this.getMmName() + "\", \"whitelist\": \"" ); + int numTargets = 0; + + for (String rv: topics) { + if ( numTargets > 0 ) { + str.append( ","); + } + str.append( rv ); + numTargets++; + } + str.append( "\" } }" ); + + return str.toString(); + } + + // returns the JSON for MM message indicating that a MM agent is needed between two clusters + // example: + /* + * + { + "messageID":"12345" + "createMirrorMaker": + { + "name":"Global1ToGlobal2", + "consumer":"192.168.0.1:2181", + "producer":"192.168.0.2:9092" + } + } + */ + public String createMirrorMaker( String consumerPort, String producerPort ) { + StringBuilder str = new StringBuilder( "{ \"messageID\": \"" + MirrorMakerService.genTransactionId() + "\", \"createMirrorMaker\": {" ); + str.append( " \"name\": \"" + this.getMmName() + "\", " ); + str.append( " \"consumer\": \"" + this.sourceCluster + ":" + consumerPort + "\", " ); + str.append( " \"producer\": \"" + this.targetCluster + ":" + producerPort + "\", "); + + str.append( " \"numStreams\": \"10\" } }" ); + + return str.toString(); + } + + public String getSourceCluster() { + return sourceCluster; + } + + public void setSourceCluster(String sourceCluster) { + this.sourceCluster = sourceCluster; + } + + public String getTargetCluster() { + return targetCluster; + } + + public void setTargetCluster(String targetCluster) { + this.targetCluster = targetCluster; + } + + public ArrayList<String> getTopics() { + return topics; + } + + public void setTopics(ArrayList<String> topics) { + this.topics = topics; + } + + public static String genKey( String s, String t) { + StringBuilder str = new StringBuilder(); + str.append(s); + str.append("-To-"); + str.append(t); + return str.toString(); + } + + public void addTopic( String topic ) { + if ( ! topics.contains(topic)) { + topics.add(topic); + } + logger.info( "Mirrormaker.addTopic: topic=" + topic + " . Now have " + topics.size() + " topics" ); + } + + public int getTopicCount() { + return topics.size(); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ReplicationType.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ReplicationType.java new file mode 100644 index 0000000..5d5b6c6 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ReplicationType.java @@ -0,0 +1,109 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import java.util.HashMap; +import java.util.Map; + +import javax.xml.bind.annotation.XmlRootElement; + + +@XmlRootElement +public enum ReplicationType { + REPLICATION_NOT_SPECIFIED(0), + REPLICATION_NONE(1), + REPLICATION_EDGE_TO_CENTRAL(10), + REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL(110), + REPLICATION_CENTRAL_TO_EDGE(20), + REPLICATION_CENTRAL_TO_GLOBAL(21), + REPLICATION_GLOBAL_TO_CENTRAL(30), + REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE(120), + REPLICATION_EDGE_TO_FQDN(40), + REPLICATION_FQDN_TO_EDGE(41), + REPLICATION_FQDN_TO_GLOBAL(50), + REPLICATION_GLOBAL_TO_FQDN(51), + REPLICATION_EDGE_TO_FQDN_TO_GLOBAL(130), + REPLICATION_GLOBAL_TO_FQDN_TO_EDGE (140); + + private int value; + private static Map map = new HashMap<>(); + + private ReplicationType(int value) { + this.value = value; + } + + static { + for (ReplicationType repType : ReplicationType.values()) { + map.put(repType.value, repType); + } + } + + public static ReplicationType valueOf(int repType) { + return (ReplicationType) map.get(repType); + } + + public int getValue() { + return value; + } + + static public ReplicationType Validator( String input ){ + + ReplicationType t; + try { + t = ReplicationType.valueOf( input ); + } catch ( IllegalArgumentException e ) { + t = REPLICATION_NOT_SPECIFIED; + } + return t; + } + + public boolean involvesGlobal() { + + + if ( ( this.compareTo(REPLICATION_CENTRAL_TO_GLOBAL) == 0 ) || + ( this.compareTo(REPLICATION_GLOBAL_TO_CENTRAL) == 0 ) || + ( this.compareTo(REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL) == 0 ) || + ( this.compareTo(REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE) == 0 ) || + ( this.compareTo(REPLICATION_EDGE_TO_FQDN_TO_GLOBAL) == 0 ) || + ( this.compareTo(REPLICATION_GLOBAL_TO_FQDN_TO_EDGE) == 0 ) || + ( this.compareTo(REPLICATION_FQDN_TO_GLOBAL) == 0 ) || + ( this.compareTo(REPLICATION_GLOBAL_TO_FQDN) == 0 ) ) { + return true; + } + return false; + } + + public boolean involvesFQDN() { + if ( + ( this.compareTo(REPLICATION_EDGE_TO_FQDN) == 0 ) || + ( this.compareTo(REPLICATION_EDGE_TO_FQDN_TO_GLOBAL) == 0 ) || + ( this.compareTo(REPLICATION_GLOBAL_TO_FQDN_TO_EDGE) == 0 ) || + ( this.compareTo(REPLICATION_FQDN_TO_GLOBAL) == 0 ) || + ( this.compareTo(REPLICATION_GLOBAL_TO_FQDN) == 0 ) || + ( this.compareTo(REPLICATION_FQDN_TO_EDGE) == 0 ) + ) { + return true; + } + return false; + } + + + +}
\ No newline at end of file diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java new file mode 100644 index 0000000..d2a9077 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java @@ -0,0 +1,352 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.model; + +import com.google.common.base.Objects; +import io.swagger.annotations.ApiModelProperty; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import javax.xml.bind.annotation.XmlRootElement; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.onap.dmaap.dbcapi.service.DmaapService; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + + +@XmlRootElement +public class Topic extends DmaapObject { + + @ApiModelProperty( value="Fully Qualified Topic Name constructed by dbcapi, following the rules for `fqtnStyle`") + private String fqtn; + @ApiModelProperty( value="the short name used by humans, and utilized to construct the `FQTN`") + private String topicName; + @ApiModelProperty( value="a description of what this Topic is used for") + private String topicDescription; + private String tnxEnabled; + @ApiModelProperty( value="a label used to identify who requested this `Topic` to be provisioned. In the future this " + + "may be an AAF Identity.") + private String owner; + @ApiModelProperty( value="a reference to an identifier that describes a data format used for this `Topic`") + private String formatUuid; + @ApiModelProperty( value="An indicator for how this `Topic` should be replicated when there are more than one `MR_Cluster` instances") + private ReplicationType replicationCase; + @ApiModelProperty( value="the URL of an outside MR instance") + private String globalMrURL; // optional: URL of global MR to replicate to/from + @ApiModelProperty( value="the construction rule for the `fqtn` field") + private FqtnType fqtnStyle; + @ApiModelProperty( value="a hook for any versioning needed for managing a `Topic` over time") + private String version; + @ApiModelProperty( value="the kafka attribute for specifying the number of partitions") + private String partitionCount; + @ApiModelProperty( value="the kafka attribute for specifying replication within an `MR_Cluster` instance") + private String replicationCount; + @ApiModelProperty( value="a value generated by dbcapi, this AAF Role has permission to publish to this `Topic`") + private String publisherRole; + @ApiModelProperty( value="a value generated by dbcapi, this AAF Role has permission to subscribe to this `Topic`") + private String subscriberRole; + + @ApiModelProperty( value="an array of `MR_Client` objects associated to this `Topic`") + private List<MR_Client> clients; + + + + private static Dmaap dmaap = new DmaapService().getDmaap(); + + private static String defaultPartitionCount; + private static String defaultReplicationCount; + + // during unit testing, discovered that presence of dots in some values + // creates an unplanned topic namespace as we compose the FQTN. + // this may create sensitivity (i.e. 403) for subsequent creation of AAF perms, so best to not allow it + private static String removeDots( String source, String def ) { + if ( source == null || source.isEmpty()) { + return def; + } + return source.replaceAll("\\.", "_"); + } + // + // utility function to generate the FQTN of a topic + public String genFqtn( ) { + DmaapConfig dc = (DmaapConfig)DmaapConfig.getConfig(); + String projectId = dc.getProperty("MR.projectID", "99999"); + CharSequence signal = "."; + String ret; + if ( this.getTopicName().contains( signal )) { + // presence of a dot indicates the name is already fully qualified + ret = this.getTopicName(); + } else { + // these vars may not contain dots + String p = removeDots( projectId, "90909"); + String v = removeDots( this.getVersion(), "v1"); + switch( this.getFqtnStyle() ) { + case FQTN_PROJECTID_VERSION_FORMAT: + + ret = dmaap.getTopicNsRoot() + "." + dmaap.getDmaapName() + "." + p + "-" + this.getTopicName() + "-" + v; + break; + + case FQTN_PROJECTID_FORMAT: + + ret = dmaap.getTopicNsRoot() + "." + dmaap.getDmaapName() + "." + p + "-" + this.getTopicName(); + break; + + case FQTN_LEGACY_FORMAT: + default: // for backwards compatibility + ret = dmaap.getTopicNsRoot() + "." + dmaap.getDmaapName() + "." + this.getTopicName(); + break; + + + } + + } + return ret; + } + + + + public Topic() { + super(); + this.clients = new ArrayList<>(); + this.lastMod = new Date(); + this.replicationCase = ReplicationType.Validator("none"); + this.setLastMod(); + logger.debug( "Topic constructor " + this.lastMod ); + } + public Topic(String fqtn, String topicName, String topicDescription, + String tnxEnabled, String owner) { + super(); + this.fqtn = fqtn; + this.topicName = topicName; + this.topicDescription = topicDescription; + this.tnxEnabled = tnxEnabled; + this.owner = owner; + this.init(); + this.setLastMod(); + logger.debug( "Topic constructor w args " + this.getLastMod() ); + } + + public Topic init() { + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + + defaultPartitionCount = p.getProperty( "MR.partitionCount", "2"); + defaultReplicationCount = p.getProperty( "MR.replicationCount", "1"); + + this.setStatus( DmaapObject_Status.NEW ); + this.replicationCase = ReplicationType.Validator("none"); + this.fqtnStyle = FqtnType.Validator("none"); + this.setPartitionCount( defaultPartitionCount ); + this.setReplicationCount( defaultReplicationCount ); + + return this; + } + + // expects a String in JSON format, with known fields to populate Topic object + public Topic ( String json ) { + JSONParser parser = new JSONParser(); + JSONObject jsonObj; + try { + jsonObj = (JSONObject) parser.parse( json ); + } catch ( ParseException pe ) { + logger.error( "Error parsing provisioning data: " + json ); + this.setStatus( DmaapObject_Status.INVALID ); + return; + } + this.setFqtn( (String) jsonObj.get( "fqtn" ) ); + this.setTopicName( (String) jsonObj.get( "topicName" ) ); + this.setTopicDescription( (String) jsonObj.get( "topicDescription" )); + this.setOwner( (String) jsonObj.get( "owner" ) ); + this.setStatus( (String) jsonObj.get( "status" ) ); + this.setReplicationCase( ReplicationType.Validator( (String) jsonObj.get( "replicationCase" ) )); + this.setFqtnStyle( FqtnType.Validator( (String) jsonObj.get( "fqtnStyle" ) ) ); + this.setPartitionCount( (String) jsonObj.get("partitionCount")); + + } + public String getFqtn() { + return fqtn; + } + public void setFqtn(String fqtn) { + this.fqtn = fqtn; + } + public String getTopicName() { + return topicName; + } + public void setTopicName(String topicName) { + this.topicName = topicName; + } + public String getTopicDescription() { + return topicDescription; + } + public void setTopicDescription(String topicDescription) { + this.topicDescription = topicDescription; + } + + public String getTnxEnabled() { + return tnxEnabled; + } + public void setTnxEnabled(String tnxEnabled) { + this.tnxEnabled = tnxEnabled; + } + public String getOwner() { + return owner; + } + public void setOwner(String owner) { + this.owner = owner; + } + public String getPartitionCount() { + return partitionCount; + } + public void setPartitionCount(String partitions) { + this.partitionCount = partitions; + } + public String getReplicationCount() { + return replicationCount; + } + public void setReplicationCount(String replicationCount) { + this.replicationCount = replicationCount; + } + + + public void setClients(List<MR_Client> clients) { + this.clients = clients; + } + + public List<MR_Client> getClients() { + return clients; + } + + @ApiModelProperty( hidden=true ) + public int getNumClients() { + if ( this.clients == null ) { + return 0; + } + return this.clients.size(); + } + + + + + public String getFormatUuid() { + return formatUuid; + } + + + + public void setFormatUuid(String formatUuid) { + this.formatUuid = formatUuid; + } + + + public ReplicationType getReplicationCase() { + return replicationCase; + } + + + public void setReplicationCase(ReplicationType t) { + this.replicationCase = t; + } + public FqtnType getFqtnStyle() { + return fqtnStyle; + } + + + public void setFqtnStyle(FqtnType t) { + this.fqtnStyle = t; + } + + public String getGlobalMrURL() { + return globalMrURL; + } + + + + public void setGlobalMrURL(String globalMrURL) { + this.globalMrURL = globalMrURL; + } + + + + public String getVersion() { + return version; + } + + + + public void setVersion(String version) { + this.version = version; + } + + + + public String getPublisherRole() { + return publisherRole; + } + public void setPublisherRole(String publisherRole) { + this.publisherRole = publisherRole; + } + public String getSubscriberRole() { + return subscriberRole; + } + public void setSubscriberRole(String subscriberRole) { + this.subscriberRole = subscriberRole; + } + public String toProvJSON() { + StringBuilder str = new StringBuilder(); + str.append("{ \"topicName\": \""); + str.append( this.getFqtn() ); + str.append( "\", \"topicDescription\": \""); + str.append( this.getTopicDescription()); + str.append( "\", \"partitionCount\": \""); + str.append( this.getPartitionCount()); + str.append( "\", \"replicationCount\": \""); + str.append( this.getReplicationCount()); + str.append( "\" } "); + + logger.info( str.toString() ); + return str.toString(); + } + @ApiModelProperty( hidden=true ) + public byte[] getBytes() { + return toProvJSON().getBytes(StandardCharsets.UTF_8); + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Topic topic = (Topic) o; + return Objects.equal(fqtn, topic.fqtn) && + Objects.equal(topicName, topic.topicName) && + Objects.equal(tnxEnabled, topic.tnxEnabled) && + Objects.equal(owner, topic.owner); + } + + @Override + public int hashCode() { + return Objects.hashCode(fqtn, topicName, tnxEnabled, owner); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilter.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilter.java new file mode 100644 index 0000000..d8a7302 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilter.java @@ -0,0 +1,141 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2019 Nokia 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.dmaap.dbcapi.resources; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.filter.CadiFilter; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +public class AAFAuthenticationFilter extends BaseLoggingClass implements Filter{ + + static final String CADI_PROPERTIES = "cadi.properties"; + static final String CADI_AUTHN_FLAG = "enableCADI"; + + private boolean isCadiEnabled; + private CadiFilter cadiFilter; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + DmaapConfig dmaapConfig = getConfig(); + String flag = dmaapConfig.getProperty(CADI_AUTHN_FLAG, "false"); + isCadiEnabled = "true".equalsIgnoreCase(flag); + initCadi(dmaapConfig); + } + + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + + if(isCadiEnabled) { + cadiFilter.doFilter(servletRequest, servletResponse, filterChain); + updateResponseBody((HttpServletResponse)servletResponse); + } else { + filterChain.doFilter(servletRequest, servletResponse); + } + } + + private void updateResponseBody(HttpServletResponse httpResponse) + throws IOException { + if(httpResponse.getStatus() == 401) { + String errorMsg = "invalid or no credentials provided"; + errorLogger.error(errorMsg); + httpResponse.setContentType("application/json"); + httpResponse.setCharacterEncoding("UTF-8"); + httpResponse.getWriter().print(buildErrorResponse(errorMsg)); + httpResponse.getWriter().flush(); + } + } + + private String buildErrorResponse(String msg) { + try { + return new ObjectMapper().writeValueAsString(new ApiError(HttpStatus.UNAUTHORIZED_401, msg, "Authentication")); + } catch (JsonProcessingException e) { + logger.warn("Could not serialize response entity: " + e.getMessage()); + return ""; + } + } + + + @Override + public void destroy() { + //nothing to cleanup + } + + private void initCadi(DmaapConfig dmaapConfig) throws ServletException { + if(isCadiEnabled) { + try { + String cadiPropertiesFile = dmaapConfig.getProperty(CADI_PROPERTIES); + if(cadiPropertiesFile != null && !cadiPropertiesFile.isEmpty()) { + cadiFilter = new CadiFilter(loadCadiProperties(cadiPropertiesFile)); + } else { + throw new ServletException("Cannot initialize CADI filter.CADI properties not available."); + } + } catch (ServletException e) { + errorLogger.error("CADI init error :" + e.getMessage()); + throw e; + } + } + } + + private PropAccess loadCadiProperties(String propertiesFilePath) throws ServletException { + try { + Properties props = new Properties(); + props.load(new FileInputStream(propertiesFilePath)); + return new PropAccess(props); + } catch (IOException e) { + String msg = "Could not load CADI properties file: " + propertiesFilePath; + errorLogger.error(msg, e); + throw new ServletException(msg); + } + } + + DmaapConfig getConfig() { + return (DmaapConfig) DmaapConfig.getConfig(); + } + + //tests only + CadiFilter getCadiFilter() { + return cadiFilter; + } + + void setCadiFilter(CadiFilter cadiFilter) { + this.cadiFilter = cadiFilter; + } + + boolean isCadiEnabled() { + return isCadiEnabled; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilter.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilter.java new file mode 100644 index 0000000..779f71b --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilter.java @@ -0,0 +1,115 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2019 Nokia 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.dmaap.dbcapi.resources; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.service.DmaapService; +import org.onap.dmaap.dbcapi.util.DmaapConfig; +import org.onap.dmaap.dbcapi.util.PermissionBuilder; + +public class AAFAuthorizationFilter extends BaseLoggingClass implements Filter { + + static final String CADI_AUTHZ_FLAG = "enableCADI"; + private boolean isCadiEnabled = false; + + private PermissionBuilder permissionBuilder; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + DmaapConfig dmaapConfig = getConfig(); + isCadiEnabled = "true".equalsIgnoreCase(dmaapConfig.getProperty(CADI_AUTHZ_FLAG, "false")); + if(isCadiEnabled) { + permissionBuilder = new PermissionBuilder(dmaapConfig, getDmaapService()); + } + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + + if(isCadiEnabled) { + HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; + permissionBuilder.updateDmaapInstance(); + String permission = permissionBuilder.buildPermission(httpRequest); + + if (httpRequest.isUserInRole(permission)) { + logger.info("User " + httpRequest.getUserPrincipal().getName() + " has permission " + permission); + filterChain.doFilter(servletRequest, servletResponse); + } else { + String msg = "User " + httpRequest.getUserPrincipal().getName() + " does not have permission " + permission; + errorLogger.error(msg); + ((HttpServletResponse) servletResponse).setStatus(HttpStatus.FORBIDDEN_403); + servletResponse.setContentType("application/json"); + servletResponse.setCharacterEncoding("UTF-8"); + servletResponse.getWriter().print(buildErrorResponse(msg)); + servletResponse.getWriter().flush(); + } + } else { + filterChain.doFilter(servletRequest, servletResponse); + } + } + + @Override + public void destroy() { + //nothing to cleanup + } + + DmaapConfig getConfig() { + return (DmaapConfig) DmaapConfig.getConfig(); + } + + DmaapService getDmaapService() { + return new DmaapService(); + } + + private String buildErrorResponse(String msg) { + try { + return new ObjectMapper().writeValueAsString(new ApiError(HttpStatus.FORBIDDEN_403, msg, "Authorization")); + } catch (JsonProcessingException e) { + logger.warn("Could not serialize response entity: " + e.getMessage()); + return ""; + } + } + + PermissionBuilder getPermissionBuilder() { + return permissionBuilder; + } + + void setPermissionBuilder(PermissionBuilder permissionBuilder) { + this.permissionBuilder = permissionBuilder; + } + + void setCadiEnabled(boolean cadiEnabled) { + isCadiEnabled = cadiEnabled; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/Authorization.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/Authorization.java new file mode 100644 index 0000000..e8b05c6 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/Authorization.java @@ -0,0 +1,35 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.resources; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; + +import javax.ws.rs.NameBinding; + +// @Authorization annotation +@NameBinding +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface Authorization { + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AuthorizationFilter.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AuthorizationFilter.java new file mode 100644 index 0000000..32e8845 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AuthorizationFilter.java @@ -0,0 +1,68 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.resources; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; + +import org.onap.dmaap.dbcapi.authentication.AuthenticationErrorException; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.service.ApiService; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + + +@Authorization +public class AuthorizationFilter extends BaseLoggingClass implements ContainerRequestFilter { + + private static final String AAF_CADI_FLAG = "enableCADI"; + private final ResponseBuilder responseBuilder = new ResponseBuilder(); + private final boolean isCadiEnabled; + + + public AuthorizationFilter() { + DmaapConfig dmaapConfig = (DmaapConfig) DmaapConfig.getConfig(); + String flag = dmaapConfig.getProperty(AAF_CADI_FLAG, "false"); + isCadiEnabled = "true".equalsIgnoreCase(flag); + } + + @Override + public void filter(ContainerRequestContext requestContext) { + + if(!isCadiEnabled) { + ApiService apiResp = new ApiService() + .setAuth(requestContext.getHeaderString("Authorization")) + .setUriPath(requestContext.getUriInfo().getPath()) + .setHttpMethod(requestContext.getMethod()) + .setRequestId(requestContext.getHeaderString("X-ECOMP-RequestID")); + + try { + apiResp.checkAuthorization(); + } catch (AuthenticationErrorException ae) { + errorLogger.error("Error", ae); + requestContext.abortWith(responseBuilder.unauthorized(apiResp.getErr().getMessage())); + } catch (Exception e) { + errorLogger.error("Error", e); + requestContext.abortWith(responseBuilder.unavailable()); + } + } + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/BridgeResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/BridgeResource.java new file mode 100644 index 0000000..299c48f --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/BridgeResource.java @@ -0,0 +1,191 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Modifications Copyright (C) 2018 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.resources; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.BrTopic; +import org.onap.dmaap.dbcapi.model.MirrorMaker; +import org.onap.dmaap.dbcapi.service.MirrorMakerService; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; + +@Path("/bridge") +@Api( value= "bridge", description = "Endpoint for retreiving MR Bridge metrics" ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Authorization +public class BridgeResource extends BaseLoggingClass { + + private MirrorMakerService mmService = new MirrorMakerService(); + private ResponseBuilder responseBuilder = new ResponseBuilder(); + + @GET + @ApiOperation( value = "return BrTopic details", + notes = "Returns array of `BrTopic` objects. If source and target query params are specified, only report on that bridge. " + + "If detail param is true, list topics names, else just a count is returned.", + response = BrTopic.class) +@ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = BrTopic.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) +}) + public Response getBridgedTopics(@QueryParam("mmagent") String mmagent, + @QueryParam("detail") Boolean detailFlag ){ + + if ( mmagent == null ) { + return responseBuilder.success(getMMcounts(Boolean.TRUE.equals(detailFlag))); + + } + logger.info( "getBridgeTopics():" + " mmagent=" + mmagent); + + if ( ! Boolean.TRUE.equals(detailFlag)) { + BrTopic brTopic = new BrTopic(); + + // get topics between 2 bridged locations + + MirrorMaker mm = mmService.getMirrorMaker(mmagent); + if ( mm == null ) { + return responseBuilder.notFound(); + } + + brTopic.setTopicCount( mm.getTopicCount() ); + brTopic.setBrSource( mm.getSourceCluster()); + brTopic.setBrTarget( mm.getTargetCluster()); + brTopic.setMmAgentName(mm.getMmName()); + + logger.info( "topicCount [2 locations]: " + brTopic.getTopicCount() ); + + return responseBuilder.success(brTopic); + } else { + logger.info( "getBridgeTopics() detail:" + " mmagent=" + mmagent); + // get topics between 2 bridged locations + MirrorMaker mm = mmService.getMirrorMaker(mmagent); + if ( mm == null ) { + return responseBuilder.notFound(); + } + + return responseBuilder.success(mm); + } + } + + private BrTopic[] getMMcounts( Boolean showDetail ) { + + List<String> mmList = mmService.getAllMirrorMakers(); + int s = 1; + if ( showDetail ) { + s = mmList.size() + 1; + } + BrTopic[] brTopic = new BrTopic[s]; + + int totCnt = 0; + s = 0; + for( String key: mmList ) { + int mCnt = 0; + MirrorMaker mm = mmService.getMirrorMaker(key); + if ( mm != null ) { + mCnt = mm.getTopicCount(); + } + logger.info( "Count for "+ key + ": " + mCnt); + totCnt += mCnt; + if (showDetail && mm!=null) { + brTopic[s] = new BrTopic(); + brTopic[s].setBrSource( mm.getSourceCluster()); + brTopic[s].setBrTarget(mm.getTargetCluster()); + brTopic[s].setMmAgentName(mm.getMmName()); + brTopic[s].setTopicCount(mm.getTopicCount()); + s++; + } + } + + logger.info( "topicCount [all locations]: " + totCnt ); + brTopic[s] = new BrTopic(); + brTopic[s].setBrSource("all"); + brTopic[s].setBrTarget("all"); + brTopic[s].setMmAgentName("n/a"); + brTopic[s].setTopicCount(totCnt); + return brTopic; + } + + @PUT + @ApiOperation( value = "update MirrorMaker details", + notes = "replace the topic list for a specific Bridge. Use JSON Body for value to replace whitelist, " + + "but if refreshFlag param is true, simply refresh using existing whitelist." + + "If split param is true, spread whitelist over smaller mmagents.", + response = MirrorMaker.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = BrTopic.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response putBridgedTopics(@QueryParam("mmagent") String mmagent, + @QueryParam("refresh") Boolean refreshFlag, + @QueryParam("split") Boolean splitFlag, + MirrorMaker newBridge ){ + logger.info( "putBridgeTopics() mmagent:" + mmagent ); + + if ( mmagent != null ) { // put topics between 2 bridged locations + + MirrorMaker mm = mmService.getMirrorMaker(mmagent); + if ( mm == null ) { + return responseBuilder.notFound(); + } + + if ( splitFlag != null && splitFlag == true ) { + mm = mmService.splitMM( mm ); + } else if ( refreshFlag == null || refreshFlag == false ) { + logger.info( "setting whitelist from message body containing mmName=" + newBridge.getMmName()); + if ( ! mmagent.equals(newBridge.getMmName()) ){ + logger.error( "mmagent query param does not match mmName in body"); + return responseBuilder.error(new ApiError(BAD_REQUEST.getStatusCode(), + "mmagent query param does not match mmName in body")); + } + mm.setTopics( newBridge.getTopics() ); + } else { + logger.info( "refreshing whitelist from memory"); + } + mmService.updateMirrorMaker(mm); + return responseBuilder.success(mm); + } + + else { + logger.error( "mmagent is required for PUT"); + return responseBuilder.error(new ApiError(BAD_REQUEST.getStatusCode(), "mmagent is required for PUT")); + } + + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_NodeResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_NodeResource.java new file mode 100644 index 0000000..f001136 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_NodeResource.java @@ -0,0 +1,172 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dcae + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DR_Node; +import org.onap.dmaap.dbcapi.service.DR_NodeService; + +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static javax.ws.rs.core.Response.Status.NO_CONTENT; + +@Path("/dr_nodes") +@Api( value= "dr_nodes", description = "Endpoint for a Data Router Node server" ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Authorization +public class DR_NodeResource extends BaseLoggingClass { + + private DR_NodeService dr_nodeService = new DR_NodeService(); + private ResponseBuilder responseBuilder = new ResponseBuilder(); + private RequiredChecker checker = new RequiredChecker(); + + @GET + @ApiOperation( value = "return DR_Node details", + notes = "Returns array of `DR_Node` object array. Need to add filter by dcaeLocation.", + response = DR_Node.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Node.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response getDr_Nodes() { + List<DR_Node> nodes = dr_nodeService.getAllDr_Nodes(); + + GenericEntity<List<DR_Node>> list = new GenericEntity<List<DR_Node>>(nodes) { + }; + return responseBuilder.success(list); + } + + @POST + @ApiOperation( value = "return DR_Node details", + notes = "create a `DR_Node` in a *dcaeLocation*. Note that multiple `DR_Node`s may exist in the same `dcaeLocation`.", + response = DR_Node.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Node.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response addDr_Node(DR_Node node) { + + ApiError apiError = new ApiError(); + + try { + checker.required( "dcaeLocation", node.getDcaeLocationName()); + checker.required( "fqdn", node.getFqdn()); + } catch ( RequiredFieldException rfe ) { + return responseBuilder.error(new ApiError(BAD_REQUEST.getStatusCode(), + "missing required field", "dcaeLocation, fqdn")); + } + DR_Node nNode = dr_nodeService.addDr_Node(node, apiError); + if (apiError.is2xx()) { + return responseBuilder.success(nNode); + } + return responseBuilder.error(apiError); + } + + @PUT + @ApiOperation( value = "return DR_Node details", + notes = "Update a single `DR_Node` object.", + response = DR_Node.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Node.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{fqdn}") + public Response updateDr_Node(@PathParam("fqdn") String name, DR_Node node) { + + ApiError apiError = new ApiError(); + + try { + checker.required( "dcaeLocation", node.getDcaeLocationName()); + checker.required( "fqdn", node.getFqdn()); + } catch ( RequiredFieldException rfe ) { + return responseBuilder.error(new ApiError(BAD_REQUEST.getStatusCode(), + "missing required field", "dcaeLocation, fqdn")); + } + node.setFqdn(name); + DR_Node nNode = dr_nodeService.updateDr_Node(node, apiError); + if (apiError.is2xx()) { + return responseBuilder.success(nNode); + } + return responseBuilder.error(apiError); + } + + @DELETE + @ApiOperation( value = "No Content", + notes = "Delete a single `DR_Node` object.", + response = DR_Node.class) + @ApiResponses( value = { + @ApiResponse( code = 204, message = "Success", response = DR_Node.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{fqdn}") + public Response deleteDr_Node( + @PathParam("fqdn") String name){ + + + ApiError apiError = new ApiError(); + + dr_nodeService.removeDr_Node(name, apiError); + if (apiError.is2xx()) { + return responseBuilder.success(NO_CONTENT.getStatusCode(), null); + } + return responseBuilder.error(apiError); + } + + @GET + @ApiOperation( value = "return DR_Node details", + notes = "Retrieve a single `DR_Node` object.", + response = DR_Node.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Node.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{fqdn}") + public Response get(@PathParam("fqdn") String name) { + + ApiError apiError = new ApiError(); + + DR_Node nNode = dr_nodeService.getDr_Node( name, apiError ); + if (apiError.is2xx()) { + return responseBuilder.success(nNode); + } + return responseBuilder.error(apiError); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_PubResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_PubResource.java new file mode 100644 index 0000000..f512124 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_PubResource.java @@ -0,0 +1,252 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DR_Pub; +import org.onap.dmaap.dbcapi.model.Feed; +import org.onap.dmaap.dbcapi.service.DR_PubService; +import org.onap.dmaap.dbcapi.service.FeedService; + + +@Path("/dr_pubs") +@Api( value= "dr_pubs", description = "Endpoint for a Data Router client that implements a Publisher" ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Authorization +public class DR_PubResource extends BaseLoggingClass { + + private DR_PubService dr_pubService = new DR_PubService(); + private ResponseBuilder responseBuilder = new ResponseBuilder(); + private RequiredChecker checker = new RequiredChecker(); + + @GET + @ApiOperation( value = "return DR_Pub details", + notes = "Returns array of `DR_Pub` objects. Add filter for feedId.", + response = DR_Pub.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response getDr_Pubs() { + logger.info( "Entry: GET /dr_pubs"); + List<DR_Pub> pubs = dr_pubService.getAllDr_Pubs(); + + GenericEntity<List<DR_Pub>> list = new GenericEntity<List<DR_Pub>>(pubs) { + }; + return responseBuilder.success(list); + } + + + @POST + @ApiOperation( value = "return DR_Pub details", + notes = "create a DR Publisher in the specified environment.", + response = DR_Pub.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response addDr_Pub(DR_Pub pub) { + ApiError apiError = new ApiError(); + FeedService feeds = new FeedService(); + Feed fnew = null; + + logger.info( "Entry: POST /dr_pubs"); + + try { + checker.required( "feedId", pub.getFeedId()); + } catch ( RequiredFieldException rfe ) { + try { + checker.required( "feedName", pub.getFeedName()); + }catch ( RequiredFieldException rfe2 ) { + logger.debug( rfe2.getApiError().toString() ); + return responseBuilder.error(rfe2.getApiError()); + } + // if we found a FeedName instead of a FeedId then try to look it up. + List<Feed> nfeeds = feeds.getAllFeeds( pub.getFeedName(), pub.getFeedVersion(), "equals"); + if ( nfeeds.isEmpty() ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields("feedName"); + return responseBuilder.error(apiError); + } + fnew = nfeeds.get(0); + } + try { + checker.required( "dcaeLocationName", pub.getDcaeLocationName()); + } catch ( RequiredFieldException rfe ) { + logger.debug( rfe.getApiError().toString() ); + return responseBuilder.error(rfe.getApiError()); + } + + + // we may have fnew already if located by FeedName + if ( fnew == null ) { + fnew = feeds.getFeed(pub.getFeedId(), apiError); + } + if ( fnew == null ) { + logger.info( "Specified feed " + pub.getFeedId() + " or " + pub.getFeedName() + " not known to Bus Controller"); + return responseBuilder.error(apiError); + } + + ArrayList<DR_Pub> pubs = fnew.getPubs(); + logger.info( "num existing pubs before = " + pubs.size() ); + + logger.info( "update feed"); + pub.setNextPubId(); + if ( pub.getUsername() == null ) { + pub.setRandomUserName(); + } + if ( pub.getUserpwd() == null ) { + pub.setRandomPassword(); + } + pubs.add( pub ); + fnew.setPubs(pubs); + fnew = feeds.updateFeed(fnew, apiError); + + if (!apiError.is2xx()) { + return responseBuilder.error(apiError); + } + pubs = fnew.getPubs(); + logger.info( "num existing pubs after = " + pubs.size() ); + + DR_Pub pnew = dr_pubService.getDr_Pub(pub.getPubId(), apiError); + return responseBuilder.success(Status.CREATED.getStatusCode(), pnew); + } + + @PUT + @ApiOperation( value = "return DR_Pub details", + notes = "update a DR Publisher in the specified environment. Update a `DR_Pub` object by pubId", + response = DR_Pub.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{pubId}") + public Response updateDr_Pub(@PathParam("pubId") String name, DR_Pub pub) { + logger.info( "Entry: PUT /dr_pubs"); + pub.setPubId(name); + DR_Pub res = dr_pubService.updateDr_Pub(pub); + return responseBuilder.success(res); + } + + @DELETE + @ApiOperation( value = "return DR_Pub details", + notes = "delete a DR Publisher in the specified environment. Delete a `DR_Pub` object by pubId", + response = DR_Pub.class) + @ApiResponses( value = { + @ApiResponse( code = 204, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{pubId}") + public Response deleteDr_Pub(@PathParam("pubId") String id){ + + ApiError apiError = new ApiError(); + + try { + checker.required( "pubId", id); + } catch ( RequiredFieldException rfe ) { + return responseBuilder.error(rfe.getApiError()); + } + + DR_Pub pub = dr_pubService.getDr_Pub(id, apiError); + if ( !apiError.is2xx()) { + return responseBuilder.error(apiError); + } + FeedService feeds = new FeedService(); + Feed fnew = feeds.getFeed(pub.getFeedId(), apiError); + if ( fnew == null ) { + logger.info( "Specified feed " + pub.getFeedId() + " not known to Bus Controller"); + return responseBuilder.error(apiError); + } + ArrayList<DR_Pub> pubs = fnew.getPubs(); + if ( pubs.size() == 1 ) { + apiError.setCode(Status.BAD_REQUEST.getStatusCode()); + apiError.setMessage( "Can't delete the last publisher of a feed"); + return responseBuilder.error(apiError); + } + + for( Iterator<DR_Pub> i = pubs.iterator(); i.hasNext(); ) { + DR_Pub listItem = i.next(); + if ( listItem.getPubId().equals(id)) { + i.remove(); + } + } + fnew.setPubs(pubs); + fnew = feeds.updateFeed(fnew,apiError); + if (!apiError.is2xx()) { + return responseBuilder.error(apiError); + } + + dr_pubService.removeDr_Pub(id, apiError); + if (!apiError.is2xx()) { + return responseBuilder.error(apiError); + } + return responseBuilder.success(Status.NO_CONTENT.getStatusCode(), null); + } + + @GET + @ApiOperation( value = "return DR_Pub details", + notes = "returns a DR Publisher in the specified environment. Gets a `DR_Pub` object by pubId", + response = DR_Pub.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{pubId}") + public Response get(@PathParam("pubId") String id) { + ApiError apiError = new ApiError(); + + try { + checker.required( "feedId", id); + } catch ( RequiredFieldException rfe ) { + return responseBuilder.error(rfe.getApiError()); + } + + DR_Pub pub = dr_pubService.getDr_Pub(id, apiError); + if (!apiError.is2xx()) { + return responseBuilder.error(apiError); + } + return responseBuilder.success(Status.OK.getStatusCode(), pub); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_SubResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_SubResource.java new file mode 100644 index 0000000..2fa6ccd --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_SubResource.java @@ -0,0 +1,243 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.resources; + +import com.google.common.collect.Iterables; +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DR_Sub; +import org.onap.dmaap.dbcapi.model.Feed; +import org.onap.dmaap.dbcapi.service.DR_SubService; +import org.onap.dmaap.dbcapi.service.FeedService; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import static javax.ws.rs.core.Response.Status.CREATED; + + +@Path("/dr_subs") +@Api( value= "dr_subs", description = "Endpoint for a Data Router client that implements a Subscriber" ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Authorization +public class DR_SubResource extends BaseLoggingClass { + + private ResponseBuilder responseBuilder = new ResponseBuilder(); + private RequiredChecker checker = new RequiredChecker(); + + @GET + @ApiOperation( value = "return DR_Sub details", + notes = "Returns array of `DR_Sub` objects. Add filter for feedId.", + response = DR_Sub.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Sub.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response getDr_Subs() { + DR_SubService dr_subService = new DR_SubService(); + List<DR_Sub> subs = dr_subService.getAllDr_Subs(); + + GenericEntity<List<DR_Sub>> list = new GenericEntity<List<DR_Sub>>(subs) { + }; + return responseBuilder.success(list); + } + + @POST + @ApiOperation( value = "return DR_Sub details", + notes = "Create a `DR_Sub` object. ", + response = DR_Sub.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Sub.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response addDr_Sub(DR_Sub sub) { + + ApiError apiError = new ApiError(); + FeedService feeds = new FeedService(); + Feed fnew = null; + try { + checker.required( "feedId", sub.getFeedId()); + } catch ( RequiredFieldException rfe ) { + try { + checker.required( "feedName", sub.getFeedName()); + }catch ( RequiredFieldException rfe2 ) { + logger.debug( rfe2.getApiError().toString() ); + return responseBuilder.error(rfe2.getApiError()); + } + // if we found a FeedName instead of a FeedId then try to look it up. + List<Feed> nfeeds = feeds.getAllFeeds( sub.getFeedName(), sub.getFeedVersion(), "equals"); + if ( nfeeds.isEmpty() ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields("feedName"); + return responseBuilder.error(apiError); + } else if (nfeeds.size() > 1) { + logger.debug( "Attempt to match "+ sub.getFeedName() + " ver="+sub.getFeedVersion() + " matched " + nfeeds.size() ); + apiError.setCode(Status.CONFLICT.getStatusCode()); + apiError.setFields("feedName"); + return responseBuilder.error(apiError); + } + fnew = Iterables.getOnlyElement(nfeeds); + } + + try { + checker.required( "dcaeLocationName", sub.getDcaeLocationName()); + } catch ( RequiredFieldException rfe ) { + logger.debug( rfe.getApiError().toString() ); + return responseBuilder.error(rfe.getApiError()); + } + // we may have fnew already if located by FeedName + if ( fnew == null ) { + fnew = feeds.getFeed( sub.getFeedId(), apiError); + } + if ( fnew == null ) { + logger.warn( "Specified feed " + sub.getFeedId() + " or " + sub.getFeedName() + " not known to Bus Controller"); + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + return responseBuilder.error(apiError); + } + DR_SubService dr_subService = new DR_SubService( fnew.getSubscribeURL()); + ArrayList<DR_Sub> subs = fnew.getSubs(); + logger.info( "num existing subs before = " + subs.size() ); + DR_Sub snew = dr_subService.addDr_Sub(sub, apiError); + if (!apiError.is2xx()) { + return responseBuilder.error(apiError); + } + subs.add( snew ); + logger.info( "num existing subs after = " + subs.size() ); + + fnew.setSubs(subs); + logger.info( "update feed"); + return responseBuilder.success(CREATED.getStatusCode(), snew); + + } + + @PUT + @ApiOperation( value = "return DR_Sub details", + notes = "Update a `DR_Sub` object, selected by subId", + response = DR_Sub.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Sub.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{subId}") + public Response updateDr_Sub(@PathParam("subId") String name, DR_Sub sub) { + + ApiError apiError = new ApiError(); + + try { + checker.required( "subId", name); + checker.required( "feedId", sub.getFeedId()); + checker.required( "dcaeLocationName", sub.getDcaeLocationName()); + + } catch ( RequiredFieldException rfe ) { + logger.debug( rfe.getApiError().toString() ); + return responseBuilder.error(rfe.getApiError()); + } + FeedService feeds = new FeedService(); + Feed fnew = feeds.getFeed(sub.getFeedId(), apiError); + if ( fnew == null ) { + logger.warn( "Specified feed " + sub.getFeedId() + " not known to Bus Controller"); + return responseBuilder.error(apiError); + } + + DR_SubService dr_subService = new DR_SubService(); + sub.setSubId(name); + DR_Sub nsub = dr_subService.updateDr_Sub(sub, apiError); + if ( nsub != null && nsub.isStatusValid() ) { + return responseBuilder.success(nsub); + } + return responseBuilder.error(apiError); + } + + @DELETE + @ApiOperation( value = "return DR_Sub details", + notes = "Delete a `DR_Sub` object, selected by subId", + response = DR_Sub.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Sub.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{subId}") + public Response deleteDr_Sub(@PathParam("subId") String id){ + + ApiError apiError = new ApiError(); + + try { + checker.required( "subId", id); + } catch ( RequiredFieldException rfe ) { + logger.debug( rfe.getApiError().toString() ); + return responseBuilder.error(rfe.getApiError()); + } + DR_SubService dr_subService = new DR_SubService(); + dr_subService.removeDr_Sub(id, apiError); + if (!apiError.is2xx() ) { + return responseBuilder.error(apiError); + } + return responseBuilder.success(Status.NO_CONTENT.getStatusCode(), null ); + } + + @GET + @ApiOperation( value = "return DR_Sub details", + notes = "Retrieve a `DR_Sub` object, selected by subId", + response = DR_Sub.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Sub.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{subId}") + public Response get(@PathParam("subId") String id) { + + ApiError apiError = new ApiError(); + + try { + checker.required( "subId", id); + } catch ( RequiredFieldException rfe ) { + logger.debug( rfe.getApiError().toString() ); + return responseBuilder.error(rfe.getApiError()); + } + DR_SubService dr_subService = new DR_SubService(); + DR_Sub sub = dr_subService.getDr_Sub(id, apiError); + if ( sub != null && sub.isStatusValid() ) { + return responseBuilder.success(sub); + } + return responseBuilder.error(apiError); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResource.java new file mode 100644 index 0000000..89c9b49 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResource.java @@ -0,0 +1,153 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.resources; + + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DcaeLocation; +import org.onap.dmaap.dbcapi.service.DcaeLocationService; + +import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static javax.ws.rs.core.Response.Status.NO_CONTENT; + + +@Path("/dcaeLocations") +@Api( value= "dcaeLocations", description = "an OpenStack tenant purposed for OpenDCAE (i.e. where OpenDCAE components might be deployed)" ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Authorization +public class DcaeLocationResource extends BaseLoggingClass { + private DcaeLocationService locationService = new DcaeLocationService(); + private ResponseBuilder responseBuilder = new ResponseBuilder(); + + @GET + @ApiOperation( value = "return dcaeLocation details", + notes = "Returns array of `dcaeLocation` objects. All objects managed by DMaaP are deployed in some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer* (ecomp or edge).", + response = DcaeLocation.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response getDcaeLocations() { + List<DcaeLocation> locs = locationService.getAllDcaeLocations(); + + GenericEntity<List<DcaeLocation>> list = new GenericEntity<List<DcaeLocation>>(locs) {}; + return responseBuilder.success(list); + } + + @POST + @ApiOperation( value = "return dcaeLocation details", + notes = "Create some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer* (ecomp or edge).", + response = DcaeLocation.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response addDcaeLocation(DcaeLocation location) { + + if ( locationService.getDcaeLocation(location.getDcaeLocationName()) != null ) { + return responseBuilder.error(new ApiError(Status.CONFLICT.getStatusCode(), + "dcaeLocation already exists", "dcaeLocation")); + } + DcaeLocation loc = locationService.addDcaeLocation(location); + return responseBuilder.success(Status.CREATED.getStatusCode(), loc); + } + + @PUT + @ApiOperation( value = "return dcaeLocation details", + notes = "update the openStackAvailabilityZone of a dcaeLocation", + response = DcaeLocation.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{locationName}") + public Response updateDcaeLocation( + @PathParam("locationName") String name, DcaeLocation location) { + + location.setDcaeLocationName(name); + if ( locationService.getDcaeLocation(location.getDcaeLocationName()) == null ) { + return responseBuilder.notFound(); + + } + DcaeLocation loc = locationService.updateDcaeLocation(location); + return responseBuilder.success(Status.CREATED.getStatusCode(), loc ); + } + + @DELETE + @ApiOperation( value = "return dcaeLocation details", notes = "delete a dcaeLocation", response = DcaeLocation.class) + @ApiResponses( value = { + @ApiResponse( code = 204, message = "Success", response = DcaeLocation.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{locationName}") + public Response deleteDcaeLocation( + @PathParam("locationName") String name + ){ + locationService.removeDcaeLocation(name); + return responseBuilder.success(NO_CONTENT.getStatusCode(), null); + } + + @GET + @ApiOperation( value = "return dcaeLocation details", notes = "Returns a specific `dcaeLocation` object with specified tag", response = DcaeLocation.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{locationName}") + public Response getDcaeLocation( + @PathParam("locationName") String name) { + + DcaeLocation loc = locationService.getDcaeLocation( name ); + if ( loc == null ) { + ApiError err = new ApiError(); + + err.setCode(NOT_FOUND.getStatusCode()); + err.setMessage("dcaeLocation does not exist"); + err.setFields("dcaeLocation"); + + return responseBuilder.error(err); + } + + return responseBuilder.success(loc); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DmaapResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DmaapResource.java new file mode 100644 index 0000000..955cab7 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DmaapResource.java @@ -0,0 +1,123 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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========================================================= + */ + +// +// $Id$ + +package org.onap.dmaap.dbcapi.resources; + + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.Dmaap; +import org.onap.dmaap.dbcapi.service.DmaapService; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + + +@Path("/dmaap") +@Api( value= "dmaap", description = "Endpoint for this instance of DMaaP object containing values for this OpenDCAE deployment" ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Authorization +public class DmaapResource extends BaseLoggingClass { + + + private DmaapService dmaapService = new DmaapService(); + private ResponseBuilder responseBuilder = new ResponseBuilder(); + private RequiredChecker checker = new RequiredChecker(); + + @GET + @ApiOperation( value = "return dmaap details", notes = "returns the `dmaap` object, which contains system wide configuration settings", response = Dmaap.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Dmaap.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + + public Response getDmaap(@Context UriInfo uriInfo) { + Dmaap d = dmaapService.getDmaap(); + return responseBuilder.success(d); + } + + @POST + @ApiOperation( value = "return dmaap details", notes = "Create a new DMaaP set system wide configuration settings for the *dcaeEnvironment*. Deprecated with introduction of persistence in 1610.", response = Dmaap.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Dmaap.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response addDmaap( Dmaap obj ) { + + try { + validateRequiredFields(obj); + } catch( RequiredFieldException rfe ) { + return responseBuilder.error(rfe.getApiError()); + } + + Dmaap d = dmaapService.addDmaap(obj); + if ( d == null ) { + return responseBuilder.notFound(); + + } + + return responseBuilder.success(d); + } + + @PUT + @ApiOperation( value = "return dmaap details", notes = "Update system settings for *dcaeEnvironment*.", response = Dmaap.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Dmaap.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response updateDmaap( Dmaap obj ) { + + try { + validateRequiredFields(obj); + } catch( RequiredFieldException rfe ) { + return responseBuilder.error(rfe.getApiError()); + } + + Dmaap d = dmaapService.updateDmaap(obj); + if ( d != null ) { + return responseBuilder.success(d); + } else { + return responseBuilder.notFound(); + } + } + + private void validateRequiredFields(Dmaap obj) throws RequiredFieldException { + checker.required( "dmaapName", obj.getDmaapName(), "^\\S+$" ); //no white space allowed in dmaapName + checker.required( "dmaapProvUrl", obj.getDrProvUrl()); + checker.required( "topicNsRoot", obj.getTopicNsRoot()); + checker.required( "bridgeAdminTopic", obj.getBridgeAdminTopic()); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java new file mode 100644 index 0000000..28bdb00 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java @@ -0,0 +1,259 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DR_Pub; +import org.onap.dmaap.dbcapi.model.Feed; +import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; +import org.onap.dmaap.dbcapi.service.FeedService; + + +@Path("/feeds") +@Api( value= "Feeds", description = "Endpoint for a Data Router Feed" ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Authorization +public class FeedResource extends BaseLoggingClass { + + private ResponseBuilder responseBuilder = new ResponseBuilder(); + private RequiredChecker checker = new RequiredChecker(); + + @GET + @ApiOperation( value = "return Feed details", + notes = "Returns array of `Feed` objects.", + response = Feed.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Feed.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response getFeeds( + @QueryParam("feedName") String feedName, + @QueryParam("version") String version, + @QueryParam("match") String match) { + + FeedService feedService = new FeedService(); + List<Feed> nfeeds = feedService.getAllFeeds( feedName, version, match ); + GenericEntity<List<Feed>> list = new GenericEntity<List<Feed>>(nfeeds) { + }; + return responseBuilder.success(list); + } + + + + @POST + @ApiOperation( value = "return Feed details", + notes = "Create a of `Feed` object.", + response = Feed.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Feed.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response addFeed( + Feed feed, + @QueryParam("useExisting") String useExisting) { + + ApiError apiError = new ApiError(); + + try { + checker.required( "feedName", feed.getFeedName()); + checker.required( "feedVersion", feed.getFeedVersion()); + checker.required( "owner", feed.getOwner()); + checker.required( "asprClassification", feed.getAsprClassification()); + } catch ( RequiredFieldException rfe ) { + logger.debug( rfe.getApiError().toString() ); + return responseBuilder.error(rfe.getApiError()); + } + + + FeedService feedService = new FeedService(); + Feed nfeed = feedService.getFeedByName( feed.getFeedName(), feed.getFeedVersion(), apiError); + if ( nfeed == null ) { + nfeed = feedService.addFeed(feed, apiError); + if ( nfeed != null ) { + return responseBuilder.success(nfeed); + } else { + logger.error( "Unable to create: " + feed.getFeedName() + ":" + feed.getFeedVersion()); + + return responseBuilder.error(apiError); + } + } else if ( nfeed.getStatus() == DmaapObject_Status.DELETED ) { + feed.setFeedId( nfeed.getFeedId()); + nfeed = feedService.updateFeed(feed, apiError); + if ( nfeed != null ) { + return responseBuilder.success(nfeed); + } else { + logger.info( "Unable to update: " + feed.getFeedName() + ":" + feed.getFeedVersion()); + + return responseBuilder.error(apiError); + } + } else if ( (useExisting != null) && ("true".compareToIgnoreCase( useExisting ) == 0)) { + return responseBuilder.success(nfeed); + } + + apiError.setCode(Status.CONFLICT.getStatusCode()); + return responseBuilder.error(apiError); + } + + @PUT + @ApiOperation( value = "return Feed details", + notes = "Update a `Feed` object, specified by id.", + response = Feed.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Feed.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{id}") + public Response updateFeed( + @PathParam("id") String id, + Feed feed) { + + FeedService feedService = new FeedService(); + ApiError apiError = new ApiError(); + + try { + checker.required( "feedId", id); + } catch ( RequiredFieldException rfe ) { + logger.debug( rfe.getApiError().toString() ); + return responseBuilder.error(rfe.getApiError()); + } + + Feed nfeed = feedService.getFeed(id, apiError); + if ( nfeed == null || nfeed.getStatus() == DmaapObject_Status.DELETED ) { + return responseBuilder.notFound(); + } + + // we assume there is no updates allowed for pubs and subs objects via this api... + // need to update any fields supported by PUT but preserve original field values. + nfeed.setSuspended(feed.isSuspended()); + nfeed.setFeedDescription(feed.getFeedDescription()); + nfeed.setFormatUuid(feed.getFormatUuid()); + + nfeed = feedService.updateFeed(nfeed, apiError); + if ( nfeed != null ) { + return responseBuilder.success(nfeed); + } else { + logger.info( "Unable to update: " + feed.getFeedName() + ":" + feed.getFeedVersion()); + + return responseBuilder.error(apiError); + } + } + + @DELETE + @ApiOperation( value = "return Feed details", + notes = "Delete a `Feed` object, specified by id.", + response = Feed.class) + @ApiResponses( value = { + @ApiResponse( code = 204, message = "Success", response = Feed.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{id}") + public Response deleteFeed(@PathParam("id") String id){ + ApiError apiError = new ApiError(); + + logger.debug( "Entry: DELETE " + id); + FeedService feedService = new FeedService(); + Feed nfeed = feedService.getFeed(id, apiError); + if ( nfeed == null ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + return responseBuilder.error(apiError); + } + nfeed = feedService.removeFeed(nfeed, apiError); + if ( nfeed == null || nfeed.getStatus() == DmaapObject_Status.DELETED ) { + return responseBuilder.success(Status.NO_CONTENT.getStatusCode(), null); + } + logger.info( "Unable to delete: " + id + ":" + nfeed.getFeedVersion()); + + return responseBuilder.error(apiError); + } + + @GET + @ApiOperation( value = "return Feed details", + notes = "Retrieve a `Feed` object, specified by id.", + response = Feed.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{id}") + public Response getFeed(@PathParam("id") String id) { + ApiError apiError = new ApiError(); + + FeedService feedService = new FeedService(); + Feed nfeed = feedService.getFeed(id, apiError); + if ( nfeed == null ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + return responseBuilder.error(apiError); + } + return responseBuilder.success(nfeed); + } + + @PUT + @ApiOperation( value = "sync feeds to existing DR", + notes = "When Bus Controller is deployed after DR, then it is possible" + + "that DR has previous provisioning data that needs to be imported" + + "into Bus Controller.", + response = Feed.class ) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Feed.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path( "/sync") + public Response syncFeeds (@QueryParam("hard") String hardParam) { + ApiError error = new ApiError(); + + FeedService feedService = new FeedService(); + boolean hard = false; + if ( hardParam != null && hardParam.equalsIgnoreCase("true")) { + hard = true; + } + feedService.sync( hard, error ); + if ( error.is2xx()) { + List<Feed> nfeeds = feedService.getAllFeeds(); + GenericEntity<List<Feed>> list = new GenericEntity<List<Feed>>(nfeeds) { + }; + return responseBuilder.success(list); + } + return responseBuilder.error(error); + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/InfoResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/InfoResource.java new file mode 100644 index 0000000..bcb7ed2 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/InfoResource.java @@ -0,0 +1,72 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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========================================================= + */ + +// +// $Id$ + +package org.onap.dmaap.dbcapi.resources; + + + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.Dmaap; + + + +@Path("/info") +@Api( value= "info", description = "Endpoint for this instance of DBCL. Returns health info." ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Authorization +public class InfoResource extends BaseLoggingClass { + + + private ResponseBuilder responseBuilder = new ResponseBuilder(); + + @GET + @ApiOperation( value = "return info details", notes = "returns the `info` object", response = Dmaap.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Dmaap.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + + public Response getInfo(@Context UriInfo uriInfo) { + return responseBuilder.success(204, null); + } + + + + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClientResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClientResource.java new file mode 100644 index 0000000..80ee0a6 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClientResource.java @@ -0,0 +1,208 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.MR_Client; +import org.onap.dmaap.dbcapi.model.MR_Cluster; +import org.onap.dmaap.dbcapi.model.Topic; +import org.onap.dmaap.dbcapi.service.MR_ClientService; +import org.onap.dmaap.dbcapi.service.MR_ClusterService; +import org.onap.dmaap.dbcapi.service.TopicService; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import java.util.List; + +import static javax.ws.rs.core.Response.Status.NO_CONTENT; + + +@Path("/mr_clients") +@Api( value= "MR_Clients", description = "Endpoint for a Message Router Client that implements a Publisher or a Subscriber" ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Authorization +public class MR_ClientResource extends BaseLoggingClass { + + private MR_ClientService mr_clientService = new MR_ClientService(); + private ResponseBuilder responseBuilder = new ResponseBuilder(); + private RequiredChecker checker = new RequiredChecker(); + + @GET + @ApiOperation( value = "return MR_Client details", + notes = "Returns array of `MR_Client` objects.", + response = MR_Client.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = MR_Client.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response getMr_Clients() { + List<MR_Client> clients = mr_clientService.getAllMr_Clients(); + + GenericEntity<List<MR_Client>> list = new GenericEntity<List<MR_Client>>(clients) { + }; + return responseBuilder.success(list); + } + + @POST + @ApiOperation( value = "Associate an MR_Client object to a Topic", + notes = "Create a `MR_Client` object." + + "The `dcaeLocation` attribute is used to match an `MR_Cluster` object with the same value, with the intent of localizing message traffic." + + " In legacy implementation, the `clientRole` is granted appropriate permission in AAF." + + " Newer implementions may instead specify an AAF Identity, which will be added to the appropriate `Topic` role.", + response = MR_Client.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = MR_Client.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response addMr_Client(MR_Client client) { + ApiError apiError = new ApiError(); + + try { + checker.required( "fqtn", client.getFqtn()); + checker.required( "dcaeLocationName", client.getDcaeLocationName()); + String s = client.getClientRole(); + if ( s == null ) { + s = client.getClientIdentity(); + } + checker.required( "clientRole or clientIdentity", s); + checker.required( "action", client.getAction()); + + } catch ( RequiredFieldException rfe ) { + logger.debug( rfe.getApiError().toString() ); + return responseBuilder.error(rfe.getApiError()); + } + MR_ClusterService clusters = new MR_ClusterService(); + + MR_Cluster cluster = clusters.getMr_Cluster(client.getDcaeLocationName(), apiError); + if ( cluster == null ) { + + apiError.setCode(Status.BAD_REQUEST.getStatusCode()); + apiError.setMessage( "MR_Cluster alias not found for dcaeLocation: " + client.getDcaeLocationName()); + apiError.setFields("dcaeLocationName"); + logger.warn(apiError.toString()); + return responseBuilder.error(apiError); + } + + TopicService topics = new TopicService(); + + Topic t = topics.getTopic(client.getFqtn(), apiError); + if ( t == null ) { + return responseBuilder.error(apiError); + } + MR_Client nClient = mr_clientService.addMr_Client(client, t, apiError); + if (apiError.is2xx()) { + t = topics.getTopic(client.getFqtn(), apiError); + topics.checkForBridge(t, apiError); + return responseBuilder.success(nClient); + } + else { + return responseBuilder.error(apiError); + } + } + + @PUT + @ApiOperation( value = "Update an MR_Client object", + notes = "Update a `MR_Client` object, specified by clientId", + response = MR_Client.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = MR_Client.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{clientId}") + public Response updateMr_Client(@PathParam("clientId") String clientId, MR_Client client) { + ApiError apiError = new ApiError(); + + try { + checker.required( "fqtn", client.getFqtn()); + checker.required( "dcaeLocationName", client.getDcaeLocationName()); + checker.required( "clientRole", client.getClientRole()); + checker.required( "action", client.getAction()); + + } catch ( RequiredFieldException rfe ) { + logger.debug( rfe.getApiError().toString() ); + return responseBuilder.error(rfe.getApiError()); + } + client.setMrClientId(clientId); + MR_Client nClient = mr_clientService.updateMr_Client(client, apiError); + if (apiError.is2xx()) { + return Response.ok(nClient) + .build(); + } + return Response.status(apiError.getCode()) + .entity(apiError) + .build(); + } + + @DELETE + @ApiOperation( value = "Delete an MR_Client object", + notes = "Delete a `MR_Client` object, specified by clientId", + response = MR_Client.class) + @ApiResponses( value = { + @ApiResponse( code = 204, message = "Success", response = MR_Client.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{subId}") + public Response deleteMr_Client(@PathParam("subId") String id){ + ApiError apiError = new ApiError(); + + mr_clientService.removeMr_Client(id, true, apiError); + if (apiError.is2xx()) { + return responseBuilder.success(NO_CONTENT.getStatusCode(), null); + } + + return responseBuilder.error(apiError); + } + + @GET + @ApiOperation( value = "return MR_Client details", + notes = "Retrieve a `MR_Client` object, specified by clientId", + response = MR_Client.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = MR_Client.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{subId}") + public Response getMr_Client(@PathParam("subId") String id) { + ApiError apiError = new ApiError(); + + MR_Client nClient = mr_clientService.getMr_Client(id, apiError); + if (apiError.is2xx()) { + return responseBuilder.success(nClient); + } + return responseBuilder.error(apiError); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResource.java new file mode 100644 index 0000000..0a361ff --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResource.java @@ -0,0 +1,174 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.MR_Cluster; +import org.onap.dmaap.dbcapi.service.MR_ClusterService; + + +@Path("/mr_clusters") +@Api( value= "MR_Clusters", description = "Endpoint for a Message Router servers in a Cluster configuration" ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Authorization +public class MR_ClusterResource extends BaseLoggingClass { + + private MR_ClusterService mr_clusterService = new MR_ClusterService(); + private ResponseBuilder responseBuilder = new ResponseBuilder(); + private RequiredChecker checker = new RequiredChecker(); + + @GET + @ApiOperation( value = "return MR_Cluster details", + notes = "Returns array of `MR_Cluster` objects.", + response = MR_Cluster.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response getMr_Clusters() { + List<MR_Cluster> clusters = mr_clusterService.getAllMr_Clusters(); + + GenericEntity<List<MR_Cluster>> list = new GenericEntity<List<MR_Cluster>>(clusters) { + }; + return responseBuilder.success(list); + } + + @POST + @ApiOperation( value = "return MR_Cluster details", + notes = "Create an `MR_Cluster` object.", + response = MR_Cluster.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response addMr_Cluster(MR_Cluster cluster) { + ApiError apiError = new ApiError(); + + try { + checker.required( "dcaeLocationName", cluster.getDcaeLocationName()); + checker.required( "fqdn", cluster.getFqdn()); + } catch( RequiredFieldException rfe ) { + return responseBuilder.error(rfe.getApiError()); + } + MR_Cluster mrc = mr_clusterService.addMr_Cluster(cluster, apiError); + if ( mrc != null && mrc.isStatusValid() ) { + return responseBuilder.success(Status.CREATED.getStatusCode(), mrc); + } + return responseBuilder.error(apiError); + + } + + @PUT + @ApiOperation( value = "return MR_Cluster details", + notes = "Update an `MR_Cluster` object, specified by clusterId.", + response = MR_Cluster.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{clusterId}") + public Response updateMr_Cluster(@PathParam("clusterId") String clusterId, MR_Cluster cluster) { + ApiError apiError = new ApiError(); + + try { + checker.required( "fqdn", clusterId); + checker.required( "dcaeLocationName", cluster.getDcaeLocationName()); + } catch( RequiredFieldException rfe ) { + return responseBuilder.error(rfe.getApiError()); + } + cluster.setDcaeLocationName(clusterId); + MR_Cluster mrc = mr_clusterService.updateMr_Cluster(cluster, apiError); + if ( mrc != null && mrc.isStatusValid() ) { + return responseBuilder.success(Status.CREATED.getStatusCode(), mrc); + } + return responseBuilder.error(apiError); + } + + @DELETE + @ApiOperation( value = "return MR_Cluster details", + notes = "Delete an `MR_Cluster` object, specified by clusterId.", + response = MR_Cluster.class) + @ApiResponses( value = { + @ApiResponse( code = 204, message = "Success", response = MR_Cluster.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{clusterId}") + public Response deleteMr_Cluster(@PathParam("clusterId") String id){ + ApiError apiError = new ApiError(); + + try { + checker.required( "fqdn", id); + } catch( RequiredFieldException rfe ) { + return responseBuilder.error(rfe.getApiError()); + } + mr_clusterService.removeMr_Cluster(id, apiError); + if (apiError.is2xx()) { + return responseBuilder.success(Status.NO_CONTENT.getStatusCode(), null); + } + return responseBuilder.error(apiError); + } + + @GET + @ApiOperation( value = "return MR_Cluster details", + notes = "Retrieve an `MR_Cluster` object, specified by clusterId.", + response = MR_Cluster.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{clusterId}") + public Response getMR_Cluster(@PathParam("clusterId") String id) { + ApiError apiError = new ApiError(); + + try { + checker.required( "dcaeLocationName", id); + } catch( RequiredFieldException rfe ) { + return responseBuilder.error(rfe.getApiError()); + } + MR_Cluster mrc = mr_clusterService.getMr_Cluster(id, apiError); + if ( mrc != null && mrc.isStatusValid() ) { + return responseBuilder.success(Status.CREATED.getStatusCode(), mrc); + } + return responseBuilder.error(apiError); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequestTimeLogFilter.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequestTimeLogFilter.java new file mode 100644 index 0000000..b2b98b6 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequestTimeLogFilter.java @@ -0,0 +1,55 @@ +/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia 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.dmaap.dbcapi.resources;
+
+import com.att.eelf.configuration.EELFLogger;
+import java.time.Clock;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+
+public class RequestTimeLogFilter extends BaseLoggingClass implements ContainerRequestFilter, ContainerResponseFilter {
+
+ private final EELFLogger log;
+ private Clock clock;
+
+ public RequestTimeLogFilter() {
+ this(auditLogger, Clock.systemDefaultZone());
+ }
+
+ RequestTimeLogFilter(EELFLogger logger, Clock clock) {
+ this.log = logger;
+ this.clock = clock;
+ }
+
+ @Override
+ public void filter(ContainerRequestContext requestContext) {
+ requestContext.setProperty("start", clock.millis());
+ }
+
+ @Override
+ public void filter(ContainerRequestContext requestContext, ContainerResponseContext containerResponseContext) {
+ long startTime = (long) requestContext.getProperty("start");
+ long elapsedTime = clock.millis() - startTime;
+ log.info("Request took {} ms", elapsedTime);
+ }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredChecker.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredChecker.java new file mode 100644 index 0000000..52646aa --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredChecker.java @@ -0,0 +1,52 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2019 NOKIA 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.dmaap.dbcapi.resources; + +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.onap.dmaap.dbcapi.model.ApiError; + +public class RequiredChecker { + + public void required(String name, Object val) throws RequiredFieldException { + if (val == null) { + throw new RequiredFieldException(new ApiError(BAD_REQUEST.getStatusCode(), + "missing required field", name)); + } + } + + public void required(String name, String val, String expr) throws RequiredFieldException { + + required(name, val); + + if (expr != null && !expr.isEmpty()) { + Pattern pattern = Pattern.compile(expr); + Matcher matcher = pattern.matcher(val); + if (!matcher.find()) { + throw new RequiredFieldException(new ApiError(BAD_REQUEST.getStatusCode(), + "value '" + val + "' violates regexp check '" + expr + "'", name)); + } + } + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredFieldException.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredFieldException.java new file mode 100644 index 0000000..2968d18 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredFieldException.java @@ -0,0 +1,46 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.resources; + +import org.onap.dmaap.dbcapi.model.ApiError; + +public class RequiredFieldException extends Exception { + + private static final long serialVersionUID = 2L; + + private final ApiError apiError; + + public RequiredFieldException(ApiError apiError) { + super(); + this.apiError = apiError; + } + + public ApiError getApiError() { + return apiError; + } + + @Override + public String toString() { + return "RequiredFieldException{" + + "apiError=" + apiError + + '}'; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/ResponseBuilder.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/ResponseBuilder.java new file mode 100644 index 0000000..044e7c4 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/ResponseBuilder.java @@ -0,0 +1,84 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2019 NOKIA 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.dmaap.dbcapi.resources; + +import static com.att.eelf.configuration.Configuration.MDC_RESPONSE_CODE; +import static com.att.eelf.configuration.Configuration.MDC_RESPONSE_DESC; +import static com.att.eelf.configuration.Configuration.MDC_STATUS_CODE; +import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static javax.ws.rs.core.Response.Status.SERVICE_UNAVAILABLE; +import static javax.ws.rs.core.Response.Status.UNAUTHORIZED; + +import javax.ws.rs.core.Response; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.slf4j.MDC; + +public class ResponseBuilder extends BaseLoggingClass { + + Response success(Object d) { + return buildSuccessResponse(d, Response.Status.OK.getStatusCode()); + } + + Response success(int code, Object d) { + return buildSuccessResponse(d, code); + } + + Response error(ApiError err) { + return buildErrResponse(err); + } + + Response unauthorized(String msg) { + return buildErrResponse(new ApiError(UNAUTHORIZED.getStatusCode(), msg, "Authorization")); + } + + Response unavailable() { + return buildErrResponse(new ApiError(SERVICE_UNAVAILABLE.getStatusCode(), + "Request is unavailable due to unexpected condition")); + } + + Response notFound() { + return buildErrResponse(new ApiError(NOT_FOUND.getStatusCode(),"Requested object not found")); + } + + private Response buildSuccessResponse(Object d, int code) { + MDC.put(MDC_STATUS_CODE, "COMPLETE"); + MDC.put(MDC_RESPONSE_DESC, ""); + return buildResponse(d, code); + } + + private Response buildErrResponse(ApiError err) { + MDC.put(MDC_STATUS_CODE, "ERROR"); + MDC.put(MDC_RESPONSE_DESC, err.getMessage()); + + return buildResponse(err, err.getCode()); + } + + private Response buildResponse(Object obj, int code) { + MDC.put(MDC_RESPONSE_CODE, String.valueOf(code)); + + auditLogger.auditEvent(""); + return Response.status(code) + .entity(obj) + .build(); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/TopicResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/TopicResource.java new file mode 100644 index 0000000..01926b7 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/TopicResource.java @@ -0,0 +1,218 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.ReplicationType; +import org.onap.dmaap.dbcapi.model.FqtnType; +import org.onap.dmaap.dbcapi.model.Topic; +import org.onap.dmaap.dbcapi.service.TopicService; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +import static javax.ws.rs.core.Response.Status.CREATED; + +@Path("/topics") +@Api( value= "topics", description = "Endpoint for retreiving MR Topics" ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Authorization +public class TopicResource extends BaseLoggingClass { + private static FqtnType defaultTopicStyle; + private static String defaultPartitionCount; + private static String defaultReplicationCount; + private TopicService mr_topicService = new TopicService(); + private ResponseBuilder responseBuilder = new ResponseBuilder(); + private RequiredChecker checker = new RequiredChecker(); + static final String UNSUPPORTED_PUT_MSG = "Method /PUT not supported for /topics"; + + public TopicResource() { + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + defaultTopicStyle = FqtnType.Validator( p.getProperty("MR.topicStyle", "FQTN_LEGACY_FORMAT")); + defaultPartitionCount = p.getProperty( "MR.partitionCount", "2"); + defaultReplicationCount = p.getProperty( "MR.replicationCount", "1"); + + logger.info( "Setting defaultTopicStyle=" + defaultTopicStyle ); + } + + @GET + @ApiOperation( value = "return Topic details", + notes = "Returns array of `Topic` objects.", + response = Topic.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Topic.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response getTopics() { + List<Topic> allTopics = mr_topicService.getAllTopics(); + + GenericEntity<List<Topic>> list = new GenericEntity<List<Topic>>(allTopics) { + }; + return responseBuilder.success(list); + + } + + @POST + @ApiOperation( value = "Create a Topic object", + notes = "Create `Topic` object." + + "For convenience, the message body may populate the `clients` array, in which case each entry will be added as an `MR_Client`." + + " Beginning in ONAP Dublin Release, dbcapi will create two AAF Roles by default, one each for the publisher and subscriber per topic." + + " MR_Clients can then specify an AAF Identity to be added to the appropriate default Role, avoiding the need to create Role(s) in advance.", + response = Topic.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Topic.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + public Response addTopic(Topic topic, @QueryParam("useExisting") String useExisting) { + logger.info( "addTopic request: " + topic + " useExisting=" + useExisting ); + ApiError apiError = new ApiError(); + + try { + checker.required( "topicName", topic.getTopicName(), "^\\S+$" ); //no white space allowed in topicName + checker.required( "topicDescription", topic.getTopicDescription()); + checker.required( "owner", topic.getOwner()); + } catch( RequiredFieldException rfe ) { + logger.error("Error", rfe.getApiError()); + return responseBuilder.error(rfe.getApiError()); + } + + ReplicationType t = topic.getReplicationCase(); + if ( t == null || t == ReplicationType.REPLICATION_NOT_SPECIFIED ) { + topic.setReplicationCase( mr_topicService.reviewTopic(topic)); + } + FqtnType ft = topic.getFqtnStyle(); + if ( ft == null || ft == FqtnType.FQTN_NOT_SPECIFIED ) { + logger.info( "setting defaultTopicStyle=" + defaultTopicStyle + " for topic " + topic.getTopicName() ); + topic.setFqtnStyle( defaultTopicStyle ); + } + String pc = topic.getPartitionCount(); + if ( pc == null ) { + topic.setPartitionCount(defaultPartitionCount); + } + String rc = topic.getReplicationCount(); + if ( rc == null ) { + topic.setReplicationCount(defaultReplicationCount); + } + topic.setLastMod(); + Boolean flag = false; + if (useExisting != null) { + flag = "true".compareToIgnoreCase( useExisting ) == 0; + } + + Topic mrc = mr_topicService.addTopic(topic, apiError, flag); + if ( mrc != null && apiError.is2xx() ) { + return responseBuilder.success(CREATED.getStatusCode(), mrc); + } + return responseBuilder.error(apiError); + } + + @PUT + @ApiOperation( value = "return Topic details", + notes = "Update a `Topic` object, identified by topicId", + response = Topic.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Topic.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{topicId}") + public Response updateTopic(@PathParam("topicId") String topicId) { + ApiError apiError = new ApiError(); + + apiError.setCode(Status.BAD_REQUEST.getStatusCode()); + apiError.setMessage(UNSUPPORTED_PUT_MSG); + + return responseBuilder.error(apiError); + } + + @DELETE + @ApiOperation( value = "return Topic details", + notes = "Delete a `Topic` object, identified by topicId", + response = Topic.class) + @ApiResponses( value = { + @ApiResponse( code = 204, message = "Success", response = Topic.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{topicId}") + public Response deleteTopic(@PathParam("topicId") String id){ + ApiError apiError = new ApiError(); + + try { + checker.required( "fqtn", id); + } catch( RequiredFieldException rfe ) { + logger.error("Error", rfe.getApiError()); + return responseBuilder.error(rfe.getApiError()); + } + + mr_topicService.removeTopic(id, apiError); + if (apiError.is2xx()) { + return responseBuilder.success(Status.NO_CONTENT.getStatusCode(), null); + } + return responseBuilder.error(apiError); + } + + + @GET + @ApiOperation( value = "return Topic details", + notes = "Retrieve a `Topic` object, identified by topicId", + response = Topic.class) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Topic.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path("/{topicId}") + public Response getTopic(@PathParam("topicId") String id) { + logger.info("Entry: /GET " + id); + ApiError apiError = new ApiError(); + + try { + checker.required( "topicName", id, "^\\S+$" ); //no white space allowed in topicName + } catch( RequiredFieldException rfe ) { + logger.error("Error", rfe.getApiError()); + return responseBuilder.error(rfe.getApiError()); + } + Topic mrc = mr_topicService.getTopic(id, apiError); + if ( mrc == null ) { + return responseBuilder.error(apiError); + } + return responseBuilder.success(mrc); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/ApplicationConfig.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/ApplicationConfig.java new file mode 100644 index 0000000..2244b73 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/ApplicationConfig.java @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.server; + +import org.glassfish.jersey.server.ResourceConfig; +import org.onap.dmaap.dbcapi.resources.RequestTimeLogFilter; +import org.onap.dmaap.dbcapi.resources.AuthorizationFilter; + +public class ApplicationConfig extends ResourceConfig { + + /* + * Register JAX-RS application components + */ + public ApplicationConfig() { + + register(AuthorizationFilter.class). + register(RequestTimeLogFilter.class); + + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CadiCertificateManager.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CadiCertificateManager.java new file mode 100644 index 0000000..1da2bc4 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CadiCertificateManager.java @@ -0,0 +1,61 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2020 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.dmaap.dbcapi.server; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; + +import org.onap.aaf.cadi.PropAccess; + +public class CadiCertificateManager extends CertificateManager { + private PropAccess propAccess; + + CadiCertificateManager( Properties properties ) { + String cadiPropsFile = properties.getProperty("cadi.properties", "etc/org.onap.dmaa-bc.props"); + logger.info( "using cadi properties in ", cadiPropsFile); + + propAccess = new PropAccess(); + ready = true; + try { + propAccess.load( new FileInputStream( cadiPropsFile )); + } catch ( IOException e ) { + logger.error( "Failed to load props file: " + cadiPropsFile + "\n" + e.getMessage()); + ready = false; + } + setKeyStoreType( "jks"); + setKeyStoreFile( propAccess.getProperty("cadi_keystore") ); + setKeyStorePassword( decryptPass( propAccess.getProperty("cadi_keystore_password_jks" ) )); + + setTrustStoreType( "jks"); + setTrustStoreFile( propAccess.getProperty("cadi_truststore" ) ); + setTrustStorePassword( decryptPass( propAccess.getProperty("cadi_truststore_password" ) )); + } + + private String decryptPass( String password ) { + String clear = null; + try { + clear = propAccess.decrypt(password, false ); + } catch (IOException e) { + logger.error( "Failed to decrypt " + password + ": " + e.getMessage() ); + } + return clear; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertficateManagerFactory.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertficateManagerFactory.java new file mode 100644 index 0000000..0bffd84 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertficateManagerFactory.java @@ -0,0 +1,51 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2020 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.dmaap.dbcapi.server; + + +import java.util.Properties; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +public class CertficateManagerFactory extends BaseLoggingClass { + private final Properties dmaapConfig; + + public CertficateManagerFactory() { + this((DmaapConfig) DmaapConfig.getConfig()); + } + + CertficateManagerFactory(Properties params) { + this.dmaapConfig = params; + } + + public CertificateManager initCertificateManager() { + boolean useCadi = "cadi".equalsIgnoreCase(dmaapConfig.getProperty("CertificateManagement", "legacy")); + logger.info("CertificateManagerFactory: useCadi=" + useCadi); + + if ( useCadi ) { + return new CadiCertificateManager( dmaapConfig ); + } + return new LegacyCertificateManager( dmaapConfig ); + } + + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertificateManager.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertificateManager.java new file mode 100644 index 0000000..2772b92 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertificateManager.java @@ -0,0 +1,104 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2020 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.dmaap.dbcapi.server; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; + +public abstract class CertificateManager extends BaseLoggingClass{ + + class cmAttribute { + private String type; + private String file; + private String password; + + private String getType() { + return type; + } + private void setType(String certificateType) { + this.type = certificateType; + } + private String getFile() { + return file; + } + private void setFile(String keyStoreFile) { + this.file = keyStoreFile; + } + private void setPassword( String pwd ) { + this.password = pwd; + } + private String getPassword() { + return password; + } + } + + private cmAttribute keyStore; + private cmAttribute trustStore; + protected boolean ready; + + CertificateManager() { + keyStore = new cmAttribute(); + trustStore = new cmAttribute(); + ready = false; + } + + public boolean isReady() { + return ready; + } + + public String getKeyStoreType() { + return keyStore.getType(); + } + public void setKeyStoreType(String certificateType) { + this.keyStore.setType( certificateType) ; + } + public String getKeyStoreFile() { + return keyStore.getFile(); + } + public void setKeyStoreFile(String keyStoreFile) { + this.keyStore.setFile(keyStoreFile); + } + + public String getKeyStorePassword() { + return keyStore.getPassword(); + } + public void setKeyStorePassword(String keyStorePassword) { + this.keyStore.setPassword(keyStorePassword); + } + public String getTrustStoreType() { + return trustStore.getType(); + } + public void setTrustStoreType( String type ) { + this.trustStore.setType(type); + } + public String getTrustStoreFile() { + return trustStore.getFile(); + } + public void setTrustStoreFile(String trustStoreFile) { + this.trustStore.setFile(trustStoreFile); + } + public String getTrustStorePassword() { + return trustStore.getPassword(); + } + public void setTrustStorePassword(String trustStorePassword) { + this.trustStore.setPassword(trustStorePassword); + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/JettyServer.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/JettyServer.java new file mode 100644 index 0000000..52d7570 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/JettyServer.java @@ -0,0 +1,174 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.server; + +import com.google.common.collect.Sets; +import java.util.Properties; +import javax.servlet.DispatcherType; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +/** + * A Jetty server which supports: + * - http and https (simultaneously for dev env) + * - REST API context + * - static html pages (for documentation). + */ +public class JettyServer extends BaseLoggingClass { + + private static final CertificateManager certificateManager = + new CertficateManagerFactory(DmaapConfig.getConfig()).initCertificateManager(); + private final Server server; + + + public Server getServer() { + return server; + } + + public static CertificateManager getCertificateManager() { + return certificateManager; + } + + public JettyServer(Properties params) { + + server = new Server(); + int httpPort = Integer.parseInt(params.getProperty("IntHttpPort", "80")); + int sslPort = Integer.parseInt(params.getProperty("IntHttpsPort", "443")); + boolean allowHttp = Boolean.parseBoolean(params.getProperty("HttpAllowed", "false")); + serverLogger.info("port params: http=" + httpPort + " https=" + sslPort); + serverLogger.info("allowHttp=" + allowHttp); + + // HTTP Server + HttpConfiguration httpConfig = new HttpConfiguration(); + httpConfig.setSecureScheme("https"); + httpConfig.setSecurePort(sslPort); + httpConfig.setOutputBufferSize(32768); + + try (ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfig))) { + httpConnector.setPort(httpPort); + httpConnector.setIdleTimeout(30000); + + // HTTPS Server + HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); + httpsConfig.addCustomizer(new SecureRequestCustomizer()); + SslContextFactory sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setWantClientAuth(true); + + if ( ! certificateManager.isReady()) { + serverLogger.error("CertificateManager is not ready. NOT starting https!"); + } else { + setUpKeystore(sslContextFactory); + setUpTrustStore(sslContextFactory); + + + if (sslPort != 0) { + try (ServerConnector sslConnector = new ServerConnector(server, + new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), + new HttpConnectionFactory(httpsConfig))) { + sslConnector.setPort(sslPort); + server.addConnector(sslConnector); + serverLogger.info("Starting sslConnector on port " + sslPort + " for https"); + } + } else { + serverLogger.info("NOT starting sslConnector because InHttpsPort param is " + sslPort ); + } + } + if (allowHttp) { + serverLogger.info("Starting httpConnector on port " + httpPort); + server.addConnector(httpConnector); + } else { + serverLogger.info("NOT starting httpConnector because HttpAllowed param is " + allowHttp); + } + } + + // Set context for servlet. This is shared for http and https + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + server.setHandler(context); + + ServletHolder jerseyServlet = context + .addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/webapi/*"); + jerseyServlet.setInitOrder(1); + jerseyServlet.setInitParameter("jersey.config.server.provider.packages", "org.onap.dmaap.dbcapi.resources"); + jerseyServlet.setInitParameter("javax.ws.rs.Application", "org.onap.dmaap.dbcapi.server.ApplicationConfig"); + + // also serve up some static pages... + ServletHolder staticServlet = context.addServlet(DefaultServlet.class, "/*"); + staticServlet.setInitParameter("resourceBase", "www"); + staticServlet.setInitParameter("pathInfoOnly", "true"); + + registerAuthFilters(context); + + try { + + serverLogger.info("Starting jetty server"); + String unitTest = params.getProperty("UnitTest", "No"); + serverLogger.info("UnitTest=" + unitTest); + if (unitTest.equals("No")) { + server.start(); + server.dumpStdErr(); + server.join(); + } + } catch (Exception e) { + errorLogger.error("Exception " + e); + } finally { + server.destroy(); + } + + } + + private void registerAuthFilters(ServletContextHandler context) { + context.addFilter("org.onap.dmaap.dbcapi.resources.AAFAuthenticationFilter", "/webapi/*", + Sets.newEnumSet(Sets.newHashSet(DispatcherType.FORWARD, DispatcherType.REQUEST), DispatcherType.class)); + context.addFilter("org.onap.dmaap.dbcapi.resources.AAFAuthorizationFilter", "/webapi/*", + Sets.newEnumSet(Sets.newHashSet(DispatcherType.FORWARD, DispatcherType.REQUEST), DispatcherType.class)); + } + + private void setUpKeystore(SslContextFactory sslContextFactory) { + String keystore = JettyServer.certificateManager.getKeyStoreFile(); + logger.info("https Server using keystore at " + keystore); + sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStoreType(JettyServer.certificateManager.getKeyStoreType()); + sslContextFactory.setKeyStorePassword(JettyServer.certificateManager.getKeyStorePassword()); + sslContextFactory.setKeyManagerPassword(JettyServer.certificateManager.getKeyStorePassword()); + } + + private void setUpTrustStore(SslContextFactory sslContextFactory) { + String truststore = JettyServer.certificateManager.getTrustStoreFile(); + logger.info("https Server using truststore at " + truststore); + sslContextFactory.setTrustStorePath(truststore); + sslContextFactory.setTrustStoreType(JettyServer.certificateManager.getTrustStoreType()); + sslContextFactory.setTrustStorePassword(JettyServer.certificateManager.getTrustStorePassword()); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/LegacyCertificateManager.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/LegacyCertificateManager.java new file mode 100644 index 0000000..bd54003 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/LegacyCertificateManager.java @@ -0,0 +1,39 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2020 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.dmaap.dbcapi.server; + +import java.util.Properties; + +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +public class LegacyCertificateManager extends CertificateManager { + + public LegacyCertificateManager(Properties properties ) { + setKeyStoreType( properties.getProperty("KeyStoreType", "jks") ); + setKeyStoreFile( properties.getProperty("KeyStoreFile", "etc/keystore") ); + setKeyStorePassword( properties.getProperty("KeyStorePassword", "changeit") ); + + setTrustStoreFile( properties.getProperty("TrustStoreFile", "etc/org.onap.dmaap-bc.trust.jks") ); + setTrustStoreType( properties.getProperty("TrustStoreType", "jks") ); + setTrustStorePassword( properties.getProperty("TrustStorePassword", "changeit") ); + ready = true; + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/Main.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/Main.java new file mode 100644 index 0000000..942fe6c --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/Main.java @@ -0,0 +1,93 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.server; +import static com.att.eelf.configuration.Configuration.MDC_ALERT_SEVERITY; +import static com.att.eelf.configuration.Configuration.MDC_INSTANCE_UUID; +import static com.att.eelf.configuration.Configuration.MDC_SERVER_FQDN; +import static com.att.eelf.configuration.Configuration.MDC_SERVER_IP_ADDRESS; +import static com.att.eelf.configuration.Configuration.MDC_SERVICE_INSTANCE_ID; +import static com.att.eelf.configuration.Configuration.MDC_TARGET_ENTITY; + +import java.net.InetAddress; +import java.util.Properties; +import java.util.UUID; +import org.onap.dmaap.dbcapi.authentication.ApiPerms; +import org.onap.dmaap.dbcapi.authentication.ApiPolicy; +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.Dmaap; +import org.onap.dmaap.dbcapi.util.DmaapConfig; +import org.onap.dmaap.dbcapi.util.Singleton; +import org.slf4j.MDC; + +public class Main extends BaseLoggingClass { + + private static String provFQDN; + public static String getProvFQDN() { + return provFQDN; + } + public void setProvFQDN(String provFQDN) { + Main.provFQDN = provFQDN; + } + private Main() { + } + public static void main(String[] args) { + (new Main()).main(); + } + + private void main() { + + MDC.clear(); + + MDC.put(MDC_SERVICE_INSTANCE_ID, ""); + try { + MDC.put(MDC_SERVER_FQDN, InetAddress.getLocalHost().getHostName()); + MDC.put(MDC_SERVER_IP_ADDRESS, InetAddress.getLocalHost().getHostAddress()); + } catch (Exception e) { + errorLogger.error("Error while getting hostname or address", e); + } + MDC.put(MDC_INSTANCE_UUID, UUID.randomUUID().toString()); + MDC.put(MDC_ALERT_SEVERITY, "0"); + MDC.put(MDC_TARGET_ENTITY, "DCAE"); + + appLogger.info("Started."); + Properties parameters = DmaapConfig.getConfig(); + setProvFQDN( parameters.getProperty("ProvFQDN", "ProvFQDN.notset.com")); + + // for fresh installs, we may come up with no dmaap name so need to have a way for Controller to talk to us + Singleton<Dmaap> dmaapholder = DatabaseClass.getDmaap(); + String name = dmaapholder.get().getDmaapName(); + ApiPolicy apiPolicy = new ApiPolicy(); + if ( apiPolicy.isPermissionClassSet() && (name == null || name.isEmpty())) { + ApiPerms p = new ApiPerms(); + p.setBootMap(); + } + + try { + new JettyServer(parameters); + } catch (Exception e) { + errorLogger.error("Unable to start Jetty " + DmaapConfig.getConfigFileName(), e); + System.exit(1); + } + + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafPermissionService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafPermissionService.java new file mode 100644 index 0000000..1997633 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafPermissionService.java @@ -0,0 +1,122 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2019 Nokia 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.dmaap.dbcapi.service; + +import org.onap.dmaap.dbcapi.aaf.AafService; +import org.onap.dmaap.dbcapi.aaf.AafUserRole; +import org.onap.dmaap.dbcapi.aaf.DmaapGrant; +import org.onap.dmaap.dbcapi.aaf.DmaapPerm; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; +import org.onap.dmaap.dbcapi.model.MR_Client; + +import static java.lang.String.format; + +class AafPermissionService extends BaseLoggingClass { + + private static final String INSTANCE_PREFIX = ":topic."; + private final AafService aafService; + private final DmaapService dmaapService; + + AafPermissionService(AafService aafService, DmaapService dmaapService) { + this.aafService = aafService; + this.dmaapService = dmaapService; + } + + ApiError assignClientToRole(MR_Client client, String role) { + AafUserRole ur = new AafUserRole(client.getClientIdentity(), role); + int rc = aafService.addUserRole(ur); + if (rc != 201 && rc != 409) { + return handleErrorStatus(rc, client, + format("Failed to add user %s to role %s", client.getClientIdentity(), role)); + } + return handleOkStatus(client); + } + + ApiError grantClientRolePerms(MR_Client client) { + return forEachClientAction(client, this::grantPermForClientRole); + } + + private ApiError forEachClientAction(MR_Client client, PermissionUpdate permissionUpdate) { + try { + String instance = INSTANCE_PREFIX + client.getFqtn(); + + for (String action : client.getAction()) { + permissionUpdate.execute(client.getClientRole(), instance, action); + } + + } catch (PermissionServiceException e) { + return handleErrorStatus(e.getCode(), client, e.getMessage()); + } + return handleOkStatus(client); + } + + private void grantPermForClientRole(String clientRole, String instance, String action) throws PermissionServiceException { + if (clientRole != null) { + DmaapPerm perm = new DmaapPerm(dmaapService.getTopicPerm(), instance, action); + DmaapGrant g = new DmaapGrant(perm, clientRole); + int code = aafService.addGrant(g); + if (code != 201 && code != 409) { + throw new PermissionServiceException(code, format("Grant of %s|%s|%s failed for %s", + dmaapService.getTopicPerm(), instance, action, clientRole)); + } + } else { + logger.warn("No Grant of {}|{}|{} because role is null ", dmaapService.getTopicPerm(), instance, action); + } + } + + private ApiError handleErrorStatus(int code, MR_Client client, String message) { + ApiError apiError = new ApiError(code, message); + client.setStatus(DmaapObject_Status.INVALID); + logger.warn(apiError.getMessage()); + return apiError; + } + + private ApiError handleOkStatus(MR_Client client) { + client.setStatus(DmaapObject_Status.VALID); + return new ApiError(200, "OK"); + } + + private class PermissionServiceException extends Exception { + private final int code; + private final String message; + + PermissionServiceException(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + @Override + public String getMessage() { + return message; + } + } + + @FunctionalInterface + interface PermissionUpdate { + void execute(String clientRole, String instance, String action) throws PermissionServiceException; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafTopicSetupService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafTopicSetupService.java new file mode 100644 index 0000000..16ffa08 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafTopicSetupService.java @@ -0,0 +1,218 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2019 Nokia 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.dmaap.dbcapi.service; + +import org.onap.dmaap.dbcapi.aaf.AafNamespace; +import org.onap.dmaap.dbcapi.aaf.AafRole; +import org.onap.dmaap.dbcapi.aaf.AafService; +import org.onap.dmaap.dbcapi.aaf.DmaapGrant; +import org.onap.dmaap.dbcapi.aaf.DmaapPerm; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.Topic; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +import static java.lang.String.format; +import static org.apache.commons.lang3.StringUtils.isNumeric; + +class AafTopicSetupService extends BaseLoggingClass { + + private final AafService aafService; + private final DmaapService dmaapService; + private final DmaapConfig dmaapConfig; + + AafTopicSetupService(AafService aafService, DmaapService dmaapService, DmaapConfig dmaapConfig) { + this.aafService = aafService; + this.dmaapService = dmaapService; + this.dmaapConfig = dmaapConfig; + } + + ApiError aafTopicSetup(Topic topic) { + + try { + String instance = ":topic." + topic.getFqtn(); + String topicPerm = dmaapService.getTopicPerm(); + DmaapPerm pubPerm = createPermission(topicPerm, instance, "pub"); + DmaapPerm subPerm = createPermission(topicPerm, instance, "sub"); + DmaapPerm viewPerm = createPermission(topicPerm, instance, "view"); + + // creating Topic Roles was not an original feature. + // For backwards compatibility, only do this if the feature is enabled. + // Also, if the namespace of the topic is a foreign namespace, (i.e. not the same as our root ns) + // then we likely don't have permission to create sub-ns and Roles so don't try. + if (createTopicRoles() && topic.getFqtn().startsWith(getTopicsNsRoot())) { + createNamespace(topic); + + AafRole pubRole = createRole(topic, "publisher"); + topic.setPublisherRole(pubRole.getFullyQualifiedRole()); + + AafRole subRole = createRole(topic, "subscriber"); + topic.setSubscriberRole(subRole.getFullyQualifiedRole()); + + grantPermToRole(pubRole, pubPerm); + grantPermToRole(pubRole, viewPerm); + + grantPermToRole(subRole, subPerm); + grantPermToRole(subRole, viewPerm); + } + + } catch (TopicSetupException ex) { + logger.error("Exception in topic setup {}", ex.getMessage()); + return new ApiError(ex.getCode(), ex.getMessage(), ex.getFields()); + } + return okStatus(); + } + + ApiError aafTopicCleanup(Topic topic) { + try { + if (performCleanup()) { + String instance = ":topic." + topic.getFqtn(); + String topicPerm = dmaapService.getTopicPerm(); + removePermission(topicPerm, instance, "pub"); + removePermission(topicPerm, instance, "sub"); + removePermission(topicPerm, instance, "view"); + + if (createTopicRoles() && topic.getFqtn().startsWith(getTopicsNsRoot())) { + removeNamespace(topic); + } + } + } catch (TopicSetupException ex) { + return new ApiError(ex.getCode(), ex.getMessage(), ex.getFields()); + } + return okStatus(); + } + + private String getTopicsNsRoot() throws TopicSetupException { + String nsr = dmaapService.getDmaap().getTopicNsRoot(); + if (nsr == null) { + throw new TopicSetupException(500, + "Unable to establish AAF namespace root: (check /dmaap object)", "topicNsRoot"); + } + return nsr; + } + + private DmaapPerm createPermission(String permission, String instance, String action) throws TopicSetupException { + DmaapPerm perm = new DmaapPerm(permission, instance, action); + int rc = aafService.addPerm(perm); + if (rc != 201 && rc != 409) { + throw new TopicSetupException(500, + format("Unexpected response from AAF: %d permission=%s instance=%s action=%s", + rc, perm, instance, action)); + } + return perm; + } + + private void grantPermToRole(AafRole aafRole, DmaapPerm perm) throws TopicSetupException { + DmaapGrant g = new DmaapGrant(perm, aafRole.getFullyQualifiedRole()); + int rc = aafService.addGrant(g); + if (rc != 201 && rc != 409) { + String message = format("Grant of %s failed for %s", perm.toString(), aafRole.getFullyQualifiedRole()); + logger.warn(message); + throw new TopicSetupException(rc, message); + } + } + + private void createNamespace(Topic topic) throws TopicSetupException { + AafNamespace ns = new AafNamespace(topic.getFqtn(), aafService.getIdentity()); + int rc = aafService.addNamespace(ns); + if (rc != 201 && rc != 409) { + throw new TopicSetupException(500, + format("Unexpected response from AAF: %d namespace=%s identity=%s", + rc, topic.getFqtn(), aafService.getIdentity())); + } + } + + private AafRole createRole(Topic topic, String roleName) throws TopicSetupException { + AafRole role = new AafRole(topic.getFqtn(), roleName); + int rc = aafService.addRole(role); + if (rc != 201 && rc != 409) { + throw new TopicSetupException(500, + format("Unexpected response from AAF: %d topic=%s role=%s", + rc, topic.getFqtn(), roleName)); + } + return role; + } + + private void removePermission(String permission, String instance, String action) throws TopicSetupException { + DmaapPerm perm = new DmaapPerm(permission, instance, action); + int rc = aafService.delPerm(perm, true); + if (rc != 200 && rc != 404) { + throw new TopicSetupException(500, + format("Unexpected response from AAF: %d permission=%s instance=%s action=%s", + rc, perm, instance, action)); + } + } + + private void removeNamespace(Topic topic) throws TopicSetupException { + AafNamespace ns = new AafNamespace(topic.getFqtn(), aafService.getIdentity()); + int rc = aafService.delNamespace(ns, true); + if (rc != 200 && rc != 404) { + throw new TopicSetupException(500, + format("Unexpected response from AAF: %d namespace=%s identity=%s", + rc, topic.getFqtn(), aafService.getIdentity())); + } + } + + private ApiError okStatus() { + return new ApiError(200, "OK"); + } + + private boolean createTopicRoles() { + return "true".equalsIgnoreCase(dmaapConfig.getProperty("aaf.CreateTopicRoles", "true")); + } + + private boolean performCleanup() { + String deleteLevel = dmaapConfig.getProperty("MR.ClientDeleteLevel", "0"); + if (!isNumeric(deleteLevel)) { + return false; + } + return Integer.valueOf(deleteLevel) >= 2; + } + + private class TopicSetupException extends Exception { + + private final int code; + private final String message; + private final String fields; + + TopicSetupException(int code, String message) { + this(code, message, ""); + } + + TopicSetupException(int code, String message, String fields) { + this.code = code; + this.message = message; + this.fields = fields; + } + + public int getCode() { + return code; + } + + @Override + public String getMessage() { + return message; + } + + public String getFields() { + return fields; + } + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/ApiService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/ApiService.java new file mode 100644 index 0000000..ef1e6f4 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/ApiService.java @@ -0,0 +1,159 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.service; + +import static com.att.eelf.configuration.Configuration.MDC_KEY_REQUEST_ID; +import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME; + +import org.onap.dmaap.dbcapi.aaf.DmaapPerm; +import org.onap.dmaap.dbcapi.authentication.ApiPolicy; +import org.onap.dmaap.dbcapi.authentication.AuthenticationErrorException; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.Dmaap; +import org.onap.dmaap.dbcapi.util.DmaapConfig; +import org.onap.dmaap.dbcapi.util.RandomString; +import org.slf4j.MDC; + +public class ApiService extends BaseLoggingClass { + + private String apiNamespace; + private String uri; + private String uriPath; + private String method; + private String authorization; + private String requestId; + private ApiError err; + private ApiPolicy apiPolicy; + private CredentialsParser credentialsParser = new CredentialsParser(); + + public ApiService() { + + err = new ApiError(); + requestId = (new RandomString(10)).nextString(); + + if (apiNamespace == null) { + DmaapConfig p = (DmaapConfig) DmaapConfig.getConfig(); + apiNamespace = p.getProperty("ApiNamespace", "org.openecomp.dmaapBC.api"); + logger.info("config param usePE has been deprecated. Use ApiPermission.Class property instead."); + } + apiPolicy = new ApiPolicy(); + + logger.info("apiNamespace=" + apiNamespace); + } + + public ApiService setAuth(String auth) { + this.authorization = auth; + logger.info("setAuth: authorization={} ", authorization); + return this; + } + + private void setServiceName() { + String svcRequest = new String(this.method + " " + this.uriPath); + MDC.put(MDC_SERVICE_NAME, svcRequest); + } + + public ApiService setHttpMethod(String httpMethod) { + this.method = httpMethod; + logger.info("setHttpMethod: method={} ", method); + setServiceName(); + return this; + } + + public ApiService setUriPath(String uriPath) { + this.uriPath = uriPath; + this.uri = setUriFromPath(uriPath); + logger.info("setUriPath: uriPath={} uri={}", uriPath, uri); + setServiceName(); + return this; + } + + private String setUriFromPath(String uriPath) { + int ch = uriPath.indexOf("/"); + if (ch > 0) { + return ((String) uriPath.subSequence(0, ch)); + } else { + return uriPath; + } + } + + public ApiError getErr() { + return err; + } + + public void checkAuthorization() throws Exception { + + MDC.put(MDC_KEY_REQUEST_ID, requestId); + + logger.info("request: uri={} method={} auth={}", uri, method, authorization); + + if (uri == null || uri.isEmpty()) { + String errmsg = "No URI value provided "; + err.setMessage(errmsg); + logger.info(errmsg); + throw new AuthenticationErrorException(); + } + if (method == null || method.isEmpty()) { + String errmsg = "No method value provided "; + err.setMessage(errmsg); + logger.info(errmsg); + throw new AuthenticationErrorException(); + } + DmaapService dmaapService = new DmaapService(); + Dmaap dmaap = dmaapService.getDmaap(); + String env = dmaap.getDmaapName(); + + // special case during bootstrap of app when DMaaP environment may not be set. + // this allows us to authorize certain APIs used for initialization during this window. + if (env == null || env.isEmpty()) { + env = "boot"; + } + if (!apiPolicy.isPermissionClassSet()) { + return; // skip authorization if not enabled + } + + Credentials credentials = credentialsParser.parse(authorization); + try { + DmaapPerm p = new DmaapPerm(apiNamespace + "." + uri, env, method); + apiPolicy.check(credentials.getId(), credentials.getPwd(), p); + } catch (AuthenticationErrorException ae) { + String errmsg = + "User " + credentials.getId() + " failed authentication/authorization for " + apiNamespace + "." + uriPath + " " + env + + " " + method; + logger.info(errmsg); + err.setMessage(errmsg); + throw ae; + + } + } + + public ApiService setRequestId(String requestId) { + if (requestId == null || requestId.isEmpty()) { + this.requestId = (new RandomString(10)).nextString(); + logger.warn("X-ECOMP-RequestID not set in HTTP Header. Setting RequestId value to: " + this.requestId); + } else { + this.requestId = requestId; + } + MDC.put(MDC_KEY_REQUEST_ID, this.requestId); + return this; + } +} + diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/Credentials.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/Credentials.java new file mode 100644 index 0000000..aee9b4f --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/Credentials.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2019 Nokia 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.dmaap.dbcapi.service; + +public class Credentials { + + private final String id; + private final String pwd; + + Credentials(String id, String pwd) { + this.id = id; + this.pwd = pwd; + } + + static Credentials empty() { + return new Credentials("", ""); + } + + public String getId() { + return id; + } + + public String getPwd() { + return pwd; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/CredentialsParser.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/CredentialsParser.java new file mode 100644 index 0000000..4156d1b --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/CredentialsParser.java @@ -0,0 +1,39 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2019 Nokia 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.dmaap.dbcapi.service; + +import javax.xml.bind.DatatypeConverter; + +class CredentialsParser { + + Credentials parse(String authorizationHeader) { + if (authorizationHeader == null || authorizationHeader.isEmpty()) { + return Credentials.empty(); + } + + String credentials = authorizationHeader.substring("Basic".length()).trim(); + byte[] decoded = DatatypeConverter.parseBase64Binary(credentials); + String decodedString = new String(decoded); + String[] actualCredentials = decodedString.split(":"); + return new Credentials(actualCredentials[0], actualCredentials[1]); + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_NodeService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_NodeService.java new file mode 100644 index 0000000..b29beb9 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_NodeService.java @@ -0,0 +1,273 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dcae + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import javax.ws.rs.core.Response.Status; +import org.onap.dmaap.dbcapi.client.DrProvConnection; +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DR_Node; +import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + + +public class DR_NodeService extends BaseLoggingClass { + private class DrProv { + String currentNodes; + String currentStaticNodes; + + private String getX( String X, ApiError apiError ) { + + logger.info( "templog:getX at" + " 12.10.10" ); + DrProvConnection prov = new DrProvConnection(); + logger.info( "templog:getX at" + " 12.10.12" ); + prov.makeNodesConnection( X ); + logger.info( "templog:getX at" + " 12.10.14" ); + String resp = prov.doGetNodes( apiError ); + logger.info( "templog:getX at" + " 12.10.16" ); + logger.info( "rc=" + apiError.getCode() ); + logger.info( "templog:getX at" + " 12.10.18" ); + return resp; + } + + private void setX( String X, String list, ApiError apiError ) { + DrProvConnection prov = new DrProvConnection(); + prov.makeNodesConnection( X, list ); + prov.doPutNodes( apiError ); + } + + private String removeFromList( String aNode, String aList ) { + String[] nodeList = aList.split("\\|"); + StringBuilder res = new StringBuilder(); + for ( String n: nodeList ) { + logger.info( "compare existing node " + n + " vs " + aNode ); + if ( ! n.equals(aNode)) { + if (res.length() > 0 ) { + res.append( "|" ); + } + res.append(n); + } + } + logger.info( "result=" + res.toString() ); + return res.toString(); + } + + boolean containsNode( String aNode , ApiError apiError ){ + + logger.info( "templog:containsNode at" + " 12.10" ); + //DrProvConnection prov = new DrProvConnection(); + //prov.makeNodesConnection(); + currentNodes = getX( "NODES", apiError ); + logger.info( "templog:containsNode at" + " 12.12" ); + if ( ! apiError.is2xx() || currentNodes == null ) { + logger.info( "templog:containsNode at" + " 12.14" ); + return false; + } + logger.info( "templog:containsNode at" + " 12.16" ); + logger.info( "NODES now=" + currentNodes ); + String[] nodeList = currentNodes.split("\\|"); + logger.info( "templog:containsNode at" + " 12.17" ); + for( String n: nodeList ) { + logger.info( "templog:containsNode at" + " 12.18" ); + logger.info( "compare existing node " + n + " vs " + aNode ); + if ( n.equals(aNode) ) { + return true; + } + } + return false; + } + + void addNode( String aNode, ApiError apiError ) { + + currentNodes = currentNodes + "|" + aNode; + setX( "NODES", currentNodes, apiError ); + + + } + void removeNode( String aNode, ApiError apiError ) { + currentNodes = removeFromList( aNode, currentNodes ); + setX( "NODES", currentNodes, apiError ); + } + + public boolean containsStaticNode(String aNode, ApiError apiError) { + + //DrProvConnection prov = new DrProvConnection(); + //prov.makeNodesConnection(); + currentStaticNodes = getX( "STATIC_ROUTING_NODES", apiError ); + if (! apiError.is2xx() || currentStaticNodes == null ) { + return false; + } + logger.info( "STATIC_ROUTING_NODES now=" + currentNodes ); + String[] nodeList = currentStaticNodes.split("\\|"); + for( String n: nodeList ) { + logger.info( "compare existing node " + n + " vs " + aNode ); + if ( n.equals(aNode) ) { + return true; + } + } + return false; + } + + + public void addStaticNode(String aNode, ApiError apiError) { + currentStaticNodes = currentStaticNodes + "|" + aNode; + setX( "STATIC_ROUTING_NODES", currentStaticNodes, apiError ); + } + void removeStaticNode( String aNode, ApiError apiError ) { + currentStaticNodes = removeFromList( aNode, currentStaticNodes ); + setX( "STATIC_ROUTING_NODES", currentStaticNodes, apiError ); + } + } + + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + String unit_test = p.getProperty( "UnitTest", "No" ); + + private Map<String, DR_Node> dr_nodes = DatabaseClass.getDr_nodes(); + + public Map<String, DR_Node> getDr_Nodes() { + return dr_nodes; + } + + public List<DR_Node> getAllDr_Nodes() { + return new ArrayList<DR_Node>(dr_nodes.values()); + } + + public DR_Node getDr_Node( String fqdn, ApiError apiError ) { + DR_Node old = dr_nodes.get( fqdn ); + if ( old == null ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields( "fqdn"); + apiError.setMessage( "Node " + fqdn + " does not exist"); + return null; + } + apiError.setCode(200); + return old; + } + + public DR_Node addDr_Node( DR_Node node, ApiError apiError ) { + String fqdn = node.getFqdn(); + DR_Node old = dr_nodes.get( fqdn ); + if ( old != null ) { + apiError.setCode(Status.CONFLICT.getStatusCode()); + apiError.setFields( "fqdn"); + apiError.setMessage( "Node " + fqdn + " already exists"); + return null; + } + logger.info( "templog:addDr_Node at" + " 10" ); + + DrProv drProv = new DrProv(); + logger.info( "templog:addDr_Node at" + " 12" ); + + if ( ! drProv.containsNode( node.getFqdn(), apiError ) && apiError.is2xx() ) { + logger.info( "templog:addDr_Node at" + " 15" ); + drProv.addNode( node.getFqdn(), apiError ); + } + logger.info( "templog:addDr_Node at" + " 20" ); + if ( ! apiError.is2xx() && ! unit_test.equals( "Yes" ) ) { + return null; + } + logger.info( "templog:addDr_Node at" + " 30" ); + DcaeLocationService locService = new DcaeLocationService(); + if ( locService.isEdgeLocation( node.getDcaeLocationName()) && ! drProv.containsStaticNode( node.getFqdn(), apiError ) ) { + if ( apiError.is2xx() ) { + drProv.addStaticNode( node.getFqdn(), apiError ); + } + } + logger.info( "templog:addDr_Node at" + " 40" ); + if ( ! apiError.is2xx() && ! unit_test.equals("Yes") ) { + return null; + } + + logger.info( "templog:addDr_Node at" + " 50" ); + node.setLastMod(); + node.setStatus(DmaapObject_Status.VALID); + dr_nodes.put( node.getFqdn(), node ); + logger.info( "templog:addDr_Node at" + " 60" ); + apiError.setCode(200); + return node; + } + + public DR_Node updateDr_Node( DR_Node node, ApiError apiError ) { + DR_Node old = dr_nodes.get( node.getFqdn() ); + if ( old == null ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields( "fqdn"); + apiError.setMessage( "Node " + node.getFqdn() + " does not exist"); + return null; + } + node.setLastMod(); + dr_nodes.put( node.getFqdn(), node ); + apiError.setCode(200); + return node; + } + + public DR_Node removeDr_Node( String nodeName, ApiError apiError ) { + DR_Node old = dr_nodes.get( nodeName ); + if ( old == null ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields( "fqdn"); + apiError.setMessage( "Node " + nodeName + " does not exist"); + return null; + } + + DrProv drProv = new DrProv(); + if ( drProv.containsNode( old.getFqdn(), apiError ) && apiError.is2xx() ) { + drProv.removeNode( old.getFqdn(), apiError ); + } + DcaeLocationService locService = new DcaeLocationService(); + if ( locService.isEdgeLocation( old.getDcaeLocationName()) && drProv.containsStaticNode( old.getFqdn(), apiError ) ) { + if ( apiError.is2xx()) { + drProv.removeStaticNode( old.getFqdn(), apiError ); + } + + } + + apiError.setCode(200); + return dr_nodes.remove(nodeName); + } + + public String getNodePatternAtLocation( String loc, boolean allowMult ) { + logger.info( "loc=" + loc ); + if ( loc == null ) { + return null; + } + StringBuilder str = new StringBuilder(); + for( DR_Node node : dr_nodes.values() ) { + if ( loc.equals( node.getDcaeLocationName()) ) { + if ( str.length() > 0 ) { + str.append( ","); + } + str.append( node.getFqdn()); + if ( ! allowMult ) { + break; + } + } + } + logger.info( "returning " + str.toString() ); + return str.toString(); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_PubService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_PubService.java new file mode 100644 index 0000000..352330a --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_PubService.java @@ -0,0 +1,147 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import javax.ws.rs.core.Response.Status; +import org.onap.dmaap.dbcapi.client.DrProvConnection; +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DR_Pub; +import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; + +public class DR_PubService extends BaseLoggingClass{ + + private Map<String, DR_Pub> dr_pubs = DatabaseClass.getDr_pubs(); + private DR_NodeService nodeService = new DR_NodeService(); + private static DrProvConnection prov; + + public DR_PubService() { + super(); + prov = new DrProvConnection(); + } + + public Map<String, DR_Pub> getDr_Pubs() { + return dr_pubs; + } + + public List<DR_Pub> getAllDr_Pubs() { + return new ArrayList<DR_Pub>(dr_pubs.values()); + } + + public ArrayList<DR_Pub> getDr_PubsByFeedId( String feedId ) { + ArrayList<DR_Pub> somePubs = new ArrayList<DR_Pub>(); + for( DR_Pub pub : dr_pubs.values() ) { + if ( feedId.equals( pub.getFeedId() )) { + somePubs.add( pub ); + } + } + + return somePubs; + } + + public DR_Pub getDr_Pub( String key, ApiError err ) { + DR_Pub pub = dr_pubs.get( key ); + if ( pub == null ) { + err.setCode(Status.NOT_FOUND.getStatusCode()); + err.setFields( "pubId"); + err.setMessage("DR_Pub with pubId = " + key + " not found"); + } else { + err.setCode(Status.OK.getStatusCode()); + } + return pub; + } + + private void addIngressRoute( DR_Pub pub, ApiError err ) { + + String nodePattern = nodeService.getNodePatternAtLocation( pub.getDcaeLocationName(), true ); + if ( nodePattern != null && nodePattern.length() > 0 ) { + logger.info( "creating ingress rule: pub " + pub.getPubId() + " on feed " + pub.getFeedId() + " to " + nodePattern); + prov.makeIngressConnection( pub.getFeedId(), pub.getUsername(), "-", nodePattern); + int rc = prov.doXgressPost(err); + logger.info( "rc=" + rc + " error code=" + err.getCode() ); + + if ( rc != 200 ) { + switch( rc ) { + case 403: + logger.error( "Not authorized for DR ingress API"); + err.setCode(500); + err.setMessage("API deployment/configuration error - contact support"); + err.setFields( "PROV_AUTH_ADDRESSES"); + break; + + default: + logger.info( DmaapbcLogMessageEnum.INGRESS_CREATE_ERROR, Integer.toString(rc), pub.getPubId(), pub.getFeedId(), nodePattern); + } + } + + } + } + + public DR_Pub addDr_Pub( DR_Pub pub ) { + ApiError err = new ApiError(); + if ( pub.getPubId() != null && ! pub.getPubId().isEmpty() ) { + addIngressRoute( pub, err); + if ( err.getCode() > 0 ) { + pub.setStatus(DmaapObject_Status.INVALID); + } + pub.setLastMod(); + dr_pubs.put( pub.getPubId(), pub ); + return pub; + } + else { + return null; + } + } + + public DR_Pub updateDr_Pub( DR_Pub pub ) { + if ( pub.getPubId().isEmpty()) { + return null; + } + pub.setLastMod(); + dr_pubs.put( pub.getPubId(), pub ); + return pub; + } + + public DR_Pub removeDr_Pub( String pubId, ApiError err ) { + return removeDr_Pub( pubId, err, true ); + } + + + public DR_Pub removeDr_Pub( String pubId, ApiError err, boolean hitDR ) { + DR_Pub pub = dr_pubs.get( pubId ); + if ( pub == null ) { + err.setCode(Status.NOT_FOUND.getStatusCode()); + err.setFields( "pubId"); + err.setMessage( "pubId " + pubId + " not found"); + } else { + dr_pubs.remove(pubId); + err.setCode(Status.OK.getStatusCode()); + } + return pub; + + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_SubService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_SubService.java new file mode 100644 index 0000000..0a583a0 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_SubService.java @@ -0,0 +1,228 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.Response.Status; + +import org.onap.dmaap.dbcapi.client.DrProvConnection; +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DR_Sub; +import org.onap.dmaap.dbcapi.util.DmaapConfig; +import org.onap.dmaap.dbcapi.util.RandomInteger; + +public class DR_SubService extends BaseLoggingClass { + + private Map<String, DR_Sub> dr_subs = DatabaseClass.getDr_subs(); + private DR_NodeService nodeService = new DR_NodeService(); + private String provURL; + private static DrProvConnection prov; + + private String unit_test; + + + public DR_SubService( ) { + logger.debug( "Entry: DR_SubService (with no args)" ); + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + unit_test = p.getProperty( "UnitTest", "No" ); + } + public DR_SubService( String subURL ) { + logger.debug( "Entry: DR_SubService " + subURL ); + provURL = subURL; + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + unit_test = p.getProperty( "UnitTest", "No" ); + } + public Map<String, DR_Sub> getDR_Subs() { + logger.debug( "enter getDR_Subs()"); + return dr_subs; + } + + public List<DR_Sub> getAllDr_Subs() { + logger.debug( "enter getAllDR_Subs()"); + return new ArrayList<>(dr_subs.values()); + } + + public ArrayList<DR_Sub> getDr_SubsByFeedId( String pubId ) { + ArrayList<DR_Sub> someSubs = new ArrayList<>(); + for( DR_Sub sub : dr_subs.values() ) { + if ( pubId.equals( sub.getFeedId() )) { + someSubs.add( sub ); + } + } + + return someSubs; + } + public DR_Sub getDr_Sub( String key, ApiError apiError ) { + logger.debug( "enter getDR_Sub()"); + DR_Sub sub = dr_subs.get( key ); + if ( sub == null ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields( "subId"); + apiError.setMessage("subId " + key + " not found"); + } else { + apiError.setCode(200); + } + return sub; + } + + public DR_Sub addDr_Sub( DR_Sub sub, ApiError apiError ) { + logger.debug( "enter addDR_Subs()"); + prov = new DrProvConnection(); + prov.makeSubPostConnection( provURL ); + String resp = prov.doPostDr_Sub( sub, apiError ); + if ( "Yes".equals(unit_test) ) { + resp = simulateResp( sub, "POST" ); + apiError.setCode(201); + } + logger.debug( "addDr_Sub resp=" + resp ); + + DR_Sub snew = null; + + if ( resp != null ) { + snew = new DR_Sub( resp ); + snew.setDcaeLocationName(sub.getDcaeLocationName()); + snew.setLastMod(); + addEgressRoute( snew, apiError ); + dr_subs.put( snew.getSubId(), snew ); + apiError.setCode(201); + } else { + apiError.setCode(400); + } + + return snew; + } + + private void addEgressRoute( DR_Sub sub, ApiError err ) { + + String nodePattern = nodeService.getNodePatternAtLocation( sub.getDcaeLocationName(), false ); + if ( nodePattern != null && nodePattern.length() > 0 ) { + logger.info( "creating egress rule: sub " + sub.getSubId() + " on feed " + sub.getFeedId() + " to " + nodePattern); + prov.makeEgressConnection( sub.getSubId(), nodePattern); + int rc = prov.doXgressPost(err); + logger.info( "rc=" + rc + " error code=" + err.getCode() ); + + if ( rc != 200 ) { + switch( rc ) { + case 403: + logger.error( "Not authorized for DR egress API"); + err.setCode(500); + err.setMessage("API deployment/configuration error - contact support"); + err.setFields( "PROV_AUTH_ADDRESSES"); + break; + + default: + logger.info( DmaapbcLogMessageEnum.EGRESS_CREATE_ERROR, Integer.toString(rc), sub.getSubId(), sub.getFeedId(), nodePattern); + } + } + + } + } + + public DR_Sub updateDr_Sub( DR_Sub obj, ApiError apiError ) { + logger.debug( "enter updateDR_Subs()"); + + DrProvConnection prov = new DrProvConnection(); + prov.makeSubPutConnection( obj.getSubId() ); + String resp = prov.doPutDr_Sub( obj, apiError ); + if ( unit_test.equals( "Yes" ) ) { + resp = simulateResp( obj, "PUT" ); + apiError.setCode(200); + } + logger.debug( "resp=" + resp ); + + DR_Sub snew = null; + + if ( resp != null ) { + snew = new DR_Sub( resp ); + snew.setDcaeLocationName(obj.getDcaeLocationName()); + snew.setLastMod(); + dr_subs.put( snew.getSubId(), snew ); + apiError.setCode(200); + } else if ( apiError.is2xx()) { + apiError.setCode(400); + apiError.setMessage("unexpected empty response from DR Prov"); + } + + return snew; + } + + public void removeDr_Sub( String key, ApiError apiError ) { + removeDr_Sub( key, apiError, true ); + return; + } + + public void removeDr_Sub( String key, ApiError apiError, boolean hitDR ) { + logger.debug( "enter removeDR_Subs()"); + + DR_Sub sub = dr_subs.get( key ); + if ( sub == null ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields( "subId"); + apiError.setMessage("subId " + key + " not found"); + } else { + if ( hitDR ) { + DrProvConnection prov = new DrProvConnection(); + prov.makeSubPutConnection( key ); + String resp = prov.doDeleteDr_Sub( sub, apiError ); + logger.debug( "resp=" + resp ); + } else { + apiError.setCode(200); + } + + if ( apiError.is2xx() || unit_test.equals( "Yes" ) ) { + dr_subs.remove(key); + } + } + + return; + } + + private String simulateResp( DR_Sub sub, String action ){ + String server = "subscriber.onap.org"; + String subid; + if ( action.equals( "POST" ) ) { + RandomInteger ran = new RandomInteger(10000); + subid = Integer.toString( ran.next() ); + } else if ( action.equals( "PUT" ) ) { + subid = sub.getSubId(); + } else { + subid = "99"; + } + String ret = String.format("{\"suspend\": false, \"delivery\": {\"url\": \"https://%s/delivery/%s\", \"user\": \"%s\", \"password\": \"%s\", \"use100\": true}, \"metadataOnly\": false, \"groupid\": \"0\" , \"follow_redirect\": true, ", + server, subid, sub.getUsername(), sub.getUserpwd()); + String links = String.format( "\"links\": {\"feed\": \"https://dr-prov/feedlog/%s\", \"self\": \"https://dr-prov/sub/%s\", \"log\": \"https://dr-prov/sublog/%s\" }", + sub.getFeedId(), + subid, + subid ); + ret += links + "}"; + logger.info( "DR_SubService:simulateResp=" + ret); + + return ret; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DcaeLocationService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DcaeLocationService.java new file mode 100644 index 0000000..ad6c993 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DcaeLocationService.java @@ -0,0 +1,85 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.service; + +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.model.DcaeLocation; +import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class DcaeLocationService { + + private static final String DEFAULT_CENTRAL_LOCATION = "aCentralLocation"; // default value that is obvious to see is wrong + private final Map<String, DcaeLocation> dcaeLocations; + + public DcaeLocationService() { + this(DatabaseClass.getDcaeLocations()); + } + + DcaeLocationService(Map<String, DcaeLocation> dcaeLocations) { + this.dcaeLocations = dcaeLocations; + } + + public List<DcaeLocation> getAllDcaeLocations() { + return new ArrayList<>(dcaeLocations.values()); + } + + public DcaeLocation getDcaeLocation(String name) { + return dcaeLocations.get(name); + } + + public DcaeLocation addDcaeLocation(DcaeLocation location) { + location.setLastMod(); + location.setStatus(DmaapObject_Status.VALID); + dcaeLocations.put(location.getDcaeLocationName(), location); + return location; + } + + public DcaeLocation updateDcaeLocation(DcaeLocation location) { + if (location.getDcaeLocationName().isEmpty()) { + return null; + } + location.setLastMod(); + dcaeLocations.put(location.getDcaeLocationName(), location); + return location; + } + + public DcaeLocation removeDcaeLocation(String locationName) { + return dcaeLocations.remove(locationName); + } + + String getCentralLocation() { + + Optional<DcaeLocation> firstCentralLocation = + dcaeLocations.values().stream().filter(DcaeLocation::isCentral).findFirst(); + + return firstCentralLocation.isPresent() ? firstCentralLocation.get().getDcaeLocationName() : DEFAULT_CENTRAL_LOCATION; + } + + boolean isEdgeLocation(String aName) { + return dcaeLocations.get(aName) != null && !dcaeLocations.get(aName).isCentral(); + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java new file mode 100644 index 0000000..8789ac4 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java @@ -0,0 +1,310 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.service; + +import java.util.ArrayList; +import org.onap.dmaap.dbcapi.aaf.AafService; +import org.onap.dmaap.dbcapi.aaf.AafServiceFactory; +import org.onap.dmaap.dbcapi.aaf.DmaapGrant; +import org.onap.dmaap.dbcapi.aaf.DmaapPerm; +import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType; +import org.onap.dmaap.dbcapi.authentication.ApiPerms; +import org.onap.dmaap.dbcapi.authentication.ApiPolicy; +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.Dmaap; +import org.onap.dmaap.dbcapi.model.MR_Client; +import org.onap.dmaap.dbcapi.model.Topic; +import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; +import org.onap.dmaap.dbcapi.util.DmaapConfig; +import org.onap.dmaap.dbcapi.util.Singleton; + +public class DmaapService extends BaseLoggingClass { + + + private Singleton<Dmaap> dmaapholder = DatabaseClass.getDmaap(); + private static String noEnvironmentPrefix; + + + String topicFactory; // = "org.openecomp.dcae.dmaap.topicFactory"; + String topicMgrRole; // = "org.openecomp.dmaapBC.TopicMgr"; + + private boolean multiSite; + + + public DmaapService() { + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + topicFactory = p.getProperty("MR.TopicFactoryNS", "MR.topicFactoryNS.not.set"); + topicMgrRole = p.getProperty("MR.TopicMgrRole", "MR.TopicMgrRole.not.set" ); + + multiSite = "true".equalsIgnoreCase(p.getProperty("MR.multisite", "true")); + noEnvironmentPrefix = p.getProperty( "AAF.NoEnvironmentPrefix", "org.onap"); + + logger.info( "DmaapService settings: " + + " topicFactory=" + topicFactory + + " topicMgrRole=" + topicMgrRole + + + " multisite=" + multiSite + + " noEnvironmentPrefix=" + noEnvironmentPrefix + ); + + Dmaap dmaap = dmaapholder.get(); + logger.info( "DmaapService object values: " + + " dmaapName=" + dmaap.getDmaapName() + + " drProvURL=" + dmaap.getDrProvUrl() + + " version="+ dmaap.getVersion() + ); + + } + + public Dmaap getDmaap() { + logger.info( "entering getDmaap()" ); + return(dmaapholder.get()); + } + + public Dmaap addDmaap( Dmaap nd ) { + + logger.info( "entering addDmaap()" ); + Dmaap dmaap = dmaapholder.get(); + if ( dmaap.getVersion().equals( "0")) { + + nd.setLastMod(); + dmaapholder.update(nd); + + AafService aaf = new AafServiceFactory().initAafService(ServiceType.AAF_Admin); + ApiPolicy apiPolicy = new ApiPolicy(); + if ( apiPolicy.isPermissionClassSet() ) { + ApiPerms p = new ApiPerms(); + p.setEnvMap(); + } + boolean anythingWrong = false; + + if ( multiSite ) { + anythingWrong = setTopicMgtPerms( nd, aaf ) || createMmaTopic(); + } + + if ( anythingWrong ) { + dmaap.setStatus(DmaapObject_Status.INVALID); + } + else { + dmaap.setStatus(DmaapObject_Status.VALID); + } + dmaap.setLastMod(); + dmaapholder.update(dmaap); + + return dmaap; + + } + else { + return dmaap; + } + } + + public Dmaap updateDmaap( Dmaap nd ) { + logger.info( "entering updateDmaap()" ); + + boolean anythingWrong = false; + + Dmaap dmaap = dmaapholder.get(); + + // some triggers for when we attempt to reprovision perms and MMA topic: + // - if the DMaaP Name changes + // - if the version is 0 (this is a handy test to force this processing by updating the DB) + // - if the object is invalid, reprocessing might fix it. + if ( ! dmaap.isStatusValid() || ! nd.getDmaapName().equals(dmaap.getDmaapName()) || dmaap.getVersion().equals( "0") ) { + nd.setLastMod(); + dmaapholder.update(nd); //need to set this so the following perms will pick up any new vals. + //dcaeTopicNs = dmaapholder.get().getTopicNsRoot(); + ApiPolicy apiPolicy = new ApiPolicy(); + if ( apiPolicy.isPermissionClassSet()) { + ApiPerms p = new ApiPerms(); + p.setEnvMap(); + } + AafService aaf = new AafServiceFactory().initAafService(ServiceType.AAF_Admin); + if ( multiSite ) { + anythingWrong = setTopicMgtPerms( nd, aaf ) || createMmaTopic(); + } + } + + if ( anythingWrong ) { + nd.setStatus(DmaapObject_Status.INVALID); + } + else { + nd.setStatus(DmaapObject_Status.VALID); + } + nd.setLastMod(); + dmaapholder.update(nd); // may need to update status... + return(dmaapholder.get()); + + } + + public String getTopicPerm(){ + Dmaap dmaap = dmaapholder.get(); + return getTopicPerm( dmaap.getDmaapName() ); + } + public String getTopicPerm( String val ) { + Dmaap dmaap = dmaapholder.get(); + String nsRoot = dmaap.getTopicNsRoot(); + if ( nsRoot == null ) { return null; } + + String t; + // in ONAP Casablanca, we assume no distinction of environments reflected in topic namespace + if ( nsRoot.startsWith(noEnvironmentPrefix) ) { + t = nsRoot + ".mr.topic"; + } else { + t = nsRoot + "." + val + ".mr.topic"; + } + return t; + } + + public String getBridgeAdminFqtn(){ + Dmaap dmaap = dmaapholder.get(); + String topic = dmaap.getBridgeAdminTopic(); + + // check if this is already an fqtn (contains a dot) + // otherwise build it + if ( topic.indexOf('.') < 0 ) { + topic = dmaap.getTopicNsRoot() + "." + dmaap.getDmaapName() + "." + dmaap.getBridgeAdminTopic(); + } + return( topic ); + } + + private boolean setTopicMgtPerms( Dmaap nd, AafService aaf ){ + String[] actions = { "create", "destroy" }; + String instance = ":" + nd.getTopicNsRoot() + "." + nd.getDmaapName() + ".mr.topic:" + nd.getTopicNsRoot() + "." + nd.getDmaapName(); + + for( String action : actions ) { + + DmaapPerm perm = new DmaapPerm( topicFactory, instance, action ); + + int rc = aaf.addPerm( perm ); + if ( rc != 201 && rc != 409 ) { + logger.error( "unable to add perm for "+ topicFactory + "|" + instance + "|" + action ); + return true; + } + + DmaapGrant grant = new DmaapGrant( perm, topicMgrRole ); + rc = aaf.addGrant( grant ); + if ( rc != 201 && rc != 409 ) { + logger.error( "unable to grant to " + topicMgrRole + " perm for "+ topicFactory + "|" + instance + "|" + action ); + return true; + } + } + + String t = nd.getTopicNsRoot() +"." + nd.getDmaapName() + ".mr.topic"; + String[] s = { "view", "pub", "sub" }; + actions = s; + instance = "*"; + + for( String action : actions ) { + + DmaapPerm perm = new DmaapPerm( t, instance, action ); + + int rc = aaf.addPerm( perm ); + if ( rc != 201 && rc != 409 ) { + errorLogger.error( DmaapbcLogMessageEnum.AAF_UNEXPECTED_RESPONSE, Integer.toString(rc), "add perm", t + "|" + instance + "|" + action ); + return true; + } + + DmaapGrant grant = new DmaapGrant( perm, topicMgrRole ); + rc = aaf.addGrant( grant ); + if ( rc != 201 && rc != 409 ) { + errorLogger.error( DmaapbcLogMessageEnum.AAF_UNEXPECTED_RESPONSE, Integer.toString(rc), "grant to " + topicMgrRole + " perm ", topicFactory + "|" + instance + "|" + action ); + return true; + } + + } + return false; + } + + public boolean testCreateMmaTopic() { + + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + String unit_test = p.getProperty( "UnitTest", "No" ); + if ( unit_test.equals( "Yes" ) ) { + return createMmaTopic(); + } + return false; + } + + // create the special topic for MMA provisioning. + // return true indicating a problem in topic creation, + // else false means it was ok (created or previously existed) + private boolean createMmaTopic() { + boolean rc = true; + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + Dmaap dmaap = dmaapholder.get(); + + ArrayList<MR_Client> clients = new ArrayList<MR_Client>(); + String[] actions = { "pub", "sub", "view" }; + String centralMR = new DcaeLocationService().getCentralLocation(); + if ( centralMR == null ) { + return rc; + } + logger.info( "Location for " + dmaap.getBridgeAdminTopic() + " is " + centralMR ); + + // first client is the Role used by Bus Controller to send messages to MMA + String provRole = p.getProperty("MM.ProvRole"); + MR_Client nClient = new MR_Client(); + nClient.setAction(actions); + nClient.setClientRole(provRole); + nClient.setDcaeLocationName(centralMR); + clients.add( nClient ); + + // second client is the Role used by MMA to listen to messages from Bus Controller + String agentRole = p.getProperty("MM.AgentRole"); + nClient = new MR_Client(); + nClient.setAction(actions); + nClient.setClientRole(agentRole); + nClient.setDcaeLocationName(centralMR); + clients.add( nClient ); + + // initialize Topic + Topic mmaTopic = new Topic().init(); + mmaTopic.setTopicName(dmaap.getBridgeAdminTopic()); + mmaTopic.setClients(clients); + mmaTopic.setOwner("BusController"); + mmaTopic.setTopicDescription("topic reserved for MirrorMaker Administration"); + mmaTopic.setTnxEnabled("false"); + mmaTopic.setPartitionCount("1"); // a single partition should guarantee message order + + + ApiError err = new ApiError(); + TopicService svc = new TopicService(); + try { + @SuppressWarnings("unused") + Topic nTopic = svc.addTopic(mmaTopic, err, true); + if ( err.is2xx() || err.getCode() == 409 ) { + return false; + } + } catch ( Exception e) { + errorLogger.error( DmaapbcLogMessageEnum.UNEXPECTED_CONDITION, " while adding Topic: " + e.getMessage()); + } + errorLogger.error( DmaapbcLogMessageEnum.TOPIC_CREATE_ERROR, dmaap.getBridgeAdminTopic(), err.getFields(), err.getFields(), err.getMessage()); + + return rc; + + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/FeedService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/FeedService.java new file mode 100644 index 0000000..19b0267 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/FeedService.java @@ -0,0 +1,572 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.service; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.ws.rs.core.Response.Status; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.onap.dmaap.dbcapi.client.DrProvConnection; +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DR_Pub; +import org.onap.dmaap.dbcapi.model.DR_Sub; +import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; +import org.onap.dmaap.dbcapi.model.Feed; +import org.onap.dmaap.dbcapi.util.DmaapConfig; +import org.onap.dmaap.dbcapi.util.RandomInteger; + +public class FeedService extends BaseLoggingClass { + + private Map<String, Feed> feeds = DatabaseClass.getFeeds(); + private Map<String, DR_Sub> dr_subs = DatabaseClass.getDr_subs(); + private DR_PubService pubService = new DR_PubService(); + private DR_SubService subService = new DR_SubService(); + private DcaeLocationService dcaeLocations = new DcaeLocationService(); + private String deleteHandling; + private String unit_test; + + public FeedService() { + logger.info( "new FeedService"); + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + deleteHandling = p.getProperty("Feed.deleteHandling", "DeleteOnDR"); + unit_test = p.getProperty( "UnitTest", "No" ); + + } + + public Map<String, Feed> getFeeds() { + return feeds; + } + + private void getSubObjects( Feed f ) { + ArrayList<DR_Pub> pubs = pubService.getDr_PubsByFeedId( f.getFeedId() ); + f.setPubs(pubs); + ArrayList<DR_Sub> subs = subService.getDr_SubsByFeedId( f.getFeedId() ); + f.setSubs(subs); + } + + public List<Feed> getAllFeeds(){ + return getAllFeeds(null, null, null); + } + + public List<Feed> getAllFeeds( String name, String ver, String match ) { + logger.info( "getAllFeeds: name=" + name + " ver=" + ver + " match=" + match); + ArrayList<Feed> fatFeeds = new ArrayList<Feed>(); + for( Feed f: feeds.values() ) { + boolean keep = true; + if ( name != null ) { + if ( match != null && "startsWith".equals(match) ) { + if ( ! f.getFeedName().startsWith( name ) ) { + logger.info( "getAllFeeds: feedName=" + f.getFeedName() + " doesn't start with=" + name); + keep = false; + } + } else if ( match != null && match.equals("contains") ) { + if ( ! f.getFeedName().contains( name ) ) { + logger.info( "getAllFeeds: feedName=" + f.getFeedName() + " doesn't contain=" + name); + keep = false; + } + } else { + if ( ! f.getFeedName().equals( name ) ) { + logger.info( "getAllFeeds: feedName=" + f.getFeedName() + " doesn't equal=" + name); + keep = false; + } + } + + } + if ( keep && ver != null ) { + if ( ! f.getFeedVersion().equals(ver)) { + logger.info( "getAllFeeds: feedVersion=" + f.getFeedName() + " doesn't match " + ver); + keep = false; + } else { + logger.info( "getAllFeeds: feedVersion=" + f.getFeedName() + " matches " + ver); + } + } + + if (keep){ + getSubObjects(f); + fatFeeds.add(f); + } + } + return fatFeeds; + } + + + private Feed _getFeed( String key, ApiError err, boolean flag ) { + Feed f = feeds.get( key ); + if ( f != null && ( flag || f.getStatus() != DmaapObject_Status.DELETED ) ) { + getSubObjects( f ); + } else { + err.setCode(Status.NOT_FOUND.getStatusCode()); + err.setMessage("feed not found"); + err.setFields("feedId=" + key ); + return null; + } + err.setCode(200); + return f; + } + public Feed getFeed( String key, ApiError err ) { + return _getFeed( key, err, false ); + } + public Feed getFeedPure( String key, ApiError err ) { + return _getFeed( key, err, true ); + } + + public Feed getFeedByName( String name, String ver, ApiError err ) { + for( Feed f: feeds.values() ) { + if ( f.getFeedName().equals( name ) && f.getFeedVersion().equals(ver) ) { + getSubObjects(f); + return f; + } + + } + err.setCode(Status.NOT_FOUND.getStatusCode()); + err.setMessage("feed not found"); + err.setFields("feedName=" + name + " and ver=" + ver ); + return null; + + } + + private boolean savePubs( Feed f ) { + return savePubs( f, f ); + } + // need to save the Pub objects independently and copy pubId from original request + private boolean savePubs( Feed fnew, Feed req ) { + // save any pubs + DR_PubService pubSvc = new DR_PubService(); + ArrayList<DR_Pub> reqPubs = req.getPubs(); + ArrayList<DR_Pub> newPubs = fnew.getPubs(); + + + + int nSize = newPubs.size(); + int rSize = reqPubs.size(); + logger.info( "reqPubs size=" + rSize + " newPubs size=" + nSize ); + if ( nSize != rSize ) { + errorLogger.error( "Resulting set of publishers do not match requested set of publishers " + nSize + " vs " + rSize ); + fnew.setStatus( DmaapObject_Status.INVALID); + return false; + } + // NOTE: when i > 1 newPubs are in reverse order from reqPubs + for( int i = 0; i < reqPubs.size(); i++ ) { + DR_Pub reqPub = reqPubs.get(i); + ApiError err = new ApiError(); + if ( pubSvc.getDr_Pub( reqPub.getPubId(), err ) == null ) { + DR_Pub newPub = newPubs.get(nSize - i - 1); + reqPub.setPubId(newPub.getPubId()); + reqPub.setFeedId(newPub.getFeedId()); + reqPub.setStatus(DmaapObject_Status.VALID); + if ( reqPub.getDcaeLocationName() == null ) { + reqPub.setDcaeLocationName("notSpecified"); + } + pubSvc.addDr_Pub( reqPub ); + } + + } + + fnew.setPubs(reqPubs); + fnew.setStatus(DmaapObject_Status.VALID); + return true; + + } + + private boolean saveSubs( Feed f ) { + return saveSubs( f, f ); + } + // need to save the Sub objects independently + private boolean saveSubs( Feed fnew, Feed req ) { + ArrayList<DR_Sub> subs = req.getSubs(); + if ( subs == null || subs.size() == 0 ) { + logger.info( "No subs specified"); + } else { + DR_SubService subSvc = new DR_SubService( fnew.getSubscribeURL() ); + ApiError err = new ApiError(); + for( int i = 0; i < subs.size(); i++ ) { + DR_Sub sub = subs.get(i); + if ( subSvc.getDr_Sub( sub.getSubId(), err) == null ) { + subs.set( i, subSvc.addDr_Sub(sub, err)); + if ( ! err.is2xx()) { + logger.error( "i=" + i + " url=" + sub.getDeliveryURL() + " err=" + err.getCode() ); + return false; + } + } + + } + fnew.setSubs(subs); + } + + + fnew.setStatus(DmaapObject_Status.VALID); + return true; + + } + + public Feed addFeed( Feed req, ApiError err ) { + + // at least 1 pub is required by DR, so create a default pub if none is specified + if ( req.getPubs().size() == 0 ) { + logger.info( "No pubs specified - creating tmp pub"); + ArrayList<DR_Pub> pubs = new ArrayList<DR_Pub>(); + pubs.add( new DR_Pub( dcaeLocations.getCentralLocation()) + .setRandomUserName() + .setRandomPassword()); + req.setPubs(pubs); + } + + + DrProvConnection prov = new DrProvConnection(); + prov.makeFeedConnection(); + String resp = prov.doPostFeed( req, err ); + if ( unit_test.equals( "Yes" ) ) { + // assume resp is null, so need to simulate it + resp = simulateResp( req, "POST" ); + } + logger.info( "resp=" + resp ); + if ( resp == null ) { + switch( err.getCode() ) { + case 400: + err.setFields( "feedName=" + req.getFeedName() + " + feedVersion=" + req.getFeedVersion() ); + break; + case 403: + err.setCode(500); + err.setMessage("API deployment/configuration error - contact support"); + err.setFields( "PROV_AUTH_ADDRESSES"); + logger.error( "Prov response: 403. " + err.getMessage() + " regarding " + err.getFields() ); + break; + default: + err.setCode(500); + err.setMessage( "Unexpected response from DR backend" ); + err.setFields("response"); + } + return null; + + } + + + Feed fnew = new Feed( resp ); + logger.info( "fnew status is:" + fnew.getStatus() ); + if ( ! fnew.isStatusValid()) { + err.setCode(500); + err.setMessage( "Unexpected response from DR backend" ); + err.setFields("response"); + return null; + } + + //saveChildren( fnew, req ); + if ( ! savePubs( fnew, req ) || ! saveSubs( fnew, req ) ) { + err.setCode(Status.BAD_REQUEST.getStatusCode()); + err.setMessage("Unable to save Pub or Sub objects"); + return null; + } + fnew.setFormatUuid(req.getFormatUuid()); + fnew.setLastMod(); + feeds.put( fnew.getFeedId(), fnew ); + return fnew; + } + + public Feed updateFeed( Feed req, ApiError err ) { + + // at least 1 pub is required by DR, so create a default pub if none is specified + if ( req.getPubs().size() == 0 ) { + logger.info( "No pubs specified - creating tmp pub"); + ArrayList<DR_Pub> pubs = new ArrayList<DR_Pub>(); + pubs.add( new DR_Pub( dcaeLocations.getCentralLocation()) + .setRandomUserName() + .setRandomPassword()); + req.setPubs(pubs); + } + + DrProvConnection prov = new DrProvConnection(); + prov.makeFeedConnection( req.getFeedId() ); + String resp = prov.doPutFeed( req, err ); + if ( unit_test.equals( "Yes" ) ) { + // assume resp is null, so need to simulate it + resp = simulateResp( req, "PUT" ); + err.setCode(200); + } + logger.info( "resp=" + resp ); + if ( resp == null ) { + switch( err.getCode() ) { + case 400: + err.setFields( "feedName=" + req.getFeedName() + " + feedVersion=" + req.getFeedVersion() ); + break; + case 403: + err.setCode(500); + err.setMessage("API deployment/configuration error - contact support"); + err.setFields( "PROV_AUTH_ADDRESSES"); + break; + default: + err.setCode(500); + err.setMessage( "Unexpected response from DR backend" ); + err.setFields("response"); + } + return null; + } + + + Feed fnew = new Feed( resp ); + logger.info( "fnew status is:" + fnew.getStatus() ); + if ( ! fnew.isStatusValid()) { + err.setCode(500); + err.setMessage( "Unexpected response from DR backend" ); + err.setFields("response"); + return null; + } + + if ( ! savePubs( fnew, req ) || ! saveSubs( fnew, req ) ) { + err.setCode(Status.BAD_REQUEST.getStatusCode()); + err.setMessage("Unable to save Pub or Sub objects"); + return null; + } + fnew.setFormatUuid(req.getFormatUuid()); + fnew.setLastMod(); + feeds.put( fnew.getFeedId(), fnew ); + return fnew; + } + + + // + // DR does not actually delete a feed, so we provide two behaviors: + // 1) clean up the feed by removing all subs and pubs, mark it here as DELETED. + // then client can add it back if desired. + // 2) Call the DR Delete function. Feed with the same name and version can never be added again + // + public Feed removeFeed( Feed req, ApiError err ) { + return removeFeed( req, err, true ); + } + + public Feed removeFeed( Feed req, ApiError err, boolean hitDR ) { + + // strip pubs and subs from feed first no matter what + ArrayList<DR_Pub> pubs = pubService.getDr_PubsByFeedId( req.getFeedId() ); + for( DR_Pub pub: pubs ) { + pubService.removeDr_Pub(pub.getPubId(), err, hitDR); + if ( ! err.is2xx()) { + return req; + } + } + ArrayList<DR_Sub> subs = subService.getDr_SubsByFeedId( req.getFeedId() ); + for ( DR_Sub sub: subs ) { + subService.removeDr_Sub(sub.getSubId(), err, hitDR); + if ( ! err.is2xx()) { + return req; + } + } + + if ( ! hitDR ) { + return feeds.remove(req.getFeedId()); + } + + if ( deleteHandling.equalsIgnoreCase("DeleteOnDR")) { + DrProvConnection prov = new DrProvConnection(); + prov.makeFeedConnection( req.getFeedId() ); + String resp = prov.doDeleteFeed( req, err ); + if ( unit_test.equals( "Yes" ) ) { + // assume resp is null, so need to simulate it + resp = simulateDelResp( req ); + } + logger.info( "resp=" + resp ); + if ( resp == null ) { + switch( err.getCode() ) { + case 400: + err.setFields( "feedName=" + req.getFeedName() + " + feedVersion=" + req.getFeedVersion() ); + break; + case 403: + err.setCode(500); + err.setMessage("API deployment/configuration error - contact support"); + err.setFields( "PROV_AUTH_ADDRESSES"); + break; + default: + err.setCode(500); + err.setMessage( "Unexpected response from DR backend" ); + err.setFields("response"); + } + return req; // return back the requested feed - implies it wasn't removed + } + return feeds.remove(req.getFeedId()); + } else { + + logger.info( "Disable pubs for deleted feed - creating tmp pub"); + ArrayList<DR_Pub> tmppub = new ArrayList<DR_Pub>(); + tmppub.add( new DR_Pub( dcaeLocations.getCentralLocation()) + .setRandomUserName() + .setRandomPassword()); + req.setPubs(tmppub); + req.setSubs(null); + Feed fnew = updateFeed( req, err ); + if ( ! err.is2xx()) { + return req; + } + fnew.setStatus(DmaapObject_Status.DELETED); + feeds.put( fnew.getFeedId(), fnew ); + return null; + } + + + } + + + /* + * sync will retrieve current config from DR and add it to the DB + * when hard = true, then first git rid of current DR provisioning data (from the DB) + */ + public void sync( boolean hard, ApiError err ) { + + if ( hard ) { + + ArrayList<Feed> flist = new ArrayList<Feed>(this.getAllFeeds()); + for ( Iterator<Feed> it = flist.iterator(); it.hasNext(); ) { + Feed f = it.next(); + + @SuppressWarnings("unused") + Feed old = removeFeed( f, err, false ); + if (! err.is2xx()) { + return; + } + } + } + + DrProvConnection prov = new DrProvConnection(); + prov.makeDumpConnection(); + String resp = prov.doGetDump( err ); + if (! err.is2xx()) { + return; + } + logger.debug("sync: resp from DR is: " + resp); + + JSONParser parser = new JSONParser(); + JSONObject jsonObj; + try { + jsonObj = (JSONObject) parser.parse( resp ); + } catch ( ParseException pe ) { + logger.error( "Error parsing provisioning data: " + resp ); + err.setCode(500); + return; + } + + int i; + + JSONArray feedsArray = (JSONArray) jsonObj.get( "feeds"); + for( i = 0; i < feedsArray.size(); i++ ) { + JSONObject entry = (JSONObject) feedsArray.get(i); + Feed fnew = new Feed( entry.toJSONString() ); + + logger.info( "fnew status is:" + fnew.getStatus() ); + if ( ! fnew.isStatusValid()) { + err.setCode(500); + err.setMessage( "Unexpected response from DR backend" ); + err.setFields("response"); + return; + } + + if ( ! savePubs( fnew ) ) { + err.setCode(Status.BAD_REQUEST.getStatusCode()); + err.setMessage("Unable to save Pub or Sub objects"); + return; + } + fnew.setFormatUuid(fnew.getFormatUuid()); + fnew.setLastMod(); + feeds.put( fnew.getFeedId(), fnew ); + + } + + JSONArray subArray = (JSONArray) jsonObj.get( "subscriptions"); + for( i = 0; i < subArray.size(); i++ ) { + JSONObject entry = (JSONObject) subArray.get(i); + DR_Sub snew = new DR_Sub( entry.toJSONString() ); + + logger.info( "snew status is:" + snew.getStatus() ); + if ( ! snew.isStatusValid()) { + err.setCode(500); + err.setMessage( "Unexpected response from DR backend" ); + err.setFields("response"); + return; + } + + dr_subs.put( snew.getSubId(), snew ); + + } + err.setCode(200); + return; + + } + + private String simulateResp( Feed f, String action ){ + String server = "localhost"; + String feedid; + if ( action.equals( "POST" ) ) { + RandomInteger ran = new RandomInteger(10000); + feedid = Integer.toString( ran.next() ); + } else if ( action.equals( "PUT" ) ) { + feedid = f.getFeedId(); + } else { + feedid = "99"; + } + String ret = String.format( +"{\"suspend\":false,\"groupid\":0,\"description\":\"%s\",\"version\":\"1.0\",\"authorization\":", + f.getFeedDescription() ); + + String endpoints = "{\"endpoint_addrs\":[],\"classification\":\"unclassified\",\"endpoint_ids\":["; + String sep = ""; + for( DR_Pub pub: f.getPubs()) { + endpoints += String.format( "%s{\"password\":\"%s\",\"id\":\"%s\"}", + sep, pub.getUserpwd(), pub.getUsername() ); + sep = ","; + + } + endpoints += "]},"; + ret += endpoints; + + ret += String.format( + "\"name\":\"%s\",\"business_description\":\"\",\"publisher\":\"sim\",\"links\":{\"subscribe\":\"https://%s/subscribe/%s\",\"log\":\"https://%s/feedlog/%s\",\"publish\":\"https://%s/publish/%s\",\"self\":\"https://%s/feed/%s\"}}", + + f.getFeedName(), + server, feedid, + server, feedid, + server, feedid, + server, feedid + ); + logger.info( "simulateResp ret=" + ret ); + return ret; + } + private String simulateDelResp( Feed f ){ + String server = "localhost"; + String feedid = f.getFeedId(); + String ret = String.format( +"{\"suspend\":true,\"groupid\":0,\"description\":\"%s\",\"version\":\"1.0\",\"authorization\":{\"endpoint_addrs\":[],\"classification\":\"unclassified\",\"endpoint_ids\":[{\"password\":\"topSecret123\",\"id\":\"sim\"}]},\"name\":\"%s\",\"business_description\":\"\",\"publisher\":\"sim\",\"links\":{\"subscribe\":\"https://%s/subscribe/%s\",\"log\":\"https://%s/feedlog/%s\",\"publish\":\"https://%s/publish/%s\",\"self\":\"https://%s/feed/%s\"}}", + f.getFeedDescription(), + f.getFeedName(), + server, feedid, + server, feedid, + server, feedid, + server, feedid + + ); + return ret; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClientService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClientService.java new file mode 100644 index 0000000..bcf5408 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClientService.java @@ -0,0 +1,234 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.service; + +import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType; +import org.onap.dmaap.dbcapi.aaf.AafServiceFactory; +import org.onap.dmaap.dbcapi.client.MrProvConnection; +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DcaeLocation; +import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; +import org.onap.dmaap.dbcapi.model.MR_Client; +import org.onap.dmaap.dbcapi.model.MR_Cluster; +import org.onap.dmaap.dbcapi.model.Topic; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +import javax.ws.rs.core.Response.Status; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class MR_ClientService extends BaseLoggingClass { + + private static final String MR_CLIENT_ID = "mrClientId"; + private int deleteLevel; + private Map<String, MR_Client> mr_clients = DatabaseClass.getMr_clients(); + private Map<String, MR_Cluster> clusters = DatabaseClass.getMr_clusters(); + private Map<String, DcaeLocation> locations = DatabaseClass.getDcaeLocations(); + private DmaapService dmaap = new DmaapService(); + private AafPermissionService aafPermissionService = + new AafPermissionService(new AafServiceFactory().initAafService(ServiceType.AAF_TopicMgr), dmaap); + private String centralCname; + + public MR_ClientService() { + DmaapConfig p = (DmaapConfig) DmaapConfig.getConfig(); + + centralCname = p.getProperty("MR.CentralCname", "MRcname.not.set"); + deleteLevel = Integer.valueOf(p.getProperty("MR.ClientDeleteLevel", "0")); + } + + public List<MR_Client> getAllMr_Clients() { + return new ArrayList<>(mr_clients.values()); + } + + List<MR_Client> getAllMrClients(String fqtn) { + ArrayList<MR_Client> results = new ArrayList<>(); + for (Map.Entry<String, MR_Client> entry : mr_clients.entrySet()) { + MR_Client client = entry.getValue(); + if (fqtn.equals(client.getFqtn())) { + results.add(client); + } + } + return results; + } + + List<MR_Client> getClientsByLocation(String location) { + List<MR_Client> results = new ArrayList<>(); + for (Map.Entry<String, MR_Client> entry : mr_clients.entrySet()) { + MR_Client client = entry.getValue(); + if (location.equals(client.getDcaeLocationName())) { + results.add(client); + } + } + return results; + } + + public MR_Client getMr_Client(String key, ApiError apiError) { + MR_Client c = mr_clients.get(key); + if (c == null) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields(MR_CLIENT_ID); + apiError.setMessage(MR_CLIENT_ID + " " + key + " not found"); + } else { + apiError.setCode(200); + } + return c; + } + + public MR_Client addMr_Client(MR_Client client, Topic topic, ApiError err) { + if (client.getDcaeLocationName().isEmpty()) { + logger.info("Client dcaeLocation that doesn't exist or not specified"); + return null; + } + // original style: clients specified Role. This has precedence for backwards + // compatibility. + // ONAP style: clients specify Identity to be assigned to generated Role + String role = client.getClientRole(); + if (role != null) { + updateApiError(err, aafPermissionService.grantClientRolePerms(client)); + } else if (client.hasClientIdentity()) { + if (client.isSubscriber()) { + role = topic.getSubscriberRole(); + updateApiError(err, aafPermissionService.assignClientToRole(client, role)); + } + if (client.isPublisher()) { + role = topic.getPublisherRole(); + updateApiError(err, aafPermissionService.assignClientToRole(client, role)); + } + } + if (!client.isStatusValid()) { + return null; + } + String centralFqdn = null; + DcaeLocation candidate = locations.get(client.getDcaeLocationName()); + + MR_Cluster cluster = clusters.get(client.getDcaeLocationName()); + if (cluster != null && candidate != null) { + if (candidate.isCentral() && !topic.getReplicationCase().involvesFQDN()) { + centralFqdn = centralCname; + } + client.setTopicURL(cluster.genTopicURL(centralFqdn, client.getFqtn())); + if (centralFqdn == null) { + client.setStatus(addTopicToCluster(cluster, topic, err)); + if (!err.is2xx() && err.getCode() != 409) { + topic.setFqtn(err.getMessage()); + return null; + } + + } else { + MR_ClusterService clusters = new MR_ClusterService(); + // MM should only exist for edge-to-central + // we use a cname for the central target (default resiliency with no replicationGroup set) + // but still need to provision topics on all central MRs + for (MR_Cluster central : clusters.getCentralClusters()) { + client.setStatus(addTopicToCluster(central, topic, err)); + if (!err.is2xx() && err.getCode() != 409) { + topic.setFqtn(err.getMessage()); + return null; + } + } + } + + } else { + logger.warn("Client references a dcaeLocation that doesn't exist:" + client.getDcaeLocationName()); + client.setStatus(DmaapObject_Status.STAGED); + } + + mr_clients.put(client.getMrClientId(), client); + + err.setCode(200); + + return client; + } + + private DmaapObject_Status addTopicToCluster(MR_Cluster cluster, Topic topic, ApiError err) { + + MrProvConnection prov = new MrProvConnection(); + logger.info("POST topic " + topic.getFqtn() + " to cluster " + cluster.getFqdn() + " in loc " + cluster.getDcaeLocationName()); + if (prov.makeTopicConnection(cluster)) { + prov.doPostTopic(topic, err); + logger.info("response code: " + err.getCode()); + if (err.is2xx() || err.getCode() == 409) { + return DmaapObject_Status.VALID; + } + } + return DmaapObject_Status.INVALID; + } + + public MR_Client updateMr_Client(MR_Client client, ApiError apiError) { + MR_Client c = mr_clients.get(client.getMrClientId()); + if (c == null) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields(MR_CLIENT_ID); + apiError.setMessage("mrClientId " + client.getMrClientId() + " not found"); + } else { + apiError.setCode(200); + } + mr_clients.put(client.getMrClientId(), client); + return client; + } + + public void removeMr_Client(String key, boolean updateTopicView, ApiError apiError) { + MR_Client client = mr_clients.get(key); + if (client == null) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields(MR_CLIENT_ID); + apiError.setMessage("mrClientId " + key + " not found"); + return; + } else { + apiError.setCode(200); + } + + if (updateTopicView) { + + TopicService topics = new TopicService(); + + Topic t = topics.getTopic(client.getFqtn(), apiError); + if (t != null) { + List<MR_Client> tc = t.getClients(); + for (MR_Client c : tc) { + if (c.getMrClientId().equals(client.getMrClientId())) { + tc.remove(c); + break; + } + } + t.setClients(tc); + topics.updateTopic(t, apiError); + } + + } + + // remove from DB + if (deleteLevel >= 1) { + mr_clients.remove(key); + } + } + + private void updateApiError(ApiError err, ApiError permissionServiceError) { + err.setCode(permissionServiceError.getCode()); + err.setMessage(permissionServiceError.getMessage()); + err.setFields(permissionServiceError.getFields()); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClusterService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClusterService.java new file mode 100644 index 0000000..db6389e --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClusterService.java @@ -0,0 +1,205 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.service; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.core.Response.Status; + +import org.onap.dmaap.dbcapi.client.MrProvConnection; +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DcaeLocation; +import org.onap.dmaap.dbcapi.model.MR_Cluster; +import org.onap.dmaap.dbcapi.model.Topic; +import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; +import org.onap.dmaap.dbcapi.service.DcaeLocationService; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + +public class MR_ClusterService extends BaseLoggingClass { + + private Map<String, MR_Cluster> mr_clusters = DatabaseClass.getMr_clusters(); + private boolean multiSite; + + public MR_ClusterService() { + logger.info( "new ClusterService"); + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + multiSite = "true".equalsIgnoreCase(p.getProperty("MR.multisite", "true")); + + } + + public Map<String, MR_Cluster> getMR_Clusters() { + return mr_clusters; + } + + public List<MR_Cluster> getAllMr_Clusters() { + return new ArrayList<MR_Cluster>(mr_clusters.values()); + } + + public MR_Cluster getMr_Cluster( String key, ApiError apiError ) { + MR_Cluster mrc = mr_clusters.get( key ); + if ( mrc == null ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields( "dcaeLocationName"); + apiError.setMessage( "Cluster with dcaeLocationName " + key + " not found"); + } + apiError.setCode(200); + return mrc; + } + public MR_Cluster getMr_ClusterByFQDN( String key ) { + for( MR_Cluster cluster: mr_clusters.values() ) { + if ( key.equals( cluster.getFqdn() ) ) { + return cluster; + } + } + return null; + } + + public MR_Cluster getMr_ClusterByLoc( String loc ) { + return mr_clusters.get( loc ); + } + + public List<MR_Cluster> getCentralClusters() { + DcaeLocationService locations = new DcaeLocationService(); + List<MR_Cluster> result = new ArrayList<MR_Cluster>(); + for( MR_Cluster c: mr_clusters.values() ) { + try { + if ( locations.getDcaeLocation(c.getDcaeLocationName()).isCentral() ) { + result.add(c); + } + } catch ( NullPointerException npe ) { + logger.warn( "Failed test isCentral for location:" + c.getDcaeLocationName() ); + } + } + return result; + } + + // builds the set of unique cluster groups + public Set<String> getGroups() { + Set<String> result = new HashSet<String>(); + for( MR_Cluster c: mr_clusters.values() ) { + try { + result.add(c.getReplicationGroup()); + } catch ( NullPointerException npe ) { + logger.warn( "Failed to add Group for cluster:" + c.getDcaeLocationName() ); + } + } + return result; + } + + + public MR_Cluster addMr_Cluster( MR_Cluster cluster, ApiError apiError ) { + logger.info( "Entry: addMr_Cluster"); + MR_Cluster mrc = mr_clusters.get( cluster.getDcaeLocationName() ); + if ( mrc != null ) { + apiError.setCode(Status.CONFLICT.getStatusCode()); + apiError.setFields( "dcaeLocationName"); + apiError.setMessage( "Cluster with dcaeLocationName " + cluster.getDcaeLocationName() + " already exists"); + return null; + } + cluster.setLastMod(); + cluster.setStatus( addTopicsToCluster( cluster, apiError ) ); + mr_clusters.put( cluster.getDcaeLocationName(), cluster ); + DcaeLocationService svc = new DcaeLocationService(); + DcaeLocation loc = svc.getDcaeLocation( cluster.getDcaeLocationName() ); + if ( loc != null && loc.isCentral() && multiSite ) { + ApiError resp = TopicService.setBridgeClientPerms( cluster ); + if ( ! resp.is2xx() ) { + logger.error( "Unable to provision Bridge to " + cluster.getDcaeLocationName() ); + cluster.setLastMod(); + cluster.setStatus(DmaapObject_Status.INVALID); + mr_clusters.put( cluster.getDcaeLocationName(), cluster ); + } + } + apiError.setCode(200); + return cluster; + } + + public MR_Cluster updateMr_Cluster( MR_Cluster cluster, ApiError apiError ) { + MR_Cluster mrc = mr_clusters.get( cluster.getDcaeLocationName() ); + if ( mrc == null ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields( "dcaeLocationName"); + apiError.setMessage( "Cluster with dcaeLocationName " + cluster.getDcaeLocationName() + " not found"); + return null; + } + cluster.setLastMod(); + cluster.setStatus( addTopicsToCluster( cluster, apiError ) ); + mr_clusters.put( cluster.getDcaeLocationName(), cluster ); + DcaeLocationService svc = new DcaeLocationService(); + DcaeLocation loc = svc.getDcaeLocation( cluster.getDcaeLocationName() ); + if ( loc == null ) { + logger.error( "DcaeLocation not found for cluster in " + cluster.getDcaeLocationName() ); + cluster.setLastMod(); + cluster.setStatus(DmaapObject_Status.INVALID); + mr_clusters.put( cluster.getDcaeLocationName(), cluster ); + } else if ( loc.isCentral() && multiSite ) { + ApiError resp = TopicService.setBridgeClientPerms( cluster ); + if ( ! resp.is2xx() ) { + logger.error( "Unable to provision Bridge to " + cluster.getDcaeLocationName() ); + cluster.setLastMod(); + cluster.setStatus(DmaapObject_Status.INVALID); + mr_clusters.put( cluster.getDcaeLocationName(), cluster ); + } + } + + apiError.setCode(200); + return cluster; + } + + public MR_Cluster removeMr_Cluster( String key, ApiError apiError ) { + MR_Cluster mrc = mr_clusters.get( key ); + if ( mrc == null ) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields( "dcaeLocationName"); + apiError.setMessage( "Cluster with dcaeLocationName " + key + " not found"); + return null; + } + apiError.setCode(200); + return mr_clusters.remove(key); + } + + private DmaapObject_Status addTopicsToCluster( MR_Cluster cluster, ApiError err ){ + + TopicService ts = new TopicService(); + MrProvConnection prov = new MrProvConnection(); + List<Topic> topics = ts.getAllTopicsWithoutClients(); + for( Topic topic: topics ) { + logger.info( "POST topic " + topic.getFqtn() + " to cluster " + cluster.getFqdn() + " in loc " + cluster.getDcaeLocationName()); + if ( prov.makeTopicConnection(cluster)) { + String resp = prov.doPostTopic(topic, err); + logger.info( "response code: " + err.getCode() ); + if ( ! err.is2xx() && ! (err.getCode() == 409) ) { + return DmaapObject_Status.INVALID; + } + } + } + + return DmaapObject_Status.VALID; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MirrorMakerService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MirrorMakerService.java new file mode 100644 index 0000000..7c4b2ce --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MirrorMakerService.java @@ -0,0 +1,255 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + + + + + + + + + +//import org.openecomp.dmaapbc.aaf.AndrewDecryptor; +import org.onap.dmaap.dbcapi.aaf.AafDecrypt; +import org.onap.dmaap.dbcapi.client.MrTopicConnection; +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.MR_Cluster; +import org.onap.dmaap.dbcapi.model.MirrorMaker; +import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; +import org.onap.dmaap.dbcapi.util.DmaapConfig; +import org.onap.dmaap.dbcapi.util.RandomInteger; + +public class MirrorMakerService extends BaseLoggingClass { + + private Map<String, MirrorMaker> mirrors = DatabaseClass.getMirrorMakers(); + private static MrTopicConnection prov; + private static AafDecrypt decryptor; + + static final String PROV_USER_PROPERTY = "MM.ProvUserMechId"; + static final String PROV_PWD_PROPERTY = "MM.ProvUserPwd"; + static final String PROV_PWD_DEFAULT = "pwdNotSet"; + static final String SOURCE_REPLICATION_PORT_PROPERTY = "MR.SourceReplicationPort"; + static final String SOURCE_REPLICATION_PORT_DEFAULT = "9092"; + static final String TARGET_REPLICATION_PORT_PROPERTY = "MR.TargetReplicationPort"; + static final String TARGET_REPLICATION_PORT_DEFAULT = "2181"; + + private static String provUser; + private static String provUserPwd; + private static String defaultProducerPort; + private static String defaultConsumerPort; + private static String centralFqdn; + private int maxTopicsPerMM; + private boolean mmPerMR; + + public MirrorMakerService() { + super(); + decryptor = new AafDecrypt(); + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + provUser = p.getProperty(PROV_USER_PROPERTY); + provUserPwd = decryptor.decrypt(p.getProperty( PROV_PWD_PROPERTY, PROV_PWD_DEFAULT )); + defaultProducerPort = p.getProperty( SOURCE_REPLICATION_PORT_PROPERTY, SOURCE_REPLICATION_PORT_DEFAULT ); + defaultConsumerPort = p.getProperty( TARGET_REPLICATION_PORT_PROPERTY, TARGET_REPLICATION_PORT_DEFAULT ); + centralFqdn = p.getProperty("MR.CentralCname", "notSet"); + maxTopicsPerMM = Integer.valueOf( p.getProperty( "MaxTopicsPerMM", "5")); + mmPerMR = "true".equalsIgnoreCase(p.getProperty("MirrorMakerPerMR", "true")); + } + + // will create a MM on MMagent if needed + // will update the MMagent whitelist with all topics for this MM + public MirrorMaker updateMirrorMaker( MirrorMaker mm ) { + logger.info( "updateMirrorMaker"); + + prov = new MrTopicConnection( provUser, provUserPwd ); + + DmaapService dmaap = new DmaapService(); + MR_ClusterService clusters = new MR_ClusterService(); + MR_Cluster target_cluster = null; + String override = null; + + if ( ! mmPerMR ) { + // in ECOMP, MM Agent is only deployed at central, so this case is needed for backwards compatibility + // we use a cname for the central MR cluster that is active, and provision on agent topic on that target + // but only send 1 message so MM Agents can read it relying on kafka delivery + for( MR_Cluster cluster: clusters.getCentralClusters() ) { + + target_cluster = cluster; + override = centralFqdn; + // we only want to send one message even if there are multiple central clusters + break; + + } + } else { + // In ONAP deployment architecture, the MM Agent is deployed with each target MR + target_cluster = clusters.getMr_ClusterByFQDN(mm.getTargetCluster()); + override = null; + } + + prov.makeTopicConnection(target_cluster, dmaap.getBridgeAdminFqtn(), override ); + ApiError resp = prov.doPostMessage(mm.createMirrorMaker( defaultConsumerPort, defaultProducerPort )); + if ( ! resp.is2xx() ) { + + errorLogger.error( DmaapbcLogMessageEnum.MM_PUBLISH_ERROR, "create MM", Integer.toString(resp.getCode()), resp.getMessage()); + mm.setStatus(DmaapObject_Status.INVALID); + } else { + prov.makeTopicConnection(target_cluster, dmaap.getBridgeAdminFqtn(), override ); + resp = prov.doPostMessage(mm.getWhitelistUpdateJSON()); + if ( ! resp.is2xx()) { + errorLogger.error( DmaapbcLogMessageEnum.MM_PUBLISH_ERROR,"MR Bridge", Integer.toString(resp.getCode()), resp.getMessage()); + mm.setStatus(DmaapObject_Status.INVALID); + } else { + mm.setStatus(DmaapObject_Status.VALID); + } + } + + mm.setLastMod(); + return mirrors.put( mm.getMmName(), mm); + } + public MirrorMaker getMirrorMaker( String part1, String part2, int index ) { + String targetPart; + + // original mm names did not have any index, so leave off index 0 for + // backwards compatibility + if ( index == 0 ) { + targetPart = part2; + } else { + targetPart = part2 + "_" + index; + } + logger.info( "getMirrorMaker using " + part1 + " and " + targetPart ); + return mirrors.get(MirrorMaker.genKey(part1, targetPart)); + } + public MirrorMaker getMirrorMaker( String part1, String part2 ) { + logger.info( "getMirrorMaker using " + part1 + " and " + part2 ); + return mirrors.get(MirrorMaker.genKey(part1, part2)); + } + public MirrorMaker getMirrorMaker( String key ) { + logger.info( "getMirrorMaker using " + key); + return mirrors.get(key); + } + + + public void delMirrorMaker( MirrorMaker mm ) { + logger.info( "delMirrorMaker"); + mirrors.remove(mm.getMmName()); + } + + // TODO: this should probably return sequential values or get replaced by the MM client API + // but it should be sufficient for initial 1610 development + public static String genTransactionId() { + RandomInteger ri = new RandomInteger(100000); + int randomInt = ri.next(); + return Integer.toString(randomInt); + } + public List<String> getAllMirrorMakers() { + List<String> ret = new ArrayList<String>(); + for( String key: mirrors.keySet()) { + ret.add( key ); + } + + return ret; + } + + public MirrorMaker findNextMM( String source, String target, String fqtn ) { + int i = 0; + MirrorMaker mm = null; + while( mm == null ) { + + mm = this.getMirrorMaker( source, target, i); + if ( mm == null ) { + mm = new MirrorMaker(source, target, i); + } + if ( mm.getTopics().contains(fqtn) ) { + break; + } + if ( mm.getTopicCount() >= maxTopicsPerMM ) { + logger.info( "getNextMM: MM " + mm.getMmName() + " has " + mm.getTopicCount() + " topics. Moving to next MM"); + i++; + mm = null; + } + } + + + return mm; + } + + public MirrorMaker splitMM( MirrorMaker orig ) { + + String source = orig.getSourceCluster(); + String target = orig.getTargetCluster(); + + + ArrayList<String> whitelist = orig.getTopics(); + while( whitelist.size() > maxTopicsPerMM ) { + + int last = whitelist.size() - 1; + String topic = whitelist.get(last); + whitelist.remove(last); + MirrorMaker mm = this.findNextMM( source, target, "aValueThatShouldNotMatchAnything" ); + mm.addTopic(topic); + this.updateMirrorMaker(mm); + } + + orig.setTopics(whitelist); + + return orig; + + } + + public static String getProvUser() { + return provUser; + } + + public static void setProvUser(String provUser) { + MirrorMakerService.provUser = provUser; + } + + public static String getProvUserPwd() { + return provUserPwd; + } + + public static void setProvUserPwd(String provUserPwd) { + MirrorMakerService.provUserPwd = provUserPwd; + } + + public static String getDefaultProducerPort() { + return defaultProducerPort; + } + + public static void setDefaultProducerPort(String defaultProducerPort) { + MirrorMakerService.defaultProducerPort = defaultProducerPort; + } + + public static String getDefaultConsumerPort() { + return defaultConsumerPort; + } + + public static void setDefaultConsumerPort(String defaultConsumerPort) { + MirrorMakerService.defaultConsumerPort = defaultConsumerPort; + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/TopicService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/TopicService.java new file mode 100644 index 0000000..009b745 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/TopicService.java @@ -0,0 +1,526 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.service; + +import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType; +import org.onap.dmaap.dbcapi.aaf.AafServiceFactory; +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; +import org.onap.dmaap.dbcapi.model.ApiError; +import org.onap.dmaap.dbcapi.model.DcaeLocation; +import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; +import org.onap.dmaap.dbcapi.model.MR_Client; +import org.onap.dmaap.dbcapi.model.MR_Cluster; +import org.onap.dmaap.dbcapi.model.MirrorMaker; +import org.onap.dmaap.dbcapi.model.ReplicationType; +import org.onap.dmaap.dbcapi.model.Topic; +import org.onap.dmaap.dbcapi.util.DmaapConfig; +import org.onap.dmaap.dbcapi.util.Fqdn; +import org.onap.dmaap.dbcapi.util.Graph; + +import javax.ws.rs.core.Response.Status; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class TopicService extends BaseLoggingClass { + + + // REF: https://wiki.web.att.com/pages/viewpage.action?pageId=519703122 + private static String defaultGlobalMrHost; + + private Map<String, Topic> mr_topics; + + private static DmaapService dmaapSvc = new DmaapService(); + private MR_ClientService clientService; + private MR_ClusterService clusters; + private DcaeLocationService locations; + private MirrorMakerService bridge; + private AafTopicSetupService aafTopicSetupService; + + private static String centralCname; + private boolean strictGraph = true; + private boolean mmPerMR; + + + public TopicService() { + this(DatabaseClass.getTopics(), new MR_ClientService(), (DmaapConfig) DmaapConfig.getConfig(), + new MR_ClusterService(), new DcaeLocationService(), new MirrorMakerService(), + new AafTopicSetupService( + new AafServiceFactory().initAafService(ServiceType.AAF_TopicMgr), + dmaapSvc, (DmaapConfig) DmaapConfig.getConfig())); + } + + TopicService(Map<String, Topic> mr_topics, MR_ClientService clientService, DmaapConfig p, + MR_ClusterService clusters, DcaeLocationService locations, MirrorMakerService bridge, AafTopicSetupService aafTopicSetupService) { + this.mr_topics = mr_topics; + this.clientService = clientService; + defaultGlobalMrHost = p.getProperty("MR.globalHost", "global.host.not.set"); + centralCname = p.getProperty("MR.CentralCname"); + String unit_test = p.getProperty("UnitTest", "No"); + if ("Yes".equals(unit_test)) { + strictGraph = false; + } + mmPerMR = "true".equalsIgnoreCase(p.getProperty("MirrorMakerPerMR", "true")); + logger.info("TopicService properties: CentralCname=" + centralCname + + " defaultGlobarlMrHost=" + defaultGlobalMrHost + + " mmPerMR=" + mmPerMR); + this.clusters = clusters; + this.locations = locations; + this.bridge = bridge; + this.aafTopicSetupService = aafTopicSetupService; + } + + public Map<String, Topic> getTopics() { + return mr_topics; + } + + public List<Topic> getAllTopics() { + return getAllTopics(true); + } + + public List<Topic> getAllTopicsWithoutClients() { + return getAllTopics(false); + } + + private List<Topic> getAllTopics(Boolean withClients) { + ArrayList<Topic> topics = new ArrayList<>(mr_topics.values()); + if (withClients) { + for (Topic topic : topics) { + topic.setClients(clientService.getAllMrClients(topic.getFqtn())); + } + } + return topics; + } + + public Topic getTopic(String key, ApiError apiError) { + logger.info("getTopic: key=" + key); + Topic t = mr_topics.get(key); + if (t == null) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setFields("fqtn"); + apiError.setMessage("topic with fqtn " + key + " not found"); + return null; + } + t.setClients(clientService.getAllMrClients(key)); + apiError.setCode(Status.OK.getStatusCode()); + return t; + } + + public Topic addTopic(Topic topic, ApiError err, Boolean useExisting) { + logger.info("Entry: addTopic"); + logger.info("Topic name=" + topic.getTopicName() + " fqtnStyle=" + topic.getFqtnStyle()); + String nFqtn = topic.genFqtn(); + logger.info("FQTN=" + nFqtn); + Topic pTopic = getTopic(nFqtn, err); + if (pTopic != null) { + String t = "topic already exists: " + nFqtn; + logger.info(t); + if (useExisting) { + err.setCode(Status.OK.getStatusCode()); + return pTopic; + } + err.setMessage(t); + err.setFields("fqtn"); + err.setCode(Status.CONFLICT.getStatusCode()); + return null; + } + err.reset(); // err filled with NOT_FOUND is expected case, but don't want to litter... + + topic.setFqtn(nFqtn); + + ApiError topicSetupError = aafTopicSetupService.aafTopicSetup(topic); + updateApiError(err, topicSetupError); + if (err.getCode() >= 400) { + return null; + } + + if (topic.getReplicationCase().involvesGlobal()) { + if (topic.getGlobalMrURL() == null) { + topic.setGlobalMrURL(defaultGlobalMrHost); + } + if (!Fqdn.isValid(topic.getGlobalMrURL())) { + logger.error("GlobalMR FQDN not valid: " + topic.getGlobalMrURL()); + topic.setStatus(DmaapObject_Status.INVALID); + err.setCode(500); + err.setMessage("Value is not a valid FQDN:" + topic.getGlobalMrURL()); + err.setFields("globalMrURL"); + + return null; + } + } + + + if (topic.getNumClients() > 0) { + ArrayList<MR_Client> clients = new ArrayList<MR_Client>(topic.getClients()); + + + ArrayList<MR_Client> clients2 = new ArrayList<MR_Client>(); + for (Iterator<MR_Client> it = clients.iterator(); it.hasNext(); ) { + MR_Client c = it.next(); + + logger.info("c fqtn=" + c.getFqtn() + " ID=" + c.getMrClientId() + " url=" + c.getTopicURL()); + MR_Client nc = new MR_Client(c.getDcaeLocationName(), topic.getFqtn(), c.getClientRole(), c.getAction()); + nc.setFqtn(topic.getFqtn()); + nc.setClientIdentity(c.getClientIdentity()); + logger.info("nc fqtn=" + nc.getFqtn() + " ID=" + nc.getMrClientId() + " url=" + nc.getTopicURL()); + clients2.add(clientService.addMr_Client(nc, topic, err)); + if (!err.is2xx()) { + return null; + } + } + + topic.setClients(clients2); + } + + Topic ntopic = checkForBridge(topic, err); + if (ntopic == null) { + topic.setStatus(DmaapObject_Status.INVALID); + if (!err.is2xx()) { + return null; + } + } + + + mr_topics.put(nFqtn, ntopic); + + err.setCode(Status.OK.getStatusCode()); + return ntopic; + } + + + public Topic updateTopic(Topic topic, ApiError err) { + logger.info("updateTopic: entry"); + logger.info("updateTopic: topic=" + topic); + logger.info("updateTopic: fqtn=" + topic.getFqtn()); + if (topic.getFqtn().isEmpty()) { + return null; + } + logger.info("updateTopic: call checkForBridge"); + Topic ntopic = checkForBridge(topic, err); + if (ntopic == null) { + topic.setStatus(DmaapObject_Status.INVALID); + if (!err.is2xx()) { + return null; + } + } + if (ntopic != null) { + logger.info("updateTopic: call put"); + mr_topics.put(ntopic.getFqtn(), ntopic); + } + err.setCode(Status.OK.getStatusCode()); + return ntopic; + } + + public Topic removeTopic(String pubId, ApiError apiError) { + Topic topic = mr_topics.get(pubId); + if (topic == null) { + apiError.setCode(Status.NOT_FOUND.getStatusCode()); + apiError.setMessage("Topic " + pubId + " does not exist"); + apiError.setFields("fqtn"); + return null; + } + + ApiError topicSetupError = aafTopicSetupService.aafTopicCleanup(topic); + updateApiError(apiError, topicSetupError); + if (apiError.getCode() >= 400) { + return null; + } + + ArrayList<MR_Client> clients = new ArrayList<MR_Client>(clientService.getAllMrClients(pubId)); + for (Iterator<MR_Client> it = clients.iterator(); it.hasNext(); ) { + MR_Client c = it.next(); + clientService.removeMr_Client(c.getMrClientId(), false, apiError); + if (!apiError.is2xx()) { + return null; + } + } + apiError.setCode(Status.OK.getStatusCode()); + return mr_topics.remove(pubId); + } + + public static ApiError setBridgeClientPerms(MR_Cluster node) { + DmaapConfig p = (DmaapConfig) DmaapConfig.getConfig(); + String mmProvRole = p.getProperty("MM.ProvRole"); + String mmAgentRole = p.getProperty("MM.AgentRole"); + String[] Roles = {mmProvRole, mmAgentRole}; + String[] actions = {"view", "pub", "sub"}; + Topic bridgeAdminTopic = new Topic().init(); + bridgeAdminTopic.setTopicName(dmaapSvc.getBridgeAdminFqtn()); + bridgeAdminTopic.setTopicDescription("RESERVED topic for MirroMaker Provisioning"); + bridgeAdminTopic.setOwner("DBC"); + + ArrayList<MR_Client> clients = new ArrayList<MR_Client>(); + for (String role : Roles) { + MR_Client client = new MR_Client(); + client.setAction(actions); + client.setClientRole(role); + client.setDcaeLocationName(node.getDcaeLocationName()); + clients.add(client); + } + bridgeAdminTopic.setClients(clients); + + TopicService ts = new TopicService(); + ApiError err = new ApiError(); + ts.addTopic(bridgeAdminTopic, err, true); + + if (err.is2xx() || err.getCode() == 409) { + err.setCode(200); + return err; + } + + errorLogger.error(DmaapbcLogMessageEnum.TOPIC_CREATE_ERROR, bridgeAdminTopic.getFqtn(), Integer.toString(err.getCode()), err.getFields(), err.getMessage()); + return err; + } + + + public Topic checkForBridge(Topic topic, ApiError err) { + logger.info("checkForBridge: entry"); + logger.info("fqtn=" + topic.getFqtn() + " replicatonType=" + topic.getReplicationCase()); + if (topic.getReplicationCase() == ReplicationType.REPLICATION_NONE) { + topic.setStatus(DmaapObject_Status.VALID); + return topic; + } + + boolean anythingWrong = false; + + Set<String> groups = clusters.getGroups(); + for (String g : groups) { + logger.info("buildBridge for " + topic.getFqtn() + " on group" + g); + anythingWrong |= buildBridge(topic, err, g); + } + if (anythingWrong) { + topic.setStatus(DmaapObject_Status.INVALID); + if (!err.is2xx()) { + return null; + } + } else { + topic.setStatus(DmaapObject_Status.VALID); + } + return topic; + } + + private boolean buildBridge(Topic topic, ApiError err, String group) { + logger.info("buildBridge: entry"); + boolean anythingWrong = false; + Graph graph; + logger.info("buildBridge: strictGraph=" + strictGraph); + if (group == null || group.isEmpty()) { + graph = new Graph(topic.getClients(), strictGraph); + } else { + graph = new Graph(topic.getClients(), strictGraph, group); + } + logger.info("buildBridge: graph=" + graph); + MR_Cluster groupCentralCluster = null; + + + if (graph.isEmpty()) { + logger.info("buildBridge: graph is empty. return false"); + return false; + } else if (group == null && topic.getReplicationCase().involvesFQDN()) { + logger.info("buildBridge: group is null and replicationCaseInvolvesFQDN. return false"); + return false; + } else if (!graph.hasCentral()) { + logger.warn("Topic " + topic.getFqtn() + " wants to be " + topic.getReplicationCase() + " but has no central clients"); + return true; + } else { + groupCentralCluster = clusters.getMr_ClusterByLoc(graph.getCentralLoc()); + } + Collection<String> clientLocations = graph.getKeys(); + for (String loc : clientLocations) { + logger.info("loc=" + loc); + DcaeLocation location = locations.getDcaeLocation(loc); + MR_Cluster cluster = clusters.getMr_ClusterByLoc(loc); + logger.info("cluster=" + cluster + " at " + cluster.getDcaeLocationName()); + logger.info("location.isCentral()=" + location.isCentral() + " getCentralLoc()=" + graph.getCentralLoc()); + + + String source = null; + String target = null; + + /* + * Provision Edge to Central bridges... + */ + if (!location.isCentral() && !graph.getCentralLoc().equals(cluster.getDcaeLocationName())) { + switch (topic.getReplicationCase()) { + case REPLICATION_EDGE_TO_CENTRAL: + case REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL: // NOTE: this is for E2C portion only + source = cluster.getFqdn(); + target = (mmPerMR) ? groupCentralCluster.getFqdn() : centralCname; + logger.info("REPLICATION_EDGE_TO_CENTRAL: source=" + source + " target=" + target); + break; + case REPLICATION_CENTRAL_TO_EDGE: + case REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE: // NOTE: this is for C2E portion only + source = (mmPerMR) ? groupCentralCluster.getFqdn() : centralCname; + target = cluster.getFqdn(); + break; + case REPLICATION_CENTRAL_TO_GLOBAL: + case REPLICATION_GLOBAL_TO_CENTRAL: + case REPLICATION_FQDN_TO_GLOBAL: + case REPLICATION_GLOBAL_TO_FQDN: + break; + + case REPLICATION_EDGE_TO_FQDN: + case REPLICATION_EDGE_TO_FQDN_TO_GLOBAL: // NOTE: this is for E2C portion only + source = cluster.getFqdn(); + target = groupCentralCluster.getFqdn(); + break; + case REPLICATION_FQDN_TO_EDGE: + case REPLICATION_GLOBAL_TO_FQDN_TO_EDGE: // NOTE: this is for F2E portion only + source = groupCentralCluster.getFqdn(); + target = cluster.getFqdn(); + break; + + default: + logger.error("Unexpected value for ReplicationType (" + topic.getReplicationCase() + ") for topic " + topic.getFqtn()); + anythingWrong = true; + err.setCode(400); + err.setFields("topic=" + topic.genFqtn() + " replicationCase=" + + topic.getReplicationCase()); + err.setMessage("Unexpected value for ReplicationType"); + continue; + } + + } else if (location.isCentral() && graph.getCentralLoc().equals(cluster.getDcaeLocationName())) { + /* + * Provision Central to Global bridges + */ + switch (topic.getReplicationCase()) { + + case REPLICATION_CENTRAL_TO_GLOBAL: + case REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL: + source = centralCname; + target = topic.getGlobalMrURL(); + break; + case REPLICATION_GLOBAL_TO_CENTRAL: + case REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE: // NOTE: this is for G2C portion only + source = topic.getGlobalMrURL(); + target = centralCname; + break; + + case REPLICATION_EDGE_TO_FQDN_TO_GLOBAL: // NOTE: this is for E2F portion only + source = groupCentralCluster.getFqdn(); + target = topic.getGlobalMrURL(); + break; + + case REPLICATION_FQDN_TO_GLOBAL: + source = groupCentralCluster.getFqdn(); + target = topic.getGlobalMrURL(); + break; + + case REPLICATION_GLOBAL_TO_FQDN: + case REPLICATION_GLOBAL_TO_FQDN_TO_EDGE: // NOTE: this is for G2F portion only + source = topic.getGlobalMrURL(); + target = groupCentralCluster.getFqdn(); + break; + + case REPLICATION_FQDN_TO_EDGE: + case REPLICATION_EDGE_TO_FQDN: + case REPLICATION_EDGE_TO_CENTRAL: + case REPLICATION_CENTRAL_TO_EDGE: + break; + default: + logger.error("Unexpected value for ReplicationType (" + topic.getReplicationCase() + ") for topic " + topic.getFqtn()); + anythingWrong = true; + err.setCode(400); + err.setFields("topic=" + topic.genFqtn() + " replicationCase=" + + topic.getReplicationCase()); + err.setMessage("Unexpected value for ReplicationType"); + continue; + } + } else { + logger.warn("dcaeLocation " + loc + " is neither Edge nor Central so no mmagent provisioning was done"); + anythingWrong = true; + continue; + } + if (source != null && target != null) { + try { + logger.info("Create a MM from " + source + " to " + target); + MirrorMaker mm = bridge.findNextMM(source, target, topic.getFqtn()); + mm.addTopic(topic.getFqtn()); + bridge.updateMirrorMaker(mm); + } catch (Exception ex) { + err.setCode(500); + err.setFields("mirror_maker.topic"); + err.setMessage("Unexpected condition: " + ex); + anythingWrong = true; + break; + } + } + + + } + return anythingWrong; + + } + + + /* + * Prior to 1707, we only supported EDGE_TO_CENTRAL replication. + * This was determined automatically based on presence of edge publishers and central subscribers. + * The following method is a modification of that original logic, to preserve some backwards compatibility, + * i.e. to be used when no ReplicationType is specified. + */ + + public ReplicationType reviewTopic(Topic topic) { + + + if (topic.getNumClients() > 1) { + Graph graph = new Graph(topic.getClients(), false); + + String centralFqdn = new String(); + if (graph.hasCentral()) { + DmaapConfig p = (DmaapConfig) DmaapConfig.getConfig(); + centralFqdn = p.getProperty("MR.CentralCname"); + } + + Collection<String> locations = graph.getKeys(); + for (String loc : locations) { + logger.info("loc=" + loc); + MR_Cluster cluster = clusters.getMr_ClusterByLoc(loc); + if (cluster == null) { + logger.info("No MR cluster for location " + loc); + continue; + } + if (graph.hasCentral() && !graph.getCentralLoc().equals(cluster.getDcaeLocationName())) { + logger.info("Detected case for EDGE_TO_CENTRAL from " + cluster.getFqdn() + " to " + centralFqdn); + return ReplicationType.REPLICATION_EDGE_TO_CENTRAL; + + } + + } + } + + return ReplicationType.REPLICATION_NONE; + } + + private void updateApiError(ApiError err, ApiError topicSetupError) { + err.setCode(topicSetupError.getCode()); + err.setMessage(topicSetupError.getMessage()); + err.setFields(topicSetupError.getFields()); + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapConfig.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapConfig.java new file mode 100644 index 0000000..e95ebab --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapConfig.java @@ -0,0 +1,78 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.util; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import java.io.*; +import java.security.KeyStore; +import java.util.*; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import org.onap.dmaap.dbcapi.server.CertificateManager; +import org.onap.dmaap.dbcapi.server.JettyServer; + +public class DmaapConfig extends Properties { + + private static final EELFLogger logger = EELFManager.getInstance().getLogger(DmaapConfig.class); + private static final long serialVersionUID = 1L; + private static final String CONFIG_FILE_NAME = System.getProperty("ConfigFile", "/opt/app/config/conf/dmaapbc.properties"); + private static final Properties config = new DmaapConfig(); + + public static Properties getConfig() { + return(config); + } + public static String getConfigFileName() { + return(CONFIG_FILE_NAME); + } + private DmaapConfig() { + try (InputStream is = new FileInputStream(CONFIG_FILE_NAME)){ + load(is); + } catch (Exception e) { + logger.error("Unable to load configuration file " + CONFIG_FILE_NAME); + System.exit(1); + } + } + + public static SSLSocketFactory getSSLSocketFactory() { + SSLSocketFactory factory = null; + try { + CertificateManager cm = JettyServer.getCertificateManager(); + String truststore = cm.getTrustStoreFile(); + KeyStore ts = KeyStore.getInstance(cm.getTrustStoreType()); + try (InputStream in = new FileInputStream(truststore)) { + ts.load(in, cm.getTrustStorePassword().toCharArray()); + } + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(ts); + TrustManager[] tm = tmf.getTrustManagers(); + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, tm, null); + factory = sslContext.getSocketFactory(); + } catch (Exception e) { + logger.error("Exception thrown trying to get SSLSocketFactory: ", e); + } + return factory; + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapTimestamp.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapTimestamp.java new file mode 100644 index 0000000..f36df1c --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapTimestamp.java @@ -0,0 +1,46 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.util; + +import javax.xml.bind.annotation.XmlRootElement; +import java.util.Date; + +@XmlRootElement +public class DmaapTimestamp { + private Date stamp; + + public DmaapTimestamp() { + this(new Date()); + } + + DmaapTimestamp(Date stamp) { + this.stamp = new Date(stamp.getTime()); + } + + public void mark() { + stamp = new Date(); + } + + public Date getVal() { + return new Date(stamp.getTime()); + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Fqdn.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Fqdn.java new file mode 100644 index 0000000..37715d3 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Fqdn.java @@ -0,0 +1,42 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * 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.dmaap.dbcapi.util; + +import java.util.regex.Pattern; + +import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; + +public class Fqdn extends BaseLoggingClass { + // regexp value sourced from https://www.regextester.com/23 + static String regexp = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$"; + + + public static boolean isValid( String s ) { + appLogger.info( "Fqdn testing: " + s ); + boolean b = false; + if ( s != null ) { + b = Pattern.matches( regexp, s); + } + appLogger.info( "Fqdn isValid=" + b ); + return b; + } + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Graph.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Graph.java new file mode 100644 index 0000000..ab40765 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Graph.java @@ -0,0 +1,132 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright (C) 2019 IBM. + * ================================================================================ + * 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.dmaap.dbcapi.util; + +import org.onap.dmaap.dbcapi.database.DatabaseClass; +import org.onap.dmaap.dbcapi.model.DcaeLocation; +import org.onap.dmaap.dbcapi.model.MR_Client; +import org.onap.dmaap.dbcapi.model.MR_Cluster; +import org.onap.dmaap.dbcapi.service.MR_ClusterService; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +public class Graph { + private HashMap<String, String> graph; + private boolean hasCentral; + + private Map<String, DcaeLocation> locations = DatabaseClass.getDcaeLocations(); + + //TODO add to properties file + private static String centralDcaeLayerName = "central"; + + + public Graph(HashMap<String, String> graph) { + super(); + this.graph = graph; + } + + public Graph( List<MR_Client> clients, boolean strict ) { + if ( clients == null ) + return; + initGraph( clients, strict, "" ); + + } + public Graph( List<MR_Client> clients, boolean strict, String group ) { + if ( clients == null ) + return; + initGraph( clients, strict, group ); + } + + private void initGraph(List<MR_Client> clients, boolean strict, String group ) { + MR_ClusterService clusters = new MR_ClusterService(); + this.graph = new HashMap<>(); + this.hasCentral = false; + for( MR_Client client: clients ) { + if ( ! strict || client.isStatusValid()) { + String loc = client.getDcaeLocationName(); + DcaeLocation dcaeLoc = locations.get(loc); + if ( dcaeLoc == null ) continue; + MR_Cluster c = clusters.getMr_ClusterByLoc(loc); + if ( group != null && ! group.isEmpty() && ! group.equals(c.getReplicationGroup())) continue; + + for( String action : client.getAction() ){ + if ( ! action.equals("view") && dcaeLoc != null ) { + String layer = dcaeLoc.getDcaeLayer(); + if ( layer != null && layer.contains(centralDcaeLayerName) ) { + this.hasCentral = true; + } + graph.put(loc, layer); + } + } + + } + } + } + + public HashMap<String, String> getGraph() { + return graph; + } + + public void setGraph(HashMap<String, String> graph) { + this.graph = graph; + } + + public String put( String key, String val ) { + return graph.put(key, val); + } + + public String get( String key ) { + return graph.get(key); + } + + public Collection<String> getKeys() { + return graph.keySet(); + } + public boolean hasCentral() { + return hasCentral; + } + public void setHasCentral(boolean hasCentral) { + this.hasCentral = hasCentral; + } + + public String getCentralLoc() { + if ( ! hasCentral ) { + return null; + } + for( String loc : graph.keySet()) { + if ( graph.get(loc).contains(centralDcaeLayerName)) { + return loc; + } + } + return null; + } + public boolean isEmpty() { + return graph.isEmpty(); + } + + +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/PermissionBuilder.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/PermissionBuilder.java new file mode 100644 index 0000000..d1f6b2b --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/PermissionBuilder.java @@ -0,0 +1,83 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2019 Nokia 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.dmaap.dbcapi.util; + +import javax.servlet.http.HttpServletRequest; +import org.onap.dmaap.dbcapi.model.Dmaap; +import org.onap.dmaap.dbcapi.service.DmaapService; + +public class PermissionBuilder { + + static final String API_NS_PROP = "ApiNamespace"; + static final String DEFAULT_API_NS = "org.onap.dmaap-bc.api"; + static final String BOOT_INSTANCE = "boot"; + private static final String PERM_SEPARATOR = "|"; + private static final String NS_SEPARATOR = "."; + private DmaapConfig dmaapConfig; + private DmaapService dmaapService; + private String instance; + private String apiNamespace; + + public PermissionBuilder(DmaapConfig dmaapConfig, DmaapService dmaapService) { + this.dmaapConfig = dmaapConfig; + this.dmaapService = dmaapService; + initFields(); + } + + public synchronized void updateDmaapInstance() { + if(instance == null || instance.isEmpty() || instance.equalsIgnoreCase(BOOT_INSTANCE)) { + String dmaapName = getDmaapName(); + instance = (dmaapName == null || dmaapName.isEmpty()) ? BOOT_INSTANCE : dmaapName; + } + } + + public String buildPermission(HttpServletRequest httpRequest) { + + StringBuilder sb = new StringBuilder(apiNamespace); + sb.append(NS_SEPARATOR) + .append(getPermissionType(httpRequest.getPathInfo())) + .append(PERM_SEPARATOR) + .append(instance) + .append(PERM_SEPARATOR) + .append(httpRequest.getMethod()); + return sb.toString(); + } + + + private void initFields() { + apiNamespace = dmaapConfig.getProperty(API_NS_PROP, DEFAULT_API_NS); + updateDmaapInstance(); + } + + private String getDmaapName() { + Dmaap dmaap = dmaapService.getDmaap(); + return ( dmaap != null ) ? dmaap.getDmaapName() : BOOT_INSTANCE; + } + + private String getPermissionType(String pathInfo) { + char pathSeparator = '/'; + String[] pathSlices = pathInfo.split(String.valueOf(pathSeparator)); + return pathSlices[1]; + } + + String getInstance() { + return instance; + } +} diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomInteger.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomInteger.java new file mode 100644 index 0000000..1c9bc11 --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomInteger.java @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.util; + +import java.util.Date; +// source: http://www.javapractices.com/topic/TopicAction.do?Id=62 +// with some modifications +import java.util.Random; + +public final class RandomInteger { + private static Random randomGenerator; + private int range; + + public RandomInteger( int r ) { + randomGenerator = new Random(); + randomGenerator.setSeed((new Date()).getTime()); + range = r; + } + + public int next(){ + return randomGenerator.nextInt(range); + } + +} + diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomString.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomString.java new file mode 100644 index 0000000..b889c1c --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomString.java @@ -0,0 +1,55 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.util; + +import java.util.Random; + +// source: http://stackoverflow.com/questions/41107/how-to-generate-a-random-alpha-numeric-string + +public class RandomString { + + private static final char[] symbols; + + static { + StringBuilder tmp = new StringBuilder(); + for (char ch = '0'; ch <= '9'; ++ch) + tmp.append(ch); + for (char ch = 'a'; ch <= 'z'; ++ch) + tmp.append(ch); + symbols = tmp.toString().toCharArray(); + } + + private final Random random = new Random(); + + private final char[] buf; + + public RandomString(int length) { + if (length < 1) + throw new IllegalArgumentException("length < 1: " + length); + buf = new char[length]; + } + + public String nextString() { + for (int idx = 0; idx < buf.length; ++idx) + buf[idx] = symbols[random.nextInt(symbols.length)]; + return new String(buf); + } + } diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Singleton.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Singleton.java new file mode 100644 index 0000000..3ef6a3d --- /dev/null +++ b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Singleton.java @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 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.dmaap.dbcapi.util; + +public interface Singleton<T> { + public T get(); + public void init(T val); + public void update(T val); + public void remove(); +} |