Collection syntax change because of Sonar
[aaf/authz.git] / auth / auth-cass / src / main / java / org / onap / aaf / auth / dao / CassAccess.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.auth.dao;
23
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import org.onap.aaf.auth.env.AuthzEnv;
29 import org.onap.aaf.cadi.config.Config;
30 import org.onap.aaf.cadi.routing.GreatCircle;
31 import org.onap.aaf.misc.env.APIException;
32 import org.onap.aaf.misc.env.Env;
33 import org.onap.aaf.misc.env.util.Split;
34
35 import com.datastax.driver.core.Cluster;
36 import com.datastax.driver.core.Cluster.Builder;
37 import com.datastax.driver.core.policies.DCAwareRoundRobinPolicy;
38 import com.datastax.driver.core.policies.TokenAwarePolicy;
39
40 public class CassAccess {
41         public static final String KEYSPACE = "authz";
42         public static final String CASSANDRA_CLUSTERS = "cassandra.clusters";
43         public static final String CASSANDRA_CLUSTERS_PORT = "cassandra.clusters.port";
44         public static final String CASSANDRA_CLUSTERS_USER_NAME = "cassandra.clusters.user";
45         public static final String CASSANDRA_CLUSTERS_PASSWORD = "cassandra.clusters.password";
46         public static final String CASSANDRA_RESET_EXCEPTIONS = "cassandra.reset.exceptions";
47         private static final List<Resettable> resetExceptions = new ArrayList<>();
48         public static final String ERR_ACCESS_MSG = "Accessing Backend";
49         private static Builder cb = null;
50
51         /**
52          * To create DCAwareRoundRobing Policy:
53          *       Need Properties
54          *              LATITUDE (or AFT_LATITUDE)
55          *              LONGITUDE (or AFT_LONGITUDE)
56          *              CASSANDRA CLUSTERS with additional information:
57          *                      machine:DC:lat:long,machine:DC:lat:long
58          * @param env
59          * @param prefix
60          * @return
61          * @throws APIException
62          * @throws IOException
63          */
64
65 //      @SuppressWarnings("deprecation")
66         public static synchronized Cluster cluster(Env env, String prefix) throws APIException, IOException {
67                 if(cb == null) {
68                         String pre;
69                         if(prefix==null) {
70                                 pre="";
71                         } else {
72                                 env.info().log("Cassandra Connection for ",prefix);
73                                 pre = prefix+'.';
74                         }
75                         cb = Cluster.builder();
76                         String str = env.getProperty(pre+CASSANDRA_CLUSTERS_PORT,env.getProperty(CASSANDRA_CLUSTERS_PORT,"9042"));
77                         if(str!=null) {
78                                 env.init().log("Cass Port = ",str );
79                                 cb.withPort(Integer.parseInt(str));
80                         }
81                         str = env.getProperty(pre+CASSANDRA_CLUSTERS_USER_NAME,env.getProperty(CASSANDRA_CLUSTERS_USER_NAME,null));
82                         if(str!=null) {
83                                 env.init().log("Cass User = ",str );
84                                 String epass = env.getProperty(pre + CASSANDRA_CLUSTERS_PASSWORD,env.getProperty(CASSANDRA_CLUSTERS_PASSWORD,null));
85                                 if(epass==null) {
86                                         throw new APIException("No Password configured for " + str);
87                                 }
88                                 //TODO Figure out way to ensure Decryptor setting in AuthzEnv
89                                 if(env instanceof AuthzEnv) {
90                                         cb.withCredentials(str,((AuthzEnv)env).decrypt(epass,true));
91                                 } else {
92                                         cb.withCredentials(str, env.decryptor().decrypt(epass));
93                                 }
94                         }
95         
96                         str = env.getProperty(pre+CASSANDRA_RESET_EXCEPTIONS,env.getProperty(CASSANDRA_RESET_EXCEPTIONS,null));
97                         if(str!=null) {
98                                 env.init().log("Cass ResetExceptions = ",str );
99                                 for(String ex : Split.split(',', str)) {
100                                         resetExceptions.add(new Resettable(env,ex));
101                                 }
102                         }
103         
104                         str = env.getProperty(Config.CADI_LATITUDE);
105                         Double lat = str!=null?Double.parseDouble(str):null;
106                         str = env.getProperty(Config.CADI_LONGITUDE);
107                         Double lon = str!=null?Double.parseDouble(str):null;
108                         if(lat == null || lon == null) {
109                                 throw new APIException(Config.CADI_LATITUDE + " and/or " + Config.CADI_LONGITUDE + " are not set");
110                         }
111                         
112                         env.init().printf("Service Latitude,Longitude = %f,%f",lat,lon);
113                         
114                         str = env.getProperty(pre+CASSANDRA_CLUSTERS,env.getProperty(CASSANDRA_CLUSTERS,"localhost"));
115                         env.init().log("Cass Clusters = ",str );
116                         String[] machs = Split.split(',', str);
117                         String[] cpoints = new String[machs.length];
118                         String bestDC = null;
119                         int numInBestDC = 1;
120                         double mlat, mlon,temp,distance = Double.MAX_VALUE;
121                         for(int i=0;i<machs.length;++i) {
122                                 String[] minfo = Split.split(':',machs[i]);
123                                 if(minfo.length>0) {
124                                         cpoints[i]=minfo[0];
125                                 }
126                                 
127                                 if(minfo.length>3) {
128                                         if(minfo[1].equals(bestDC)) {
129                                                 ++numInBestDC;
130                                         } else {
131                                                 // Calc closest DC with Great Circle
132                                                 mlat = Double.parseDouble(minfo[2]);
133                                                 mlon = Double.parseDouble(minfo[3]);
134                                                 // Note: GreatCircle Distance is always >= 0.0 (not negative)
135                                                 if((temp=GreatCircle.calc(lat, lon, mlat, mlon)) < distance) {
136                                                         distance = temp;
137                                                         if(bestDC==null || !bestDC.equals(minfo[1])) {
138                                                                 bestDC = minfo[1];
139                                                                 numInBestDC = 1;
140                                                         }
141                                                 }
142                                         }
143                                 }
144                         }
145                         
146                         cb.addContactPoints(cpoints);
147                         
148                         if(bestDC!=null) {
149                                 // 8/26/2016 Management has determined that Accuracy is preferred over speed in bad situations
150                                 // Local DC Aware Load Balancing appears to have the highest normal performance, with the best
151                                 // Degraded Accuracy
152                                 DCAwareRoundRobinPolicy dcrrPolicy = DCAwareRoundRobinPolicy.builder()
153                                         .withLocalDc(bestDC)
154                                         .withUsedHostsPerRemoteDc(numInBestDC)
155                                         .build();
156 //                              cb.withLoadBalancingPolicy(new DCAwareRoundRobinPolicy(
157 //                                              bestDC, numInBestDC, true /*allow LocalDC to look at other DCs for LOCAL_QUORUM */));
158                                 cb.withLoadBalancingPolicy(new TokenAwarePolicy(dcrrPolicy));
159                                 env.init().printf("Cassandra configured for DCAwareRoundRobinPolicy with best DC at %s with emergency remote of up to %d node(s)"
160                                         ,bestDC, numInBestDC);
161                         } else {
162                                 env.init().printf("Cassandra is using Default Policy, which is not DC aware");
163                         }
164                 }
165                 return cb.build();
166         }
167         
168         private static class Resettable {
169                 private Class<? extends Exception> cls;
170                 private List<String> messages;
171                 
172                 @SuppressWarnings("unchecked")
173                 public Resettable(Env env, String propData) throws APIException {
174                         if(propData!=null && propData.length()>1) {
175                                 String[] split = Split.split(':', propData);
176                                 if(split.length>0) {
177                                         try {
178                                                 cls = (Class<? extends Exception>)Class.forName(split[0]);
179                                         } catch (ClassNotFoundException e) {
180                                                 throw new APIException("Declared Cassandra Reset Exception, " + propData + ", cannot be ClassLoaded");
181                                         }
182                                 }
183                                 if(split.length>1) {
184                                         messages=new ArrayList<>();
185                                         for(int i=1;i<split.length;++i) {
186                                                 String str = split[i];
187                                                 int start = str.startsWith("\"")?1:0;
188                                                 int end = str.length()-(str.endsWith("\"")?1:0);
189                                                 messages.add(split[i].substring(start, end));
190                                         }
191                                 } else {
192                                         messages = null;
193                                 }
194                         }
195                 }
196                 
197                 public boolean matches(Exception ex) {
198                         if(ex.getClass().equals(cls)) {
199                                 if(messages!=null) {
200                                         String msg = ex.getMessage();
201                                         for(String m : messages) {
202                                                 if(msg.contains(m)) {
203                                                         return true;
204                                                 }
205                                         }
206                                 }
207                         }
208                         return false;
209                 }
210         }
211         
212         public static final boolean isResetException(Exception e) {
213                 if(e==null) {
214                         return true;
215                 }
216                 for(Resettable re : resetExceptions) {
217                         if(re.matches(e)) {
218                                 return true;
219                         }
220                 }
221                 return false;
222         }
223 }