Sonar Fix: NotifyApprovals.java
[aaf/authz.git] / auth / auth-batch / src / main / java / org / onap / aaf / auth / batch / Batch.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.batch;
23
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.PrintStream;
30 import java.lang.reflect.Constructor;
31 import java.net.InetAddress;
32 import java.net.URL;
33 import java.net.UnknownHostException;
34 import java.nio.ByteBuffer;
35 import java.text.SimpleDateFormat;
36 import java.util.ArrayList;
37 import java.util.Date;
38 import java.util.GregorianCalendar;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Set;
42 import java.util.TimeZone;
43
44 import org.apache.log4j.Logger;
45 import org.onap.aaf.auth.common.Define;
46 import org.onap.aaf.auth.dao.CassAccess;
47 import org.onap.aaf.auth.env.AuthzEnv;
48 import org.onap.aaf.auth.env.AuthzTrans;
49 import org.onap.aaf.auth.log4j.Log4JAccessAppender;
50 import org.onap.aaf.auth.org.Organization;
51 import org.onap.aaf.auth.org.OrganizationException;
52 import org.onap.aaf.auth.org.OrganizationFactory;
53 import org.onap.aaf.cadi.Access.Level;
54 import org.onap.aaf.cadi.PropAccess;
55 import org.onap.aaf.cadi.config.Config;
56 import org.onap.aaf.misc.env.APIException;
57 import org.onap.aaf.misc.env.Env;
58 import org.onap.aaf.misc.env.StaticSlot;
59 import org.onap.aaf.misc.env.TimeTaken;
60 import org.onap.aaf.misc.env.util.Chrono;
61 import org.onap.aaf.misc.env.util.Split;
62 import org.onap.aaf.misc.env.util.StringBuilderOutputStream;
63
64 import com.datastax.driver.core.Cluster;
65 import com.datastax.driver.core.ResultSet;
66 import com.datastax.driver.core.Row;
67 import com.datastax.driver.core.Session;
68 import com.datastax.driver.core.Statement;
69
70 public abstract class Batch {
71
72     private static StaticSlot ssargs;
73
74     protected static final String STARS = "*****";
75
76     protected final Cluster cluster; 
77     protected static AuthzEnv env;
78     protected static Session session;
79     protected static Set<String> specialNames;
80     protected static List<String> specialDomains;
81     protected static boolean dryRun; 
82     protected static String batchEnv;
83
84         private static File logdir;
85
86     public static final String CASS_ENV = "CASS_ENV";
87     public static final String LOG_DIR = "LOG_DIR";
88     protected static final String MAX_EMAILS="MAX_EMAILS";
89     protected static final String VERSION="VERSION";
90     public static final String GUI_URL="GUI_URL";
91     
92     protected final Organization org;
93     
94     protected Batch(AuthzEnv env) throws APIException, IOException, OrganizationException {
95         if (batchEnv != null) {
96             env.info().log("Redirecting to ",batchEnv,"environment");
97             String str;
98             for (String key : new String[]{
99                     CassAccess.CASSANDRA_CLUSTERS,
100                     CassAccess.CASSANDRA_CLUSTERS_PORT,
101                     CassAccess.CASSANDRA_CLUSTERS_USER_NAME,
102                     CassAccess.CASSANDRA_CLUSTERS_PASSWORD,
103                     VERSION,GUI_URL,MAX_EMAILS,
104                     LOG_DIR,
105                     "SPECIAL_NAMES"
106                     }) {
107                 if ((str = env.getProperty(batchEnv+'.'+key))!=null) {
108                     env.setProperty(key, str);
109                 }
110             }
111         }
112
113         // Setup for Dry Run
114         cluster = CassAccess.cluster(env,batchEnv);
115         env.info().log("cluster name - ",cluster.getClusterName());
116         String dryRunStr = env.getProperty( "DRY_RUN" );
117         if ( dryRunStr == null || "false".equals(dryRunStr.trim()) ) {
118             dryRun = false;
119         } else {
120             dryRun = true;
121             env.info().log("dryRun set to TRUE");
122         }
123
124         org = OrganizationFactory.init(env);
125         if(org==null) {
126                 throw new OrganizationException("Organization MUST be defined for Batch");
127         }
128         org.setTestMode(dryRun);
129
130         // Special names to allow behaviors beyond normal rules
131         specialNames = new HashSet<>();
132         specialDomains = new ArrayList<>();
133         String names = env.getProperty( "SPECIAL_NAMES" );
134         if ( names != null )
135         {
136             env.info().log("Loading SPECIAL_NAMES");
137             for (String s :names.split(",") ) {
138                 env.info().log("\tspecial: " + s );
139                 if(s.indexOf('@')>0) {
140                         specialNames.add( s.trim() );
141                 } else {
142                         specialDomains.add(s.trim());
143                 }
144             }
145         }
146     }
147
148     protected abstract void run(AuthzTrans trans);
149     protected abstract void _close(AuthzTrans trans);
150     
151     public String[] args() {
152         return env.get(ssargs);
153     }
154     
155     public boolean isDryRun()
156     {
157         return dryRun;
158     }
159     
160     public boolean isSpecial(String user) {
161         if(user==null) {
162                 return false;
163         }
164         if (specialNames != null && specialNames.contains(user)) {
165             env.info().log("specialName: " + user);
166             return (true);
167         } else {
168                 if(specialDomains!=null) {
169                         for(String sd : specialDomains) {
170                                 if(user.endsWith(sd)) {
171                                         env.info().log("specialDomain: " + user + " matches " + sd);
172                                         return (true);
173                                 }
174                         }
175                 }
176         }
177         return (false);
178     }
179
180
181     protected PrintStream fallout(PrintStream inFallout, String logType)
182             throws IOException {
183         PrintStream fallout = inFallout;
184         if (fallout == null) {
185             File dir = new File("logs");
186             if (!dir.exists()) {
187                 dir.mkdirs();
188             }
189
190             File f = null;
191             long uniq = System.currentTimeMillis();
192
193             f = new File(dir, getClass().getSimpleName() + "_" + logType + "_"
194                     + uniq + ".log");
195
196             fallout = new PrintStream(new FileOutputStream(f, true));
197         }
198         return fallout;
199     }
200
201     public Organization getOrgFromID(AuthzTrans trans, String user) {
202         Organization organization;
203         try {
204             organization = OrganizationFactory.obtain(trans.env(),user.toLowerCase());
205         } catch (OrganizationException e1) {
206             trans.error().log(e1);
207             organization=null;
208         }
209
210         if (organization == null) {
211             PrintStream fallout = null;
212
213             try {
214                 fallout = fallout(fallout, "Fallout");
215                 fallout.print("INVALID_ID,");
216                 fallout.println(user);
217             } catch (Exception e) {
218                 env.error().log("Could not write to Fallout File", e);
219             }
220             return (null);
221         }
222
223         return (organization);
224     }
225     
226     public static Row executeDeleteQuery(Statement stmt) {
227         Row row = null;
228         if (!dryRun) {
229             row = session.execute(stmt).one();
230         }
231
232         return (row);
233
234     }
235         
236     public static int acquireRunLock(String className) {
237         Boolean testEnv = true;
238         String envStr = env.getProperty("AFT_ENVIRONMENT");
239
240         if (envStr != null) {
241             if ("AFTPRD".equals(envStr)) {
242                 testEnv = false;
243             }
244         } else {
245             env.fatal()
246                     .log("AFT_ENVIRONMENT property is required and was not found. Exiting.");
247             System.exit(1);
248         }
249
250         if (testEnv) {
251             env.info().log("TESTMODE: skipping RunLock");
252             return (1);
253         }
254
255         String hostname = null;
256         try {
257             hostname = InetAddress.getLocalHost().getHostName();
258         } catch (UnknownHostException e) {
259             e.printStackTrace();
260             env.warn().log("Unable to get hostname");
261             return (0);
262         }
263
264         ResultSet existing = session.execute(String.format(
265                 "select * from authz.run_lock where class = '%s'", className));
266
267         for (Row row : existing) {
268             long curr = System.currentTimeMillis();
269             ByteBuffer lastRun = row.getBytesUnsafe(2); // Can I get this field
270                                                         // by name?
271
272             long interval = (1 * 60 * 1000); // @@ Create a value in props file
273                                                 // for this
274             long prev = lastRun.getLong();
275
276             if ((curr - prev) <= interval) {
277                 env.warn().log(
278                         String.format("Too soon! Last run was %d minutes ago.",
279                                 ((curr - prev) / 1000) / 60));
280                 env.warn().log(
281                         String.format("Min time between runs is %d minutes ",
282                                 (interval / 1000) / 60));
283                 env.warn().log(
284                         String.format("Last ran on machine: %s at %s",
285                                 row.getString("host"), row.getDate("start")));
286                 return (0);
287             } else {
288                 env.info().log("Delete old lock");
289                 deleteLock(className);
290             }
291         }
292
293         GregorianCalendar current = new GregorianCalendar();
294
295         // We want our time in UTC, hence "+0000"
296         SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss+0000");
297         fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
298
299         String cql = String
300                 .format("INSERT INTO authz.run_lock (class,host,start) VALUES ('%s','%s','%s') IF NOT EXISTS",
301                         className, hostname, fmt.format(current.getTime()));
302
303         env.info().log(cql);
304
305         Row row = session.execute(cql).one();
306         if (!row.getBool("[applied]")) {
307             env.warn().log("Lightweight Transaction failed to write lock.");
308             env.warn().log(
309                     String.format("host with lock: %s, running at %s",
310                             row.getString("host"), row.getDate("start")));
311             return (0);
312         }
313         return (1);
314     }
315     
316     private static void deleteLock( String className) {
317         Row row = session.execute( String.format( "DELETE FROM authz.run_lock WHERE class = '%s' IF EXISTS", className ) ).one();
318         if (! row.getBool("[applied]")) {
319             env.info().log( "delete failed" );
320         }
321     }
322
323     private static void transferVMProps(AuthzEnv env, String ... props) {
324         String value;
325         for (String key : props) {
326             if ((value = System.getProperty(key))!=null) {
327                 env.setProperty(key, value);
328             }
329         }
330     }
331     
332     protected static File logDir() {
333         if(logdir == null) {
334                 String ld = env.getProperty(LOG_DIR);
335                 if (ld==null) {
336                     if (batchEnv==null) { // Deployed Batch doesn't use different ENVs, and a common logdir
337                         ld = "logs/";
338                     } else {
339                         ld = "logs/"+batchEnv;
340                     }
341                 }
342                 logdir = new File(ld);
343                 if(!logdir.exists()) {
344                         logdir.mkdirs();
345                 }
346         } 
347         return logdir;
348     }
349     protected int count(String str, char c) {
350         if (str==null || str.isEmpty()) {
351             return 0;
352         } else {
353             int count=1;
354             for (int i=str.indexOf(c);i>=0;i=str.indexOf(c,i+1)) {
355                 ++count;
356             }
357             return count;
358         }
359     }
360
361     public final void close(AuthzTrans trans) {
362         _close(trans);
363         cluster.close();
364     }
365
366     public static void main(String[] args) {
367         // Use a StringBuilder to save off logs until a File can be setup
368         StringBuilderOutputStream sbos = new StringBuilderOutputStream();
369         PropAccess access = new PropAccess(new PrintStream(sbos),args);
370         access.log(Level.INIT, "------- Starting Batch ------\n  Args: ");
371         for(String s: args) {
372                 sbos.getBuffer().append(s);
373                 sbos.getBuffer().append(' ');
374         }
375         
376         InputStream is = null;
377         String filename;
378         String propLoc;
379         try {
380             Define.set(access);
381             
382             if(access.getProperty(Config.CADI_PROP_FILES)==null) {
383                     File f = new File("authBatch.props");
384                     try {
385                         if (f.exists()) {
386                             filename = f.getAbsolutePath();
387                             is = new FileInputStream(f);
388                             propLoc = f.getPath();
389                         } else {
390                             URL rsrc = ClassLoader.getSystemResource("authBatch.props");
391                             filename = rsrc.toString();
392                             is = rsrc.openStream();
393                             propLoc = rsrc.getPath();
394                         }
395                         access.load(is);
396                     } finally {
397                         if (is == null) {
398                             System.err.println("authBatch.props must exist in current dir, or in Classpath");
399                             System.exit(1);
400                         }
401                         is.close();
402                     }
403                     if (filename != null) {
404                         access.log(Level.INFO,"Instantiated properties from", filename);
405                     }
406
407                     // Log where Config found
408                     access.log(Level.INFO,"Configuring from", propLoc);
409
410             }
411
412             env = new AuthzEnv(access);
413             
414             transferVMProps(env, CASS_ENV, "DRY_RUN", "NS", "Organization");
415
416             // Be able to change Environments
417             // load extra properties, i.e.
418             // PERF.cassandra.clusters=....
419             batchEnv = env.getProperty(CASS_ENV);
420             if(batchEnv!=null) {
421                 batchEnv = batchEnv.trim();
422             }
423
424             File logFile = new File(logDir() + "/batch" + Chrono.dateOnlyStamp(new Date()) + ".log" );
425             PrintStream batchLog = new PrintStream(new FileOutputStream(logFile,true));
426             try {
427                     access.setStreamLogIt(batchLog);
428                     sbos.flush();
429                     batchLog.print(sbos.getBuffer());
430                     sbos = null;
431                     Logger.getRootLogger().addAppender(new Log4JAccessAppender(access));
432
433                     Batch batch = null;
434                     AuthzTrans trans = env.newTrans();
435         
436                     TimeTaken tt = trans.start("Total Run", Env.SUB);
437                     try {
438                         int len = args.length;
439                         if (len > 0) {
440                             String toolName = args[0];
441                             len -= 1;
442                             if (len < 0)
443                                 len = 0;
444                             String nargs[] = new String[len];
445                             if (len > 0) {
446                                 System.arraycopy(args, 1, nargs, 0, len);
447                             }
448         
449                             env.put(ssargs = env.staticSlot("ARGS"), nargs);
450         
451                             /*
452                              * Add New Batch Programs (inherit from Batch) here
453                              */
454         
455                             // Might be a Report, Update or Temp Batch
456                             Class<?> cls = null;
457                             String classifier = "";
458         
459                             String[] pkgs = new String[] {
460                                         "org.onap.aaf.auth.batch.update",
461                                         "org.onap.aaf.auth.batch.reports",
462                                         "org.onap.aaf.auth.batch.temp"
463                                         };
464                                         
465                             String ebp = env.getProperty("EXTRA_BATCH_PKGS");
466                             if(ebp!=null) {
467                                 String[] ebps = Split.splitTrim(':', ebp);
468                                 String[] temp = new String[ebps.length + pkgs.length];
469                                 System.arraycopy(pkgs,0, temp, 0, pkgs.length);
470                                 System.arraycopy(ebps,0,temp,pkgs.length,ebps.length);
471                                 pkgs = temp;
472                             }
473                             
474                             for(String p : pkgs) {
475                                     try {
476                                         cls = ClassLoader.getSystemClassLoader().loadClass(p + '.' + toolName);
477                                         int lastDot = p.lastIndexOf('.');
478                                         if(p.length()>0 || p.length()!=lastDot) { 
479                                                 StringBuilder sb = new StringBuilder();
480                                                 sb.append(Character.toUpperCase(p.charAt(++lastDot)));
481                                                 while(++lastDot<p.length()) {
482                                                         sb.append(p.charAt(lastDot));
483                                                 }
484                                                 sb.append(':');
485                                                 classifier = sb.toString();
486                                                 break;
487                                         }
488                                     } catch (ClassNotFoundException e) {
489                                         cls = null;
490                                     }
491                             }
492                             if (cls != null) {
493                                 Constructor<?> cnst = cls.getConstructor(AuthzTrans.class);
494                                 batch = (Batch) cnst.newInstance(trans);
495                                 env.info().log("Begin", classifier, toolName);
496                             }
497                         
498         
499                             if (batch == null) {
500                                 trans.error().log("No Batch named", toolName, "found");
501                             }
502                             /*
503                              * End New Batch Programs (inherit from Batch) here
504                              */
505         
506                         }
507                         if (batch != null) {
508                             batch.run(trans);
509                         }
510                     } finally {
511                         tt.done();
512                         if (batch != null) {
513                             batch.close(trans);
514                         }
515                         StringBuilder sb = new StringBuilder("Task Times\n");
516                         trans.auditTrail(4, sb, AuthzTrans.SUB, AuthzTrans.REMOTE);
517                         trans.info().log(sb);
518                     }
519             } finally {
520                 batchLog.close();
521             }
522
523         } catch (Exception e) {
524             e.printStackTrace(System.err);
525             // Exceptions thrown by DB aren't stopping the whole process.
526             System.exit(1);
527         }
528     }
529
530 }
531