2 * ============LICENSE_START====================================================
4 * ===========================================================================
5 * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
7 * Modifications Copyright (C) 2018 IBM.
8 * ===========================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 * ============LICENSE_END====================================================
24 package org.onap.aaf.cadi;
26 import java.io.ByteArrayOutputStream;
28 import java.io.FileInputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.PrintStream;
32 import java.io.PrintWriter;
33 import java.io.StringBufferInputStream;
34 import java.io.StringWriter;
35 import java.text.DateFormat;
36 import java.text.SimpleDateFormat;
37 import java.util.ArrayList;
38 import java.util.Date;
39 import java.util.List;
40 import java.util.Map.Entry;
41 import java.util.Properties;
43 import org.onap.aaf.cadi.config.Config;
44 import org.onap.aaf.cadi.config.SecurityInfo;
45 import org.onap.aaf.cadi.util.Split;
47 public class PropAccess implements Access {
48 // Sonar says cannot be static... it's ok. not too many PropAccesses created.
49 private final SimpleDateFormat iso8601 = newISO8601();
52 public static SimpleDateFormat newISO8601() {
53 return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
56 public static final Level DEFAULT = Level.AUDIT;
60 private Properties props;
61 private List<String> recursionProtection = null;
66 logIt = new StreamLogIt(System.out);
71 * This Constructor soly exists to instantiate Servlet Context Based Logging that will call "init" later.
74 protected PropAccess(Object o) {
75 logIt = new StreamLogIt(System.out);
76 props = new Properties();
79 public PropAccess(String ... args) {
80 this(System.out,args);
83 public PropAccess(PrintStream ps, String[] args) {
84 logIt = new StreamLogIt(ps==null?System.out:ps);
88 public PropAccess(LogIt logit, String[] args) {
92 public PropAccess(Properties p) {
96 public PropAccess(PrintStream ps, Properties p) {
97 logIt = new StreamLogIt(ps==null?System.out:ps);
101 protected void init(final LogIt logIt, final String[] args) {
103 Properties nprops=new Properties();
105 for (String arg : args) {
106 if ((eq=arg.indexOf('='))>0) {
107 nprops.setProperty(arg.substring(0, eq),arg.substring(eq+1));
113 protected synchronized void init(Properties p) {
114 // Make sure these two are set before any changes in Logging
117 props = new Properties();
118 // First, load related System Properties
119 for (Entry<Object,Object> es : System.getProperties().entrySet()) {
120 String key = es.getKey().toString();
121 for (String start : new String[] {"cadi_","aaf_","cm_"}) {
122 if (key.startsWith(start)) {
123 props.put(key, es.getValue());
127 // Second, overlay or fill in with Passed in Props
133 String sLevel = props.getProperty(Config.CADI_LOGLEVEL);
134 // Third, load any Chained Property Files
135 load(props.getProperty(Config.CADI_PROP_FILES));
137 if(sLevel==null) { // if LogLev wasn't set before, check again after Chained Load
138 sLevel = props.getProperty(Config.CADI_LOGLEVEL);
140 level=DEFAULT.maskOf();
142 level=Level.valueOf(sLevel).maskOf();
145 // Setup local Symmetrical key encryption
148 symm = Symm.obtain(this);
149 } catch (CadiException e) {
150 System.err.append("FATAL ERROR: Cannot obtain Key Information.");
151 e.printStackTrace(System.err);
156 name = props.getProperty(Config.CADI_LOGNAME, name);
158 SecurityInfo.setHTTPProtocols(this);
163 private void load(String cadi_prop_files) {
164 if (cadi_prop_files==null) {
167 String prevKeyFile = props.getProperty(Config.CADI_KEYFILE);
170 for(String filename : Split.splitTrim(File.pathSeparatorChar, cadi_prop_files)) {
171 Properties fileProps = new Properties();
172 File file = new File(filename);
174 printf(Level.INIT,"Loading CADI Properties from %s",file.getAbsolutePath());
176 FileInputStream fis = new FileInputStream(file);
179 // Only load props from recursion which are not already in props
180 // meaning top Property file takes precedence
181 for(Entry<Object, Object> es : fileProps.entrySet()) {
182 if(props.get(es.getKey())==null) {
183 String key = es.getKey().toString();
184 String value = es.getValue().toString();
185 props.put(key, value);
186 if(key.contains("pass")) {
189 printf(Level.DEBUG," %s=%s",key,value);
193 String chainProp = fileProps.getProperty(Config.CADI_PROP_FILES);
194 if (chainProp!=null) {
195 if (recursionProtection==null) {
196 recursionProtection = new ArrayList<>();
197 recursionProtection.add(cadi_prop_files);
199 if (!recursionProtection.contains(chainProp)) {
200 recursionProtection.add(chainProp);
201 load(chainProp); // recurse
207 } catch (Exception e) {
208 log(e,filename,"cannot be opened");
211 printf(Level.WARN,"Warning: recursive CADI Property %s does not exist",file.getAbsolutePath());
216 for (Entry<Object, Object> es : props.entrySet()) {
217 Object value = es.getValue();
218 if (value instanceof String) {
219 String trim = ((String)value).trim();
220 // Remove Beginning/End Quotes, which might be there if mixed with Bash Props
221 int s = 0, e=trim.length()-1;
222 if (s<e && trim.charAt(s)=='"' && trim.charAt(e)=='"') {
223 trim=trim.substring(s+1,e);
225 if (trim!=value) { // Yes, I want OBJECT equals
226 props.setProperty((String)es.getKey(), trim);
230 // Reset Symm if Keyfile Changes:
231 String newKeyFile = props.getProperty(Config.CADI_KEYFILE);
232 if ((prevKeyFile!=null && newKeyFile!=null) || (newKeyFile!=null && !newKeyFile.equals(prevKeyFile))) {
234 symm = Symm.obtain(this);
235 } catch (CadiException e) {
236 System.err.append("FATAL ERROR: Cannot obtain Key Information.");
237 e.printStackTrace(System.err);
241 prevKeyFile=newKeyFile;
244 String loglevel = props.getProperty(Config.CADI_LOGLEVEL);
245 if (loglevel!=null) {
247 level=Level.valueOf(loglevel).maskOf();
248 } catch (IllegalArgumentException e) {
249 printf(Level.ERROR,"%s=%s is an Invalid Log Level",Config.CADI_LOGLEVEL,loglevel);
255 public void load(InputStream is) throws IOException {
257 load(props.getProperty(Config.CADI_PROP_FILES));
261 public void log(Level level, Object ... elements) {
262 if (willLog(level)) {
263 logIt.push(level,elements);
267 public StringBuilder buildMsg(Level level, Object[] elements) {
268 return buildMsg(name,iso8601,level,elements);
272 * Need to pass in DateFormat per thread, because not marked as thread safe
274 public static StringBuilder buildMsg(final String name, final DateFormat sdf, Level level, Object[] elements) {
275 final StringBuilder sb;
276 int end = elements.length;
278 sb = new StringBuilder();
279 write(true,sb,elements);
281 sb = new StringBuilder(
282 sdf.format(new Date())
285 sb.append(level.name());
292 if(elements[idx]!=null &&
293 elements[idx] instanceof Integer) {
295 sb.append(elements[idx]);
299 write(true,sb,elements);
305 private static boolean write(boolean first, StringBuilder sb, Object[] elements) {
307 for (Object o : elements) {
309 if(o.getClass().isArray()) {
310 first = write(first,sb,(Object[])o);
311 } else if(o instanceof Throwable) {
312 ByteArrayOutputStream baos = new ByteArrayOutputStream();
313 PrintStream ps = new PrintStream(baos);
314 ((Throwable)o).printStackTrace(ps);
315 sb.append(baos.toString());
323 switch(s.charAt(l-1)) {
339 public void log(Exception e, Object... elements) {
340 StringWriter sw = new StringWriter();
341 PrintWriter pw = new PrintWriter(sw);
343 e.printStackTrace(pw);
344 log(Level.ERROR,elements,sw.toString());
348 public void printf(Level level, String fmt, Object... elements) {
349 if (willLog(level)) {
350 log(level,String.format(fmt, elements));
355 public void setLogLevel(Level level) {
356 this.level = level.maskOf();
360 public boolean willLog(Level level) {
361 return level.inMask(this.level);
365 public ClassLoader classLoader() {
366 return ClassLoader.getSystemClassLoader();
370 public String getProperty(String tag, String def) {
371 return props.getProperty(tag,def);
375 public String decrypt(String encrypted, boolean anytext) throws IOException {
376 return (encrypted!=null && (anytext==true || encrypted.startsWith(Symm.ENC)))
377 ? symm.depass(encrypted)
381 public String encrypt(String unencrypted) throws IOException {
382 return Symm.ENC+symm.enpass(unencrypted);
388 public String getProperty(String tag) {
389 return props.getProperty(tag);
393 public Properties getProperties() {
397 public void setProperty(String tag, String value) {
399 props.put(tag, value);
400 if (Config.CADI_KEYFILE.equals(tag)) {
401 // reset decryption too
403 symm = Symm.obtain(this);
404 } catch (CadiException e) {
405 System.err.append("FATAL ERROR: Cannot obtain Key Information.");
406 e.printStackTrace(System.err);
413 public interface LogIt {
414 public void push(Level level, Object ... elements) ;
417 private class StreamLogIt implements LogIt {
418 private PrintStream ps;
420 public StreamLogIt(PrintStream ps) {
424 public void push(Level level, Object ... elements) {
425 ps.println(buildMsg(level,elements));
430 public void set(LogIt logit) {
434 public void setStreamLogIt(PrintStream ps) {
435 logIt = new StreamLogIt(ps);
438 public String toString() {
439 return props.toString();