@SessionScoped
public class ActiveUser implements Serializable {

    private String uniqueId = null;
    private String webEventChannel = "default";
    private List<String> webEventSubscriptions = new ArrayList<>();
    private String csrfToken = "";
    private String eventsToken = "";
    private HashMap<String,Object> data = new HashMap<>();
    private static final Logger _log = LoggerFactory.getLogger(ActiveUser.class);
    private static final SecureTokenUtil csrfTokeUtil = new SecureTokenUtil();

    @Inject
    private transient Event<UserSubscriptionEvent> channelEvent;

    @PostConstruct
    public void init(){
        this.csrfToken = csrfTokeUtil.makeJwtToken();
        this.eventsToken = csrfTokeUtil.makeJwtToken();
        this.uniqueId = UUID.randomUUID().toString();
    }

    public void expireTokens(){
        this.csrfToken = csrfTokeUtil.makeJwtToken();
        this.eventsToken = csrfTokeUtil.makeJwtToken();
    }

    public String getEventsToken() {
        return eventsToken;
    }

    public String getCsrfToken() {
        return csrfToken;
    }

    public HashMap<String, Object> getData() {
        return data;
    }

    public void setData(HashMap<String, Object> data) {
        this.data = data;
    }

    public Object getProperty(String name){

        return data.get(name);

    }

    public Object getProperty(String name,Object defaultValue){
        Object value = data.get(name);
        if(value==null)
            return defaultValue;
        return value;
    }

    public void setProperty(String name, Object value){

        data.put(name,value);

    }

    public List<String> getWebEventSubscriptions() {
        return Collections.unmodifiableList(
                webEventSubscriptions);
    }

    public void subscribe(String webEventChannel) {
        if (webEventChannel == null || webEventChannel.isEmpty())
            throw new IllegalArgumentException("webEventChannel must not be null nor empty");
        this.webEventSubscriptions.add(webEventChannel);
        channelEvent.fire(new JoinChannel(this,
                webEventChannel));
    }

    public void unsubscribe(String webEventChannel){
        if(webEventChannel==null||webEventChannel.isEmpty())
            throw new IllegalArgumentException("webEventChannel must not be null nor empty");
        if(this.webEventSubscriptions.remove(webEventChannel))
            channelEvent.fire(new QuitChannel(this,
                    webEventChannel));
    }

    public boolean isSubscribedTo(String webEventChannel){
        if(webEventChannel==null||webEventChannel.isEmpty())
            throw new IllegalArgumentException("webEventChannel must not be null nor empty");
        return this.webEventSubscriptions.contains(
                webEventChannel);
    }

    public String getWebEventChannel() {
        return webEventChannel;
    }

    public void setWebEventChannel(String webEventChannel) {
        this.webEventChannel = webEventChannel;
    }

    public String getUniqueId() {
        return uniqueId;
    }
} @ApplicationScoped
public class AppContext implements Serializable {

    private static GsonBuilder GSON_BUILDER = null;

    static {

        GSON_BUILDER =new GsonBuilder();

    }

    private static String assetVersionPrefix = ".vd3p1d";
    private static final Logger LOGGER = LoggerFactory.getLogger(AppContext.class);

    public static void setAssetVersionPrefix(String prefix) {

        assetVersionPrefix = prefix;

    }


    @Inject
    private BootAgent bootAgent;

    @Inject
    private Event<GsonInitEvent> gsonInitEvent;

    private String baseUrl;

    private String origin;

    private String domain;

    public String getAssetVersionToken(){

       return  assetVersionPrefix+String.valueOf(getDeployId());

    }

    public String getDeployId(){

        return bootAgent.getDeployId();

    }

    public AppConfigurations.DeploymentMode getDeployMode(){

        return AppConfigurations.get().getDeploymentMode();

    }


    @PostConstruct
    public void setup(){
        if(AppContext.GSON_BUILDER==null)
            AppContext.GSON_BUILDER = new GsonBuilder();
        //Fire the GsonInit event
        LOGGER.info("Firing GSON initialization event...");
        gsonInitEvent.fire(new GsonInitEvent(AppContext.GSON_BUILDER));
    }


    public Gson createGsonInstance(){

        return AppContext.createGson();


    }

    public GsonBuilder getGsonBuilderInstance(){

        return AppContext.getGsonBuilder();

    }


    public static GsonBuilder getGsonBuilder(){

        return AppContext.GSON_BUILDER;

    }


    public static Gson createGson(){

        return AppContext.GSON_BUILDER.create();

    }

    public static void setGsonBuilder(GsonBuilder builder){
        if(builder==null)
            throw new IllegalArgumentException("builder reference must not be null");
        AppContext.GSON_BUILDER = builder;

    }

    public String getBaseURL() {
        return baseUrl;
    }

    public void setBaseURL(String baseUrl) {
        this.baseUrl = baseUrl;
        this.computeOrigin();
        this.computeDomain();
    }

    public boolean isBaseURLSet(){
        return this.baseUrl!=null;
    }

    public String getOrigin() {
        return this.origin;
    }

    public String getDomain() {
        return this.domain;
    }

    private void computeDomain(){
        int doubleSlashIndex = origin.indexOf("//");
        String withoutDoubleSlash = origin.substring(doubleSlashIndex+2,origin.length());
        int semiColonIndex = withoutDoubleSlash.indexOf(':');
        if(semiColonIndex != -1){
            domain = withoutDoubleSlash.substring(0,semiColonIndex);
        }else domain = withoutDoubleSlash;
    }

    private void computeOrigin(){
        StringBuilder builder = new StringBuilder();
        builder.append(this.baseUrl.substring(0,baseUrl.indexOf("//")+2));
        int doubleSlashIndex = this.baseUrl.indexOf("//");
        String withoutDoubleSlash = this.baseUrl.substring(doubleSlashIndex+2,this.baseUrl.length());
        int nextForwardSlashIndex = withoutDoubleSlash.indexOf('/');
        if(nextForwardSlashIndex != -1)
            builder.append(withoutDoubleSlash.substring(0,nextForwardSlashIndex));
        else builder.append(withoutDoubleSlash);
        this.origin = builder.toString();
    }

} @RequestScoped
public class FrontEnd {


    public static String TEMPLATE_SESSION_VARIABLE ="$template_";
    public static String LANGUAGE_SESSION_VARIABLE="$language_";
    public static final String COMMAND_RELOAD = "reload";
    public static final String COMMAND_RELOAD_LANGUAGE = "reloadLanguage";
    public static final String COMMAND_REDIRECT = "redirect";
    public static final String COMMAND_URL_PARAM = "url";

    private Map<String,Map> laterInvocations = new HashMap<>();

    @Inject
    private HttpServletRequest httpServletRequest;

    @Inject
    private ActiveUser activeUser;

    @Inject
    private RequestContext requestContext;

    @Inject
    private AppContext appContext;

    private String template;
    private String language;
    private Map<String,Object> templateData = new HashMap<>();


    private Logger log = LoggerFactory.getLogger(FrontEnd.class);


    @PostConstruct
    private void onReady(){

        this.language = AppConfigurations.get().getDefaultLanguage();

        Object templateObject = activeUser.getProperty(TEMPLATE_SESSION_VARIABLE);
        if(templateObject==null){
            template=AppConfigurations.get().getDefaultTemplate();
            activeUser.setProperty(TEMPLATE_SESSION_VARIABLE,template);

        }else template = templateObject.toString();


        Object langObject = activeUser.getProperty(LANGUAGE_SESSION_VARIABLE);
        if(langObject==null){
            language = AppConfigurations.get().getDefaultLanguage();
            activeUser.setProperty(LANGUAGE_SESSION_VARIABLE,language);

        }else language = langObject.toString();


    }



    public void refresh(){

        invokeAfter(COMMAND_RELOAD, Collections.emptyMap());

    }

    public void refresh(String url){

        Map<String,Object> map = new HashMap<>();
        map.put(COMMAND_URL_PARAM,url);
        invokeAfter(COMMAND_RELOAD, map);

    }


    public void submit(@Observes JSCommandSchedule jsCommandSchedule){

        this.invokeAfter(jsCommandSchedule.getName(),
                jsCommandSchedule.getParameters());

    }

    public void invokeAfter(String actionName, Map<String,Object> params) {

        laterInvocations.put("$"+actionName,params);

    }

    @Deprecated
    public void ajaxRedirect(String url){

        Map map = new HashMap<>();
        map.put(COMMAND_URL_PARAM,url);
        invokeAfter(COMMAND_REDIRECT,map);

    }

    public void putOnTemplate(String key, Object value){
        if(key==null||key.isEmpty())
            throw new IllegalArgumentException("Key must not be null nor empty");
        if(value==null)
            throw new IllegalArgumentException("Value must not be null");
        templateData.put(key,value);
    }

    public void setTemplateData(Map<String,Object> templateData) {
        if(templateData==null)
            throw new IllegalArgumentException("Template data Map must not be null");
        this.templateData = templateData;
    }

    public void setLanguage(String name){
        this.language = name;
        activeUser.setProperty(LANGUAGE_SESSION_VARIABLE,this.language);
        //Set reloadLanguage command if this method is invoked on an ajax request
        if(isRequestAjax()||isFrontierRequest())
            invokeAfter(COMMAND_RELOAD_LANGUAGE, Collections.emptyMap());
        else {

            if(I18nRuntime.isReady()){
                I18nRuntime.get().setLanguage(name);
            }

        }
    }

    public void setTemplate(String template) {
        String currentTemplate = activeUser.getProperty(TEMPLATE_SESSION_VARIABLE).toString();
        if(template.equals(currentTemplate)) {
            return;
        }

        log.debug(String.format("Template change [%s] => [%s]",currentTemplate,template));
        this.template = template;
        activeUser.setProperty(TEMPLATE_SESSION_VARIABLE,this.template);
        
        //Reload command if this method is invoked on an ajax request
        if(isRequestAjax()||isFrontierRequest()){
            StringBuilder url = new StringBuilder();
            url.append(appContext.getBaseURL());
            url.append(requestContext.getRouteUrl());
            String qs = httpServletRequest.getQueryString();
            if(qs!=null && !qs.isEmpty()){
                url.append("?");
                url.append(httpServletRequest.getQueryString());
            }
            log.debug("Ajax Reload: "+url.toString());
            refresh(url.toString());
        }


    }

    public boolean wasTemplateDataSet(){

        return templateData.size()>0;

    }

    public String getLanguage() {
        return language;
    }

    public String getTemplate() {

        return template;

    }


    public Map<String,Map> getLaterInvocations() {
        return laterInvocations;
    }
    public Map<String,Object> getTemplateData() {
        return templateData;
    }

    public boolean gotLaterInvocations() {

        return laterInvocations.size()>0;

    }

    public boolean isRequestAjax(){

        String headerValue = httpServletRequest.getHeader(RequestContext.AJAX_HEADER_KEY);
        return headerValue!=null&&headerValue.equalsIgnoreCase("1");

    }

    public boolean isFrontierRequest(){

        return httpServletRequest.getRequestURL().indexOf("jbind:")!=-1;

    }

    public String getLangDictionary(){
        String dictPath = "/i18n/"+language+".json";
        String dict = "{}";

        try {

            URL resource = httpServletRequest.getServletContext().getResource(dictPath);
            if(resource==null)
                return dict;
            dict = Helper.readLines(resource.openStream(),null);
            return dict;

        }catch (Throwable ex){

            return dict;

        }

    }




} @RequestScoped
public class RequestContext {

    public static String AJAX_HEADER_KEY = "AJAX_MVC";

    @Inject
    private AppContext appContext;

    @Inject
    private HttpServletRequest request = null;
    private HttpServletResponse response = null;

    @Inject
    private Event<JSCommandSchedule> jsCommandScheduleEvent;

    @Inject
    private ServletContext servletContext = null;
    private Map<String,Object> data = new HashMap<String, Object>();
    private String url = null;
    private String routeUrl;

    private OutputStream outputStream = null;
    private Logger log = LoggerFactory.getLogger(RequestContext.class);

    @PostConstruct
    private void getReady(){

        this.url = request.getRequestURI();

    }

    protected void setRouteUrl(String r){

        this.routeUrl = r;

    }

    public void setUrl(String url) {
        this.url = url;
    }

    protected  void setResponse(HttpServletResponse response){

        this.response = response;

    }

    public void echo(String  str){

        Helper.echo(str,this);

    }

    public void echoln(String  str){

        Helper.echoln(str,this);

    }


    public String readToEnd(InputStream inputStream){

        return  Helper.readLines(inputStream,this);

    }


    public String getUsername(){

        return request.getRemoteUser();

    }

    public boolean isUserLogged(){

        return request.getRemoteUser()!=null;

    }

    public OutputStream getOutputStream(){

        if(outputStream ==null){
            try {

                outputStream = response.getOutputStream();

            }catch (Throwable ex){
                log.error("Failed to get the HttpServletResponse OutputStream",ex);
            }
        }

        return outputStream;

    }

    public String getRouteUrl(){

        return routeUrl;

    }

    public  HttpServletRequest getRequest(){

        return request;

    }

    public HttpServletResponse getResponse(){

        return response;

    }

    public  boolean hasAjaxHeader(){

        if(request==null)
            return false;
        return request.getHeader(AJAX_HEADER_KEY)!=null;

    }



    public ServletContext getServletContext(){

        return  servletContext;

    }


    public String getUrl() {
        return url;
    }

    public Map<String,Object> getData(){

        return data;

    }

    public void sendRedirect(String route) throws IOException{
        if(hasAjaxHeader()){
            log.debug("Ajax Redirect: "+route);
            this.ajaxRedirect(routeUrl);
            return;
        }
        String path = appContext.getBaseURL() + route;
        log.debug("HTTP Redirect: "+path);
        getResponse().sendRedirect(path);
    }

    private void ajaxRedirect(String url){
        JSCommandSchedule redirectCommand = new JSCommandSchedule(FrontEnd.
                COMMAND_REDIRECT);
        redirectCommand.getParameters().put(FrontEnd.COMMAND_URL_PARAM, url);
        jsCommandScheduleEvent.fire(
                redirectCommand);
    }

} public interface ConfigProvider {

    public void load(ServletContext servletContext, ServletConfig config, Set<Index> index) throws HiException;
    public String getDocsPath();
    public AppConfigurations getAppConfigs();

} public class Frontiers {

    public class Security {

        public class CrossSiteRequestForgery {

            public class Token {

                private String jwtAlgorithm = "HS256";
                private String jwtPassphrase = "10045084-f522-4faa-afa4-bdbc955faa47";
                private int secureRandomSize = 64;

                public String getJwtAlgorithm() {
                    return jwtAlgorithm;
                }

                public void setJwtAlgorithm(String jwtAlgorithm) {
                    this.jwtAlgorithm = jwtAlgorithm;
                }

                public String getJwtPassphrase() {
                    return jwtPassphrase;
                }

                public void setJwtPassphrase(String jwtPassphrase) {
                    this.jwtPassphrase = jwtPassphrase;
                }

                public int getSecureRandomSize() {
                    return secureRandomSize;
                }

                public void setSecureRandomSize(int secureRandomSize) {
                    this.secureRandomSize = secureRandomSize;
                }
            }
            public class Cookie {

                private boolean httpOnly = true;
                private boolean secure = false;
                public static final String NAME = "_CSRF_";

                public boolean isHttpOnly() {
                    return httpOnly;
                }

                public void setHttpOnly(boolean httpOnly) {
                    this.httpOnly = httpOnly;
                }

                public boolean isSecure() {
                    return secure;
                }

                public void setSecure(boolean secure) {
                    this.secure = secure;
                }
            }

            private Token token = new Token();
            private Cookie cookie = new Cookie();

            public Token getToken() {
                return token;
            }

            public void setToken(Token token) {
                this.token = token;
            }

            public Cookie getCookie() {
                return cookie;
            }

            public void setCookie(Cookie cookie) {
                this.cookie = cookie;
            }
        }

        private CrossSiteRequestForgery crossSiteRequestForgery = new CrossSiteRequestForgery();

        public CrossSiteRequestForgery getCrossSiteRequestForgery() {
            return crossSiteRequestForgery;
        }

        public void setCrossSiteRequestForgery(CrossSiteRequestForgery crossSiteRequestForgery) {
            this.crossSiteRequestForgery = crossSiteRequestForgery;
        }

    }

    private Security security = new Security();
    private long defaultTimeout = 1500;

    public Security getSecurity() {
        return security;
    }

    public void setSecurity(Security security) {
        this.security = security;
    }

    public long getDefaultTimeout() {
        return defaultTimeout;
    }

    public void setDefaultTimeout(long defaultTimeout) {
        this.defaultTimeout = defaultTimeout;
    }
} public class CSRFAttackAttemptEvent extends HiEvent {

    

} public class FrontierRequestEvent extends MethodCallEvent {

    private Object override = null;
    private Object[] arguments;

    public FrontierRequestEvent(boolean isAfter) {
        super(isAfter);
    }

    public FrontierRequestEvent(boolean isAfter, Object[] arguments){
        super(isAfter);
        this.arguments = arguments;
    }

    public FrontierRequestEvent(Object[] arguments){
        super();
        this.arguments = arguments;
    }

    public FrontierRequestEvent(){

        super();

    }

    public void overrideResult(Object value){
        if(value==null)
            throw new IllegalArgumentException("Value object instance must not be null");
        this.override = value;

    }

    public Object[] getArguments() {
        return arguments;
    }

    public void setArguments(Object[] arguments) {
        this.arguments = arguments;
    }

    public boolean valueOverriden(){

        return this.override!=null;

    }

    public Object getOverrideValue(){

        return this.override;

    }


} public class FileUpload {

    private Part part;

    public FileUpload(Part part){

        this.part = part;

    }

    public File saveToFolder(String path) throws IOException {
        String filePath = path+"/"+getUploadFileName();
        return saveAs(filePath);
    }

    public File saveAs(File file) throws IOException{

        if(file.exists())
            throw new IllegalArgumentException("File "+file.getAbsolutePath()+" already exists");

        file.createNewFile();
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        InputStream partInput = getPart().getInputStream();
        byte[] buffer = new byte[2048];

        while (partInput.available() > 0) {
            if (partInput.available() < buffer.length)
                buffer = new byte[partInput.available()];
            int totalRead = partInput.read(buffer);
            fileOutputStream.write(buffer, 0, totalRead);
        }

        return file;

    }

    public File saveAs(String path) throws IOException{

        return saveAs(new File(path));

    }

    public String getUploadFileName(){

        return part.getSubmittedFileName();

    }

    public Part getPart(){

        return part;

    }

    public InputStream getInputStream() throws IOException{

        return part.getInputStream();

    }


} public class FrontierInvoker {

    private FrontierClass frontier;
    private FrontierMethod method;
    private Map params;
    private Object returnedObject = new HashMap<>();


    public FrontierInvoker(FrontierClass frontierClass, FrontierMethod method, Map params){

        this.frontier = frontierClass;
        this.method = method;
        this.params = params;

    }

    public void setReturnedObject(Object value){

        if(value==null)
            throw new IllegalArgumentException("Returned value must not be null");
        this.returnedObject = value;

    }

    public Object[] getCallArguments(){
        MethodParam methodParams[] = method.getParams();
        Object[] callParams = new Object[params.size()];
        int i = 0;
        for(MethodParam methodParam: methodParams){
            Object paramValue = params.get(methodParam.getName());
            callParams[i] = paramValue;
            i++;
        }
        return callParams;
    }

    public boolean invoke() throws Exception {

        return this.invoke(getCallArguments());

    }

    public boolean invoke(Object[] arguments) throws Exception {

        Object refreshedObj = frontier.getObject();
        try {

            returnedObject = method.getMethod().invoke(refreshedObj, arguments);

        }catch (Throwable ex){

            if(ex instanceof InvocationTargetException){
                Throwable throwable = ex.getCause();
                if(throwable instanceof ConstraintViolationException)
                    throw (ConstraintViolationException) throwable;
            }

            throw ex;

        }
        return true;
    }

    public Object getReturnedObject() {

        return returnedObject;

    }
} public class FrontierCallException extends HiException {

    private String frontierName;
    private String methodName;

    public FrontierCallException(String frontier, String method, Throwable ex){

        super("An error occurred while calling the method <"+method+"> of frontier <"+frontier+">",ex);
        this.frontierName = frontier;
        this.methodName = method;

    }

    public FrontierCallException(String frontier, String method, String msg){

        super(msg);
        this.frontierName = frontier;
        this.methodName = method;

    }

    public FrontierCallException(String frontier, String method, String msg, Throwable ex){

        super(msg,ex);
        this.frontierName = frontier;
        this.methodName = method;

    }


    public String getFrontierName() {
        return frontierName;
    }

    public String getMethodName() {
        return methodName;
    }
} public class InvalidFrontierParamException extends FrontierCallException {

    public InvalidFrontierParamException(String frontier, String method, String paramName){

        super(frontier,method,"Invalid value supplied for frontier param <"+paramName+"> for method <"+method+"> on "+frontier);

    }

} public class ResultConversionException extends FrontierCallException {

    public ResultConversionException(String frontier, String method, Throwable throwable){

        super(frontier,method,"Result of method method <"+method+"> on frontier <"+frontier+"> could not be converted to JSON successfully.",throwable);

    }

} public class MissingFrontierParamException extends FrontierCallException {

    public MissingFrontierParamException(String frontier, String method, String paramName){

        super(frontier,method,"No value supplied for frontier param <"+paramName+"> for method <"+method+"> on "+frontier);

    }


} public class FrontierClass {

    private HashMap<String,FrontierMethod> methodsMap = new HashMap();
    private HashMap<String,FrontierMethod> hashedMethodsMap = new HashMap();

    private String className;
    private String simpleName;
    private Object object;

    public FrontierClass(String clazz,String simpleName){

        this.className = clazz;
        this.simpleName = simpleName;
        //this.object = object;

    }

    public String getSimpleName(){

        return simpleName;

    }

    public String getClassName() {
        return className;
    }

    public void addMethod(FrontierMethod method){

        this.methodsMap.put(method.getName(),method);

    }

    public boolean hasMethod(String name){

        return methodsMap.containsKey(name);


    }

    public FrontierMethod getMethod(String name){

            return methodsMap.get(name);

    }


    public Object getObject() {

        try {

            return CDI.current().select(Class.forName(className)).get();


        }catch (Exception ex){

            return null;

        }
    }

    public Class getFrontierClazz(){

        try {

            return Class.forName(className);

        } catch (ClassNotFoundException e) {

            return null;

        }

    }




    public FrontierMethod[] getMethods(){

        FrontierMethod methods[] = new FrontierMethod[methodsMap.size()];
        methodsMap.values().toArray(methods);
        return methods;


    }
} public class FrontierMethod {

    private String name;
    private HashMap<String,MethodParam> paramsMap = new HashMap();
    private List<MethodParam> paramList = new ArrayList<MethodParam>();
    private Method method;

    public FrontierMethod(String name, Method method){
        this.name = name;
        this.method = method;

    }


    public MethodParam[] getParams(){

        MethodParam methodParam[] = new MethodParam[paramsMap.size()];
        paramList.toArray(methodParam);
        return methodParam;

    }

    public void addParam(MethodParam param){

        paramsMap.put(param.getName(),param);
        paramList.add(param);

    }

    public boolean hasParam(String name){

        return paramsMap.containsKey(name);

    }

    public MethodParam getParam(String name){


        return paramsMap.get(name);

    }

    public String getName() {

        return name;

    }

    public Method getMethod() {
        return method;
    }
} public class MethodParam {

    private String name;
    private Class type;
    private boolean nullable;

    public MethodParam(String name, Class type, boolean nullable){
        this.name = name;
        this.type = type;
        this.nullable = nullable;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Class getType() {
        return type;
    }

    public void setType(Class type) {
        this.type = type;
    }

    public boolean isNullable() {
        return nullable;
    }

    public void setNullable(boolean nullable) {
        this.nullable = nullable;
    }
} public class HTMLizer {

    private static HTMLizer instance = null;
    private static final String PREPARE_NEXT_VIEW = "Hi.$ui.js.createViewScope(vpath,context_vars,viewToLoad.html,false,false,false,false);";
    private static final String RUN_APP = "Hi.$angular.run();";
    private static final String BOOTSTRAP_ANGULAR = "angular.bootstrap(document, [\"hi\"]);";
    private static final String VIEW_NG = "<div id='app-view'></div>";

    public static final String JS_INVOKABLES_KEY = "$invoke";
    public static final String TEMPLATE_DATA_KEY = "$root";

    private GsonBuilder gsonBuilder = null;
    private RequestContext requestContext = null;
    private I18nContext i18nContext = null;
    private ActiveUser activeUser = null;
    private Map<String,String> cachedTemplates = Collections.synchronizedMap(new HashMap<>());

    private static Logger _log = LoggerFactory.getLogger(HTMLizer.class);

    private HTMLizer(String args){

        this.requestContext = CDI.current().select(RequestContext.class).get();

    }

    private String fetchTemplate(FrontEnd frontEnd, Event<TemplateTransformEvent> transformEvent) throws TemplateException {

        String templateName = frontEnd.getTemplate();

        URL templateURL = null;

        try {

            templateURL = requestContext.getServletContext().getResource("/" + templateName+".html");

        }catch (Exception ex){
            throw new NoSuchTemplateException(templateName);
        }

        if(templateURL==null)
            throw new NoSuchTemplateException(templateName);

        String templateFileContent = "";

        try {

            templateFileContent = Helper.readLines(templateURL.openStream(), null);

        }catch (Exception ex){
            throw new NoSuchTemplateException(templateName);
        }

        validateTemplate(templateName,templateFileContent);

        if(I18nRuntime.isReady())
            templateFileContent = I18nRuntime.get().translateTemplate(templateName,templateFileContent);
        TemplateTransformEvent templateTransformEvent =
                new TemplateTransformEvent(new Template(templateFileContent,templateName));
        transformEvent.fire(templateTransformEvent);
        templateFileContent = AppConfigurations.get().getTunings()
                .applySmartCaching(templateTransformEvent.getTemplate().getHtml(),false,templateName);


        return templateFileContent;

    }

    private void validateTemplate(String templateName, String templateFileContent) throws TemplateException {

        //Check the body tag, as its going to be replaced later in this same method
        int bodyCloseIndex = templateFileContent.indexOf("</body>");
        if(bodyCloseIndex==-1)
            throw new MalMarkedTemplateException(templateName,"token </body> could not be found. Body tag might not be present.");

        int headCloseIndex = templateFileContent.indexOf("</head>");
        if(headCloseIndex==-1)
            throw new MalMarkedTemplateException(templateName,"token </head> could not be found. Head tag might not be present");

        //Check the view_content div
        int viewContentDivByIdIndex = templateFileContent.indexOf("id=\"view_content\"");
        int viewContentDivByBrackets = templateFileContent.indexOf("{{view_content}}");

        if(viewContentDivByIdIndex==-1)
            throw new MalMarkedTemplateException(templateName,"no element with id property set to \"view_content\" could be found");

        if(viewContentDivByBrackets==-1)
            throw new MalMarkedTemplateException(templateName,"token {{view_content}} could not be found.");

    }


    private String javascriptVar(String name, String value){

        if(name==null)
            throw new NullPointerException("Variable name argument is null");

        if(name.trim().length()==0)
            throw new IllegalArgumentException("Variable name argument has an empty value");

        String declaration="var "+name+"="+value.toString()+";";
        return declaration;

    }

    private String makeJavascript(String code){

        return "<script>"+code+"</script>";

    }

    private String makeFunction(String name,String code){

        return "function "+name+"(){"+code+"}";

    }


    private String getViewInitSnippet(){

        return getNextClosureInformation()
                + getViewJS()+getControllerSetter();

    }


    private String getLoaderScript(FrontEnd frontEnd){

        StringBuilder scriptBuilder = new StringBuilder();
        scriptBuilder.append(" var appLang = "+ frontEnd.getLangDictionary()+";");
        scriptBuilder.append(" var appLangName = '"+ frontEnd.getLanguage()+"';");
        scriptBuilder.append("if(typeof loadApp==\"function\"){");
        scriptBuilder.append("if (window.addEventListener)");
        scriptBuilder.append(" window.addEventListener(\"load\", loadApp, false);");
        scriptBuilder.append(" else if (window.attachEvent)");
        scriptBuilder.append(" window.attachEvent(\"onload\",loadApp);");
        scriptBuilder.append("else window.onload = loadApp;");
        scriptBuilder.append(" }else{");
        scriptBuilder.append("if(typeof $ignition==\"function\"){");
        scriptBuilder.append(getViewInitSnippet());//New code
        scriptBuilder.append("angular.element(document).ready(function() {");
        scriptBuilder.append("$ignition();");
        scriptBuilder.append("});}}");
        return scriptBuilder.toString();

    }

    private String getAppData(){

        activeUser = CDI.current().select(ActiveUser.class).get();
        CharSequence http = "http://";
        CharSequence https = "https://";
        AppContext appContext = CDI.current().select(AppContext.class).get();
        Gson gson = AppContext.createGson();

        Map map = new HashMap();
        map.put("base_url", appContext.getBaseURL());
        map.put("base64_url", new BASE64Encoder().encode(appContext.getBaseURL().getBytes()));
        map.put("simple_base_url", appContext.getBaseURL().replace(http,"").replace(https,""));
        map.put("deployId",appContext.getDeployId());
        map.put("deployMode",appContext.getDeployMode().toString());
        map.put("eventsToken",activeUser.getEventsToken());
        return gson.toJson(map);

    }


    private String getNextClosureInformation(){

        String controller = requestContext.getData().get("controllerU").toString();
        String action = requestContext.getData().get("actionU").toString();
        String functionInvocation = "Hi.$nav.setNextControllerInfo(\""+controller+"\",\""+action+"\");";
        return functionInvocation;


    }

    private String getNextViewPath(){

        String controller = requestContext.getData().get("controllerU").toString();
        String action = requestContext.getData().get("actionU").toString();
        String functionInvocation = "var vpath = Hi.$nav.getViewPath(\""+controller+"\",\""+action+"\");";
        return functionInvocation;


    }

    private String getControllerSetter(){

        String controller = requestContext.getData().get("controllerU").toString();
        String action = requestContext.getData().get("actionU").toString();
        String functionInvocation = "Hi.$ui.js.setLoadedController(\""+controller+"\",\""+action+"\");";
        return functionInvocation;

    }

    private String getViewJS(){

        if(!requestContext.getData().containsKey("view_js"))
            return "Hi.view(function(_){})";

        return requestContext.getData().get("view_js").toString();

    }

    private boolean ignoreView(){

        return requestContext.getRequest()
                .getHeader("Ignore-View")!=null;

    }

    private boolean ignoreJs(){

        return requestContext.getRequest()
                .getHeader("Ignore-Js")!=null;

    }

    private void sendToClient(String result){

        requestContext.getResponse().setHeader("Cache-Control", "no-cache");
        requestContext.getResponse().setContentType("text/html;charset=UTF8");
        Helper.echo(result, requestContext);

    }

    private String ajaxProcess(FrontEnd frontEnd, String viewHTML,
                               Map<String,Object> viewData,Map route,
                               Controller controller) throws ConversionFailedException {

        Map<String,Map> actions = frontEnd.getLaterInvocations();
        if(!actions.isEmpty())
            viewData.put(JS_INVOKABLES_KEY,actions);

        Map map = new HashMap();

        if(!ignoreView()) map.put("view", viewHTML);
        if(!ignoreJs())   map.put("controller", getViewJS());
        map.put("data", viewData);
        map.put("route",route);
        map.put("response", 200);

        String resultResponse = null;
        Gson gson = gsonBuilder.create();

        try {

            resultResponse = gson.toJson(map);

        }catch (Exception ex){

            String actionMethod = requestContext.getData().get("action").toString();
            throw new ConversionFailedException(controller.getClass().getCanonicalName(),actionMethod,ex);

        }

        requestContext.getResponse().setContentType("text/json;charset=UTF8");
        requestContext.echo(resultResponse);
        return resultResponse;

    }


    private String normalProcess(FrontEnd frontEnd, String viewHTML, Map<String,Object> viewData,
                                 Map route, Controller controller, String loaderJSContent,
                                 String template) throws ConversionFailedException {

        viewData.put("$route",route);
        String loaderJavascript = makeJavascript(getLoaderScript(frontEnd));

        if(loaderJSContent==null) loaderJSContent="<!--Empty loader-->";
        else {
            String tokenToReplace = "//_place_init_code_here";
            String replacement = getViewInitSnippet();
            loaderJSContent = loaderJSContent.replace(tokenToReplace,replacement);
        }

        template = transformHeadAndBody(template,loaderJavascript,loaderJSContent);
        Gson gson = AppContext.createGson();
        String viewDataStr = null;

        try{

            viewDataStr = gson.toJson(viewData);

        }catch (Exception ex){
            String actionMethod = requestContext.getData().get("action").toString();
            throw new ConversionFailedException(controller.getClass().getCanonicalName(),actionMethod,ex);
        }

        Map html = new HashMap();
        html.put("html",viewHTML);
        String allJson = prepareIgnitionJS(viewDataStr,gson,html);

        allJson+="\n"+VIEW_NG;
        CharSequence toReplace = "{{view_content}}";
        CharSequence replaceBy = allJson;
        String processedResult =  template.replace(toReplace,replaceBy);

        this.sendToClient(processedResult);
        return processedResult;


    }


    private String prepareIgnitionJS(String viewDataStr, Gson gson, Map html){

        String appVariable = makeJavascript(javascriptVar("App",getAppData().toString()));
        String contextVarsVariable = makeJavascript(javascriptVar("context_vars",viewDataStr));
        String startupScript = makeJavascript(makeFunction("$startup",getNextViewPath()+PREPARE_NEXT_VIEW));
        String ignitionScript = makeJavascript(makeFunction("$ignition",RUN_APP+ BOOTSTRAP_ANGULAR));
        String viewToLoadVariable = makeJavascript(javascriptVar("viewToLoad",gson.toJson(html)));
        return appVariable+contextVarsVariable+viewToLoadVariable+startupScript+ignitionScript;

    }

    private String transformHeadAndBody(String template, String loaderJavascript, String loadedJSContent){

        CharSequence bodyCloseTag = "</body>";
        template = template.replace(bodyCloseTag,"</body>"+loaderJavascript);

        CharSequence headCloseTag = "</head>";
        CharSequence headClosedScript = "</head>"+makeJavascript(loadedJSContent);
        template = template.replace(headCloseTag,headClosedScript);
        return template;

    }

    public String process(Controller controller, boolean ignoreView,
                          boolean withViewMode, String viewMode,
                          Event<TemplateTransformEvent> templateTransformEventEvent,
                          Event<ViewTransformEvent> viewTransformEvent) throws TemplateException, ConversionFailedException {

        requestContext.getResponse().setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        FrontEnd frontEnd = CDI.current().select(FrontEnd.class).get();
        ES5Library es5Lib = CDI.current().select(ES5Library.class).get();
        String template = fetchTemplate(frontEnd,templateTransformEventEvent);
        String loaderJSContent= es5Lib.getHiLoaderJS();
        Map<String,Object> viewData = (Map<String,Object>) requestContext.getData().get(Controller.VIEW_DATA_KEY);

        String viewHTML = null;
        if(requestContext.getData().containsKey("view_content")){
            viewHTML = requestContext.getData().get("view_content").toString();
            if(viewHTML!=null&&!AppConfigurations.get().underDevelopment())
                viewHTML = AppConfigurations.get().getTunings().applySmartCaching(viewHTML, true,null);
        }

        if(viewHTML!=null){

             if(I18nRuntime.isReady()){
                 String viewId = requestContext.getData().get("controllerU").toString()+":"+requestContext.getData().get("actionU").toString();
                 if(withViewMode)
                     viewId+=viewMode;
                 I18nRuntime i18n = I18nRuntime.get();
                 viewHTML = i18n.translateView(viewId,viewHTML);
             }

            View v = new View(viewHTML);
            ViewTransformEvent event = new ViewTransformEvent(v);
            viewTransformEvent.fire(event);
            viewHTML = event.getTransformable().getHtml();

        }

        Map<String,Object> route = new HashMap<>();
        route.put("controller", requestContext.getData().get("controllerU").toString());
        route.put("action", requestContext.getData().get("actionU").toString());
        if(withViewMode)
            route.put("mode",viewMode);
        else route.put("mode",false);

        if(!viewData.containsKey(TEMPLATE_DATA_KEY))
            viewData.put(TEMPLATE_DATA_KEY, new HashMap<>());

        Map $templateDataMap = (Map) viewData.get("$root");
        viewData.put("$root",$templateDataMap);

        String mappedPath = "/"+requestContext.getRouteUrl();
        if(requestContext.getRequest().getHeader("Ignore-i18nMapping")==null){

            if(I18nRuntime.isReady()){

                Map<String,String> dictionary = new HashMap<>();

                I18nRuntime runtime = I18nRuntime.get();
                if(runtime.getConfiguration().isMappingsEnabled()) {

                    //Export the dictionary mapped to the current route
                    if (runtime.hasDictionary(mappedPath)) {
                        _log.debug(String.format("Exporting language dictionaries mapped to path : [%s]",mappedPath));
                        dictionary.putAll(runtime.getDictionary(mappedPath));
                    }else _log.warn(String.format("No dictionary mapped to path [%s]",mappedPath));

                }

                //Full bundle exportation disabled
                if(!runtime.getConfiguration().isExportLanguageBundle()){
                    dictionary.putAll(i18nContext.collect());
                    if(dictionary.size()>0)
                        _log.debug(String.format("Exporting %d dictionary terms",dictionary.size()));
                }

                viewData.put("$dictionary", dictionary);

            }

        }else{

            _log.debug(String.format("Ignoring I18N Mappings for [%s]",mappedPath));

        }


        if(requestContext.hasAjaxHeader()) /* AJAX REQUEST */
            return ajaxProcess(frontEnd,viewHTML,viewData,route,controller);
        return normalProcess(frontEnd,viewHTML,viewData,
                route,controller,loaderJSContent,template); /* NORMAL REQUEST */

    }

    public void setRequestContext(RequestContext requestContext){

        this.requestContext = requestContext;

    }

    public void setI18nContext(I18nContext i18nContext){

        this.i18nContext = i18nContext;

    }

    public void setGsonBuilder(GsonBuilder builder){

        this.gsonBuilder = builder;

    }


    public static HTMLizer getInstance(){

        return new HTMLizer(null);

    }


} public class SecureTokenUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(SecureTokenUtil.class);

    private String makeSecureRandom(){
        AppConfigurations appConfigurations = AppConfigurations.get();
        Frontiers frontiers = appConfigurations.getFrontiersConfig();
        Frontiers.Security frontiersSecurity = frontiers.getSecurity();
        int secureRandomSize = frontiersSecurity.getCrossSiteRequestForgery().getToken().getSecureRandomSize();
        SecureRandom secureRandom = new SecureRandom();
        byte[] randomBuffer = new byte[secureRandomSize];
        secureRandom.nextBytes(randomBuffer);
        return DatatypeConverter.printHexBinary(randomBuffer);
    }

    public String makeJwtToken(){
        AppConfigurations appConfigurations = AppConfigurations.get();
        Frontiers frontiers = appConfigurations.getFrontiersConfig();
        Frontiers.Security frontiersSecurity = frontiers.getSecurity();
        Frontiers.Security.CrossSiteRequestForgery.Token tokenConfig = frontiersSecurity
                .getCrossSiteRequestForgery()
                .getToken();

        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.valueOf(tokenConfig
                .getJwtAlgorithm());

        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        byte[] secretBytes = DatatypeConverter.parseBase64Binary(tokenConfig.getJwtPassphrase());
        Key signingKey = new SecretKeySpec(secretBytes, signatureAlgorithm.getJcaName());

        //Let's set the JWT Claims
        JwtBuilder builder = Jwts.builder().setId(UUID.randomUUID().toString())
                .setIssuedAt(now)
                .setSubject(makeSecureRandom())
                .setIssuer("")
                .signWith(signatureAlgorithm, signingKey);

        return builder.compact();
    }

    public boolean checkJwtToken(String jwt){
        AppConfigurations appConfigurations = AppConfigurations.get();
        Frontiers frontiers = appConfigurations.getFrontiersConfig();
        Frontiers.Security frontiersSecurity = frontiers.getSecurity();
        Frontiers.Security.CrossSiteRequestForgery.Token tokenConfig = frontiersSecurity
                .getCrossSiteRequestForgery()
                .getToken();
        byte[] secretBytes = DatatypeConverter.parseBase64Binary(tokenConfig
                .getJwtPassphrase());
        try {
            Jwts.parser().setSigningKey(secretBytes).parseClaimsJws(jwt)
                    .getBody()
                    .getSubject();
            return true;
        }catch (SignatureException ex){
            return false;
        }catch (JwtException ex){
            LOGGER.error("Error validating JSON Web Token: "+jwt, ex);
            return false;
        }
    }

}
import com.google.gson.*;
import org.emerjoin.hi.web.ActiveUser;
import org.emerjoin.hi.web.AppContext;
import org.emerjoin.hi.web.FrontEnd;
import org.emerjoin.hi.web.RequestContext;
import org.emerjoin.hi.web.config.ConfigProvider;
import org.emerjoin.hi.web.config.Frontiers;
import org.emerjoin.hi.web.events.CSRFAttackAttemptEvent;
import org.emerjoin.hi.web.events.FrontierRequestEvent;
import org.emerjoin.hi.web.frontier.FileUpload;
import org.emerjoin.hi.web.frontier.FrontierInvoker;
import org.emerjoin.hi.web.frontier.exceptions.FrontierCallException;
import org.emerjoin.hi.web.frontier.exceptions.InvalidFrontierParamException;
import org.emerjoin.hi.web.frontier.exceptions.ResultConversionException;
import org.emerjoin.hi.web.frontier.exceptions.MissingFrontierParamException;
import org.emerjoin.hi.web.frontier.model.FrontierClass;
import org.emerjoin.hi.web.frontier.model.FrontierMethod;
import org.emerjoin.hi.web.frontier.model.MethodParam;
import org.emerjoin.hi.web.mvc.HTMLizer;
import org.emerjoin.hi.web.security.SecureTokenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
package org.emerjoin.hi.web.req;



@HandleRequests(regexp = "jbind:[$_A-Za-z0-9=]+", supportPostMethod = true)
@ApplicationScoped
public class FrontiersReqHandler extends ReqHandler {

    private static Map<String,FrontierClass> frontiersMap = new HashMap();
    private static final Logger LOGGER = LoggerFactory.getLogger(FrontiersReqHandler.class);

    public static void addFrontier(FrontierClass frontierClass){
        frontiersMap.put(frontierClass.getSimpleName(),frontierClass);
    }

    public static boolean frontierExists(String name){
        return frontiersMap.containsKey(name);
    }

    public static FrontierClass getFrontier(String name){
        return frontiersMap.get(name);
    }

    @Inject
    private AppContext appContext;
    @Inject
    private FrontEnd frontEnd;
    @Inject
    private ServletContext servletContext;
    @Inject
    private RequestContext requestContext;

    @Inject
    private ActiveUser activeUser;

    @Inject
    private Event<FrontierRequestEvent> frontierRequestEvent;