Package com.threealike.life.core

Source Code of com.threealike.life.core.Env

package com.threealike.life.core;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.threealike.life.thirdparty.org.json.JSONArray;
import com.threealike.life.thirdparty.org.json.JSONObject;
import com.threealike.life.util.Arrays;
import com.threealike.life.util.Strings;

public class Env
{
  public static final String MODE;
  public static final EnvTypes ENV_TYPE;
  public static final String APP_ROOT;
  public static final String JAVA_HOME;
  public static final String[] APP_LABELS;
  public static final String VALID_APP_LABEL_PATTERN = "^[a-zA-Z_0-9\\-$]+$";
  public static final String[] SERVICE_LABELS;
 
  /*
   * tmp is a temporary JSONObject for purposes of ensuring atomic assignment to CONF
   * this is necessary if hot re-deploy is ever implemented
   *
   * calls to register() must my synchronized on Env.class to ensure atomicity
   */
  private static JSONObject tmp;
  private static List<String> checked;
 
  private static final String[] STACK = new String[100];
  private static final JSONObject CONF;
  private static final JSONObject[] APP_CONFS;
  private static final Map<String, List<RequestListener>> REQUEST_LISTENERS
    = new HashMap<String, List<RequestListener>>();
  static final Map<String, NicheService> SERVICES = new HashMap<String, NicheService>();
  static final Map<String, List<ServiceStopListener>> SHUT_LISTENERS = new HashMap<String, List<ServiceStopListener>>();
 
  static
  {
    /*
     * get the mode we're in
     */
    InputStream is = ClassLoader.getSystemResourceAsStream(".mode");
   
    if(is == null)
      throw new RuntimeException("\".mode\" file not found");
   
    byte[] data = null;
    try {
      data = new byte[is.available()];
      is.read(data);
      is.close();
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    MODE = new String(data).trim();
   
    /*
     * register main application environment vars
     */
    CONF = parseEnv(null);
    if(CONF == null) {
      throw new RuntimeException("Could not register main Application. Verify \"env\" and \"env.possible\" are on the classpath");
    }
   
    ENV_TYPE = EnvTypes.valueOf(CONF.getString("env-type"));
    APP_ROOT = CONF.optString("app-root");
    JAVA_HOME = CONF.optString("java-home");
   
    /*
     * register app info
     */
    if(CONF.has("apps")) {
      JSONArray apps;
      int len;
      APP_LABELS = new String[(len=(apps=CONF.getJSONArray("apps")).length())];
      System.out.println("(niche) apps="+apps);
      APP_CONFS = new JSONObject[len];
      for(int i=0; i < apps.length(); i++) {
        String appLabel = apps.getJSONObject(i).getString("label");
        APP_LABELS[i] = appLabel;
        if(!appLabel.matches(VALID_APP_LABEL_PATTERN))
          throw new RuntimeException(
              "Invalid application label, \""+appLabel+"\". Must match "+VALID_APP_LABEL_PATTERN);
        APP_CONFS[i] = parseEnv(appLabel);
      }
    } else {
      APP_LABELS = null;
      APP_CONFS = null;
    }
   
   
    if(CONF.has("services")) {
      SERVICE_LABELS = new String[CONF.getJSONArray("services").length()];
      for(int i=0; i < CONF.getJSONArray("services").length(); i++) {
        SERVICE_LABELS[i] = CONF.getJSONArray("services").getJSONObject(i).getString("label");
      }
    } else {
      SERVICE_LABELS = null;
    }
   
    //APP_LABELS[APP_LABELS.length-1] = null;
  }
 
  public static final void stopService(String label) {
    if(!SERVICES.containsKey(label)) return;
    if(Env.SHUT_LISTENERS.get(label) != null) {
      for(Iterator<ServiceStopListener> it = Env.SHUT_LISTENERS.get(label).iterator(); it.hasNext();)
        it.next().handleShutdown();
    }
    SERVICES.get(label).shutdown();
  }
 
  public static final void addServiceStopListener(String label, ServiceStopListener listener) {
    if(Env.SHUT_LISTENERS.get(label) == null)
      Env.SHUT_LISTENERS.put(label, new ArrayList<ServiceStopListener>());
    Env.SHUT_LISTENERS.get(label).add(listener);
  }
 
  public static final JSONObject getServiceConfig(String serviceLabel) {
    JSONArray services = getConfig().getJSONArray("services");
    for(int i=0; i < services.length(); i++) {
      JSONObject service = services.getJSONObject(i);
      if(service.getString("label").equals(serviceLabel)) {
        return service;
      }
    }
    return null;
  }
 
  public static final void addRequestListener(String serviceLabel, RequestListener requestListener) {
    List<RequestListener> listeners = REQUEST_LISTENERS.get(serviceLabel);
    if(listeners == null) listeners = new ArrayList<RequestListener>();
    listeners.add(requestListener);
    REQUEST_LISTENERS.put(serviceLabel, listeners);
  }
 
  public static final JSONObject getConfig() {
    return CONF;
  }
 
  public static final JSONObject getConfig(String appLabel) {
    if(appLabel == null) return getConfig();
    int i=0;
    for(String s : APP_LABELS) {
      if(s == null) continue;
      if(s.equals(appLabel)) {
        return APP_CONFS[i];
      }
      i++;
    }
    return null;
  }
 
  static final List<RequestListener> getRequestListeners(String serviceLabel) {
    return REQUEST_LISTENERS.get(serviceLabel);
  }
 
  @SuppressWarnings("unchecked")
  public static final JSONObject parseEnv(String appLabel)
  {
    String configFileLabel = appLabel == null ? "env" : appLabel+".env";
   
    System.out.print("(niche) loading "+configFileLabel+".possible");
    InputStream resource = ClassLoader.getSystemResourceAsStream(configFileLabel+".possible");
    URL res = ClassLoader.getSystemResource(configFileLabel+".possible");
    System.out.println(" file="+(res == null ? "null" : res.getFile()));
   
    if(resource == null) return null;
    JSONObject possible = new JSONObject(resource, true);
   
    if(possible == null && appLabel == null)
      throw new RuntimeException("Could not load *.possible file for "+appLabel);
    if(possible == null) return null;
   
    System.out.print("(niche) loading "+configFileLabel);
    InputStream configResource = ClassLoader.getSystemResourceAsStream(configFileLabel);
    res = ClassLoader.getSystemResource(configFileLabel);
    System.out.println(" file="+(res == null ? "null" : res.getFile()));

//    if(configResource == null) return null;
//    JSONArray allActual = new JSONArray(configResource);
//    if(allActual == null)
//      allActual = new JSONArray();
    JSONArray allActual = null;
    if(configResource == null)
      allActual = new JSONArray("[{'apply':['"+MODE+"']}]");
    else
      allActual = new JSONArray(configResource, true);
   
    tmp = new JSONObject();
   
    for(int i=0; i < allActual.length(); i++) {
      JSONObject next = allActual.getJSONObject(i);
      if(!next.has("apply")) {
        throw new RuntimeException("required key, \"apply\" not found in config block");
      }
      JSONArray apply = next.getJSONArray("apply");
      inner:
      for(int j=0; j < apply.length(); j++) {
        if(apply.getString(j).equals(MODE)) {
//          if(CONF.get(appLabel) == null)
//            CONF.put(appLabel, next);
//          else {
            for(Iterator it = next.keys(); it.hasNext();) {
              String next1 = (String)it.next();
              tmp.put(next1, next.get(next1));
            }
//          }
          break inner;
        }
      }
    }
    /*
     * apply all default config keys (which are global across all modes)
     */
    for(Iterator it = possible.keys(); it.hasNext();) {
      String next = (String)it.next();
      JSONObject desc = possible.getJSONObject(next);
      if(desc.has("default")) {
        assign(null, possible, 0, explodeKey(next.split("\\.")));
      }
    }

    checked = new ArrayList<String>();
   
    validate(possible, tmp, 0);
   
    for(Iterator it = possible.keys(); it.hasNext();) {
      String next = (String)it.next();
      String parent = getParent(next);
      if(parent != null && !checked.contains(parent)) {
        continue;
      }
      JSONObject desc = possible.getJSONObject(next);
      boolean required = true;
      if(desc.has("required"))
        required = desc.getBoolean("required");
      if(required && !checked.contains(next))
        throw new RuntimeException("Key, \""+configFileLabel+"."+next+"\", is required");
    }
   
    return tmp;
  }
 
  private static final String[] explodeKey(String[] parts)
  {
    for(int i=0; i < parts.length; i++) {
      int index;
      if((index=parts[i].indexOf("[]")) > 0) {
        int brackets = Strings.countMatches(parts[i], "[]");
        parts[i] = parts[i].substring(0,index);
        String[][] subs = Arrays.split(parts, i);
        String[] brkts = new String[brackets];
        java.util.Arrays.fill(brkts, "[]");
        String[] newa = Arrays.merge(subs[0], brkts);
        parts = Arrays.merge(newa, subs[1]);
      }
    }
    return parts;
  }
 
  private static final void assign(Object parent, JSONObject possible, int index, String[] key)
  {
    assign(parent, possible, index, key, -1);
  }
  private static final void assign(Object parent, JSONObject possible, int index, String[] key, int arrayIndex)
  {
    String sub = "";
    for(int i=0; i <= index; i++) {
      sub += key[i];
      if(i < index && !key[i+1].equals("[]")) sub += ".";
    }
    JSONObject desc = possible.getJSONObject(sub);
    Types type = Types.String;
    if(desc.has("type"))
      type = Types.valueOf(desc.getString("type"));
    Object apply = null;
    if(type == Types.Array) {
      if(parent == null) {
        if(tmp.has(key[index]))
          apply = tmp.getJSONArray(key[index]);
      } else if(parent instanceof JSONObject) {
        if(((JSONObject )parent).has(key[index])) {
          apply = ((JSONObject)parent).getJSONArray(key[index]);
        }
      } else if(parent instanceof JSONArray) {
        for(int i=0; i < ((JSONArray)parent).length(); i++) {
          if(((JSONArray)parent).get(i) instanceof JSONArray) {
            apply = ((JSONArray)parent).get(i);
            break;
          }
        }
      }
      if(apply == null)
        apply = new JSONArray();
    } else if(type == Types.Object) {
      if(parent == null) {
        if(tmp.has(key[index]))
          apply = tmp.getJSONObject(key[index]);
      } else if(parent instanceof JSONObject) {
        apply = ((JSONObject)parent).opt(key[index]);
      } else if(parent instanceof JSONArray) {
        for(int i=0; i < ((JSONArray)parent).length(); i++) {
          if(((JSONArray)parent).get(i) instanceof JSONObject && i == arrayIndex) {
            apply = ((JSONArray)parent).get(i);
            break;
          }
        }
      }
      if(apply == null)
        apply = new JSONObject();
    } else if(type == Types.String)
      apply = desc.get("default");
    else if(type == Types.Int) {
      apply = desc.get("default");
    } else if(type == Types.Boolean)
      apply = desc.get("default");
   
    if(apply == null)
      throw new RuntimeException("Could not determine type for key, \""+sub+"\"");
   
    if(parent == null) {
      if(!tmp.has(key[index]))
        tmp.put(key[index], apply);
    } else if(parent instanceof JSONObject) {
      if(!((JSONObject)parent).has(key[index]))
        ((JSONObject)parent).put(key[index], apply);
    } else {
      boolean there = false;
      for(int i=0; i < ((JSONArray)parent).length(); i++) {
        if(((JSONArray)parent).get(i).equals(apply) && i == arrayIndex) {
          there = true;
          break;
        }
      }
      if(!there) {
        ((JSONArray)parent).put(apply);
      }
    }
   
    if(index+1 == key.length) return;
   
    if(apply instanceof JSONArray) {
      for(int k=0; k < ((JSONArray)apply).length(); k++) {
        assign(apply, possible, index+1, key, k);
      }
      return;
    }
   
    assign(apply, possible, index+1, key);
  }
 
  private static final String getParent(String key)
  {
    String out = null;
    if(key.length() > 2 && key.substring(key.length()-2).equals("[]"))
      return key.substring(0,key.length()-2);
    else if(key.indexOf(".") != -1) {
      String[] parts = key.split("\\.");
      out = "";
      for(int i=0; i < parts.length-1; i++) {
        out += parts[i];
        if(i < parts.length-2)
          out += ".";
      }
    }
    return out;
  }
 
  @SuppressWarnings("unchecked")
  private static final void validate(JSONObject possible, Object node, int level)
  {
    Types nextNodeType = null;
    Object nextNode = null;
    if(node instanceof JSONObject) {
      JSONObject _node = (JSONObject)node;
      for(Iterator it = _node.keys(); it.hasNext();) {
        String next = (String)it.next();
        if(next.equals("apply") && level == 0)
          continue;
        if(next.matches(".*[^a-zA-Z_0-9\\-$].*"))
          throw new RuntimeException("Bad characters found in key, \""+next+"\"");
        STACK[level] = next;
        nextNodeType = checkType(possible, (nextNode=_node.get(next)), level);
        if(nextNodeType == Types.Object || nextNodeType == Types.Array)
          validate(possible, nextNode, level+1);
      }
    }
    if(node instanceof JSONArray) {
      STACK[level] = "[]";
      JSONArray _node = (JSONArray)node;
      for(int i=0; i < _node.length(); i++) {
        nextNodeType = checkType(possible, (nextNode=_node.get(i)), level);
        if(nextNodeType == Types.Object || nextNodeType == Types.Array)
          validate(possible, nextNode, level+1);
      }
    }
  }
 
  private static final Types checkType(JSONObject possible, Object node, int level)
  {
    String sig = "";
    for(int i=0; i < level+1; i++) {
      sig += STACK[i];
      if(i < level && !STACK[i+1].equals("[]")) {
        sig += ".";
      }
    }
    if(!possible.has(sig))
      throw new RuntimeException("No config descriptor found for key, \""+sig+"\"");
   
    checked.add(sig);
   
    JSONObject descr = possible.getJSONObject(sig);
   
    Types type = Types.String;
    if(descr.has("type"))
      type = Types.valueOf(descr.getString("type"));
   
    boolean failed = false;
   
    if(type == Types.String) {
      if(!(node instanceof String))
        failed = true;
    } else if(type == Types.Array) {
      if(!(node instanceof JSONArray))
        failed = true;
    } else if(type == Types.Int) {
      if(!(node instanceof Integer))
        failed = true;
    } else if(type == Types.Object) {
      if(!(node instanceof JSONObject))
        failed = true;
    } else if(type == Types.Boolean) {
      if(!(node instanceof Boolean))
        failed = true;
    } else
      failed = true;
   
    if(failed)
      throw new RuntimeException("Type validation failed for key, \""+sig+"\"");
   
    return type;
  }
 
  private static enum Types {
    String, Array, Object, Int, Boolean
  }
 
  public static enum EnvTypes {
    dev, test, prod
  }
}
TOP

Related Classes of com.threealike.life.core.Env

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.