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