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