com.chiorichan.site.Site.java Source code

Java tutorial

Introduction

Here is the source code for com.chiorichan.site.Site.java

Source

/**
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * Copyright 2015 Chiori-chan. All Right Reserved.
 * 
 * @author Chiori Greene
 * @email chiorigreene@gmail.com
 */
package com.chiorichan.site;

import groovy.lang.Binding;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.ConnectException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;

import org.json.JSONObject;

import com.chiorichan.Loader;
import com.chiorichan.configuration.ConfigurationSection;
import com.chiorichan.configuration.file.YamlConfiguration;
import com.chiorichan.database.DatabaseEngine;
import com.chiorichan.event.EventException;
import com.chiorichan.event.server.SiteLoadEvent;
import com.chiorichan.factory.EvalFactory;
import com.chiorichan.factory.EvalMetaData;
import com.chiorichan.http.Routes;
import com.chiorichan.lang.EvalFactoryException;
import com.chiorichan.lang.SiteException;
import com.chiorichan.lang.StartupException;
import com.chiorichan.util.FileUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

public class Site {
    protected String siteId = null, title = null, domain = null;
    protected File source, resource;
    protected Map<String, String> subdomains = Maps.newConcurrentMap(), aliases = Maps.newConcurrentMap();
    protected List<String> metatags = Lists.newCopyOnWriteArrayList(),
            protectedFiles = Lists.newCopyOnWriteArrayList();
    protected YamlConfiguration config;
    protected DatabaseEngine sql;
    protected SiteType siteType = SiteType.NOTSET;
    protected File filePath = null;
    protected File cacheDir = null;
    protected List<String> cachePatterns = Lists.newArrayList();
    protected Routes routes = null;

    // Binding and evaling for use inside each site for executing site scripts outside of web requests.
    Binding binding = new Binding();
    EvalFactory factory = EvalFactory.create(binding);

    protected Site(File f) throws SiteException, StartupException {
        siteType = SiteType.FILE;
        filePath = f;

        config = YamlConfiguration.loadConfiguration(f);

        if (config == null)
            throw new SiteException("Could not load site from YAML FileBase '" + f.getAbsolutePath() + "'");

        siteId = config.getString("site.siteId", null);
        title = config.getString("site.title",
                Loader.getConfig().getString("framework.sites.defaultTitle", "Unnamed Site"));
        domain = config.getString("site.domain", null);

        String reason = null;

        if (siteId == null)
            reason = "the provided Site Id is NULL. Check configs";
        else
            siteId = siteId.toLowerCase();

        if (domain == null)
            reason = "the provided domain is NULL. Check configs";
        else
            domain = domain.toLowerCase();

        if (Loader.getSiteManager().getSiteById(siteId) != null)
            reason = "there already exists a site by the provided Site Id '" + siteId + "'";

        if (reason != null)
            throw new SiteException(
                    "Could not load site from YAML FileBase '" + f.getAbsolutePath() + "' because " + reason + ".");

        Loader.getLogger().info("Loading site '" + siteId + "' with title '" + title + "' from YAML FileBase '"
                + f.getAbsolutePath() + "'.");

        // Load protected files list
        List<?> protectedFilesPre = config.getList("protected", new CopyOnWriteArrayList<String>());

        for (Object o : protectedFilesPre) {
            if (o instanceof String)
                protectedFiles.add((String) o);
            else
                Loader.getLogger().warning("Site '" + siteId
                        + "' had an incorrect data object type under the YAML config for option 'protected', found type '"
                        + o.getClass() + "'.");
        }

        // Load sources location
        String sources = config.getString("site.source", "pages");

        if (sources == null || sources.isEmpty()) {
            source = getAbsoluteRoot();
        } else if (sources.startsWith(".")) {
            source = new File(getAbsoluteRoot() + sources);
        } else {
            source = new File(getAbsoluteRoot(), sources);
            protectedFiles.add("/" + sources);
        }

        FileUtil.directoryHealthCheck(source);

        // Load resources location
        String resources = config.getString("site.resource", "resource");

        if (resources == null || resources.isEmpty()) {
            resource = getAbsoluteRoot();
        } else if (resources.startsWith(".")) {
            resource = new File(getAbsoluteRoot() + resources);
        } else {
            resource = new File(getAbsoluteRoot(), resources);
            protectedFiles.add("/" + resources);
        }

        FileUtil.directoryHealthCheck(resource);

        // Load metatags
        List<?> metatagsPre = config.getList("metatags", new CopyOnWriteArrayList<String>());

        for (Object o : metatagsPre) {
            if (o instanceof String)
                metatags.add((String) o);
            else
                Loader.getLogger().warning("Site '" + siteId
                        + "' had an incorrect data object type under the YAML config for option 'metatags', found type '"
                        + o.getClass() + "'.");
        }

        // Load aliases map
        ConfigurationSection aliasesPre = config.getConfigurationSection("aliases");
        if (aliasesPre != null) {
            Set<String> akeys = aliasesPre.getKeys(false);

            if (akeys != null)
                for (String k : akeys) {
                    if (aliasesPre.getString(k, null) != null)
                        aliases.put(k, aliasesPre.getString(k));
                }
        }

        // Loader subdomains map
        ConfigurationSection subdomainsPre = config.getConfigurationSection("subdomains");
        if (subdomainsPre != null) {
            Set<String> skeys = subdomainsPre.getKeys(false);

            if (skeys != null)
                for (String k : skeys) {
                    if (subdomainsPre.getString(k, null) != null)
                        subdomains.put(k, subdomainsPre.getString(k));
                }
        }

        finishLoad();
    }

    @SuppressWarnings("unchecked")
    protected Site(ResultSet rs) throws SiteException, StartupException {
        siteType = SiteType.SQL;

        try {
            Type mapType = new TypeToken<HashMap<String, String>>() {
            }.getType();

            siteId = rs.getString("siteId");
            title = rs.getString("title");
            domain = rs.getString("domain");

            String reason = null;

            if (siteId == null)
                reason = "the provided Site Id is NULL. Check configs";
            else
                siteId = siteId.toLowerCase();

            if (title == null)
                title = Loader.getConfig().getString("framework.sites.defaultTitle",
                        "Unnamed Chiori-chan's Web Server Site");

            if (domain == null)
                reason = "the provided domain is NULL. Check configs";
            else
                domain = domain.toLowerCase();

            if (Loader.getSiteManager().getSiteById(siteId) != null)
                reason = "there already exists a site by the provided Site Id '" + siteId + "'";

            if (reason != null)
                throw new SiteException("Could not load site from Database because " + reason + ".");

            Loader.getLogger().info("Loading site '" + siteId + "' with title '" + title + "' from Database.");

            Gson gson = new GsonBuilder().create();
            try {
                if (!rs.getString("protected").isEmpty())
                    protectedFiles.addAll(
                            gson.fromJson(new JSONObject(rs.getString("protected")).toString(), ArrayList.class));
            } catch (Exception e) {
                Loader.getLogger()
                        .warning("MALFORMED JSON EXPRESSION for 'protected' field for site '" + siteId + "'");
            }

            String sources = rs.getString("source");

            if (sources == null || sources.isEmpty()) {
                source = getAbsoluteRoot();
            } else if (sources.startsWith(".")) {
                source = new File(getAbsoluteRoot() + sources);
            } else {
                source = new File(getAbsoluteRoot(), sources);
                protectedFiles.add("/" + sources);
            }

            if (source.isFile())
                source.delete();

            if (!source.exists())
                source.mkdirs();

            String resources = rs.getString("resource");

            if (resources == null || resources.isEmpty()) {
                resource = getAbsoluteRoot();
            } else if (resources.startsWith(".")) {
                resource = new File(getAbsoluteRoot() + resources);
            } else {
                resource = new File(getAbsoluteRoot(), resources);
                protectedFiles.add("/" + resources);
            }

            if (resource.isFile())
                resource.delete();

            if (!resource.exists())
                resource.mkdirs();

            try {
                if (!rs.getString("metatags").isEmpty())
                    metatags.addAll(
                            gson.fromJson(new JSONObject(rs.getString("metatags")).toString(), ArrayList.class));
            } catch (Exception e) {
                Loader.getLogger()
                        .warning("MALFORMED JSON EXPRESSION for 'metatags' field for site '" + siteId + "'");
            }

            try {
                if (!rs.getString("aliases").isEmpty())
                    aliases = gson.fromJson(new JSONObject(rs.getString("aliases")).toString(), mapType);
            } catch (Exception e) {
                Loader.getLogger()
                        .warning("MALFORMED JSON EXPRESSION for 'aliases' field for site '" + siteId + "'");
            }

            try {
                if (!rs.getString("subdomains").isEmpty())
                    subdomains = gson.fromJson(new JSONObject(rs.getString("subdomains")).toString(), mapType);
            } catch (Exception e) {
                Loader.getLogger()
                        .warning("MALFORMED JSON EXPRESSION for 'subdomains' field for site '" + siteId + "'");
            }

            try {
                String yaml = rs.getString("configYaml");
                InputStream is = new ByteArrayInputStream(yaml.getBytes("ISO-8859-1"));
                config = YamlConfiguration.loadConfiguration(is);
            } catch (Exception e) {
                Loader.getLogger()
                        .warning("MALFORMED YAML EXPRESSION for 'configYaml' field for site '" + siteId + "'");
                config = new YamlConfiguration();
            }

            finishLoad();
        } catch (SQLException e) {
            throw new SiteException(e);
        }
    }

    private void finishLoad() throws SiteException, StartupException {
        // Framework site always uses the Builtin SQL Connector. Ignore YAML FileBase on this one.
        if (siteId.equalsIgnoreCase("framework")) {
            sql = Loader.getDatabase();
        } else if (config != null && config.getConfigurationSection("database") != null) {
            String type = config.getString("database.type");

            String host = config.getString("database.host");
            String port = config.getString("database.port");
            String database = config.getString("database.database");
            String username = config.getString("database.username");
            String password = config.getString("database.password");

            String filename = config.getString("database.filename");

            sql = new DatabaseEngine();

            try {
                if (type.equalsIgnoreCase("mysql"))
                    sql.init(database, username, password, host, port);
                else if (type.equalsIgnoreCase("sqlite"))
                    sql.init(filename);
                else
                    throw new SiteException("The SqlConnector for site '" + siteId
                            + "' can not support anything other then mySql or sqLite at the moment. Please change 'database.type' in the site config to 'mysql' or 'sqLite' and set the connection params.");
            } catch (SQLException e) {
                if (e.getCause() instanceof ConnectException)
                    throw new SiteException("We had a problem connecting to database '" + database + "'. Reason: "
                            + e.getCause().getMessage());
                else
                    throw new SiteException(e.getMessage());
            }
        }

        if (config != null) {
            List<String> onLoadScripts = config.getStringList("scripts.on-load");

            if (onLoadScripts != null) {
                for (String script : onLoadScripts) {
                    try {
                        EvalMetaData meta = new EvalMetaData();
                        meta.shell = "groovy";

                        File file = getResourceWithException(script);
                        String result = factory.eval(file, meta, this).getString();

                        if (result == null || result.isEmpty())
                            Loader.getLogger().info(
                                    "Finsihed evaling onLoadScript '" + script + "' for site '" + siteId + "'");
                        else
                            Loader.getLogger().info("Finsihed evaling onLoadScript '" + script + "' for site '"
                                    + siteId + "' with result: " + result);
                    } catch (EvalFactoryException e) {
                        SiteManager.getLogger()
                                .warning("There was an exception encountered while evaling onLoadScript '" + script
                                        + "' for site '" + siteId + "'.", e);
                    } catch (FileNotFoundException e) {
                        SiteManager.getLogger().warning(
                                "The onLoadScript '" + script + "' was not found for site '" + siteId + "'.");
                    }
                }
            }
        }

        SiteLoadEvent event = new SiteLoadEvent(this);

        try {
            Loader.getEventBus().callEventWithException(event);
        } catch (EventException e) {
            throw new SiteException(e);
        }

        // Plugins are not permitted to cancel the loading of the framework site
        if (event.isCancelled() && !siteId.equalsIgnoreCase("framework"))
            throw new SiteException("Loading of site '" + siteId + "' was cancelled by an internal event.");

        if (new File(getAbsoluteRoot(), "fw").exists() && !siteId.equalsIgnoreCase("framework"))
            SiteManager.getLogger().warning("It would appear that site '" + siteId
                    + "' contains a subfolder by the name of 'fw', since this server uses the uri '/fw' for special functions, you will be unable to serve files from this folder!");
    }

    protected void save() {
        switch (siteType) {
        case FILE:

            break;
        case SQL:

            break;
        default: // DO NOTHING
        }
    }

    protected Site setDatabase(DatabaseEngine sql) {
        this.sql = sql;

        return this;
    }

    public YamlConfiguration getYaml() {
        if (config == null)
            config = new YamlConfiguration();

        return config;
    }

    public List<String> getMetatags() {
        if (metatags == null)
            return new CopyOnWriteArrayList<String>();

        return metatags;
    }

    public Map<String, String> getAliases() {
        return aliases;
    }

    public Site(String id, String title0, String domain0) {
        siteId = id;
        title = title0;
        domain = domain0;
        protectedFiles = Lists.newCopyOnWriteArrayList();
        metatags = Lists.newCopyOnWriteArrayList();
        aliases = Maps.newLinkedHashMap();
        subdomains = Maps.newLinkedHashMap();

        source = getAbsoluteRoot();
        resource = new File(getAbsoluteRoot(), "resource");

        if (!source.exists())
            source.mkdirs();

        if (!resource.exists())
            resource.mkdirs();
    }

    public boolean protectCheck(String file) {
        if (protectedFiles == null)
            return false;

        // Does this file belong to our webroot
        if (file.startsWith(getRoot())) {
            // Strip our webroot from file
            file = file.substring(getRoot().length());

            for (String n : protectedFiles) {
                if (n != null && !n.isEmpty()) {
                    // If the length is greater then 1 and file starts with this string.
                    if (n.length() > 1 && file.startsWith(n))
                        return true;

                    // Does our file end with this. ie: .php, .txt, .etc
                    if (file.endsWith(n))
                        return true;

                    // If the pattern does not start with a /, see if name contain this string.
                    if (!n.startsWith("/") && file.contains(n))
                        return true;

                    // Lastly try the string as a RegEx pattern
                    if (file.matches(n))
                        return true;
                }
            }
        }

        return false;
    }

    public File getAbsoluteRoot() {
        try {
            return getAbsoluteRoot(null);
        } catch (SiteException e) {
            return null;
            // A SiteException will never be thrown when the subdomain is empty.
        }
    }

    public File getAbsoluteRoot(String subdomain) throws SiteException {
        File target = new File(Loader.webroot, getRoot(subdomain));

        if (target.isFile())
            target.delete();

        if (!target.exists())
            target.mkdirs();

        return target;
    }

    public String getRoot() {
        try {
            return getRoot(null);
        } catch (SiteException e) {
            return null;
            // A SiteException will never be thrown when the subdomain is empty.
        }
    }

    public String getRoot(String subdomain) throws SiteException {
        String target = siteId;

        if (subdomains != null && subdomain != null && !subdomain.isEmpty()) {
            String sub = subdomains.get(subdomain);

            if (sub != null)
                target = siteId + "/" + sub;
            else if (Loader.getConfig().getBoolean("framework.sites.autoCreateSubdomains", true))
                target = siteId + "/" + subdomain;
            else if (!Loader.getConfig().getBoolean("framework.sites.subdomainsDefaultToRoot"))
                throw new SiteException(
                        "This subdomain was not found on this server. If your the website owner, please check documentation.");
        }

        return target;
    }

    public DatabaseEngine getDatabase() {
        return sql;
    }

    public String getName() {
        return siteId;
    }

    public File getResourceDirectory() {
        if (resource == null)
            resource = new File(getAbsoluteRoot(), "resource");

        return resource;
    }

    public File getCacheDirectory() {
        if (cacheDir == null)
            cacheDir = new File(Loader.getTempFileDirectory(), this.getSiteId());

        cacheDir.mkdirs();

        return cacheDir;
    }

    public File getSourceDirectory() {
        if (source == null)
            source = getAbsoluteRoot();

        return source;
    }

    public void setAutoSave(boolean b) {
        // TODO Auto-generated method stub
    }

    // TODO: Add methods to add protected files, metatags and aliases to site and save

    public void setGlobal(String key, Object val) {
        binding.setVariable(key, val);
    }

    public Object getGlobal(String key) {
        return binding.getVariable(key);
    }

    @SuppressWarnings("unchecked")
    public Map<String, Object> getGlobals() {
        return binding.getVariables();
    }

    protected Binding getBinding() {
        return binding;
    }

    public File getResource(String packageNode) {
        try {
            return getResourceWithException(packageNode);
        } catch (FileNotFoundException e) {
            if (!packageNode.contains(".includes."))
                Loader.getLogger().warning(e.getMessage());
            return null;
        }
    }

    public File getResourceWithException(String pack) throws FileNotFoundException {
        if (pack == null || pack.isEmpty())
            throw new FileNotFoundException("Package can't be empty!");

        pack = pack.replace(".", System.getProperty("file.separator"));

        File root = getResourceDirectory();

        File packFile = new File(root, pack);

        if (packFile.exists())
            return packFile;

        root = packFile.getParentFile();

        if (root.exists() && root.isDirectory()) {
            File[] files = root.listFiles();
            String[] exts = new String[] { "html", "htm", "groovy", "gsp", "jsp", "chi" };

            for (File child : files)
                if (child.getName().startsWith(packFile.getName()))
                    for (String ext : exts)
                        if (child.getName().toLowerCase().endsWith("." + ext))
                            return child;
        }

        throw new FileNotFoundException(
                "Could not find the package `" + pack + "` file in site `" + getName() + "`.");
    }

    public String readResource(String pack) {
        try {
            return readResourceWithException(pack);
        } catch (EvalFactoryException e) {
            return "";
        }
    }

    public String readResourceWithException(String pack) throws EvalFactoryException {
        EvalMetaData codeMeta = new EvalMetaData();

        try {
            File file = getResourceWithException(pack);

            codeMeta.shell = "text";// FileInterpreter.determineShellFromName( file.getName() );
            codeMeta.fileName = file.getAbsolutePath();

            return factory.eval(file, this).getString();
        } catch (IOException e) {
            throw new EvalFactoryException(e, factory.getShellFactory());
        }
    }

    public String getSiteId() {
        return siteId;
    }

    public String getTitle() {
        return title;
    }

    public String getDomain() {
        return domain;
    }

    public File getFile() {
        return filePath;
    }

    public void addToCachePatterns(String pattern) {
        if (!cachePatterns.contains(pattern.toLowerCase()))
            cachePatterns.add(pattern.toLowerCase());
    }

    public List<String> getCachePatterns() {
        return cachePatterns;
    }

    public Routes getRoutes() {
        if (routes == null)
            routes = new Routes(this);

        return routes;
    }

    /**
     * TODO Make it so site config can change the location the the temp directory.
     * 
     * @return The temp directory for this site.
     */
    public File getTempFileDirectory() {
        File tmpFileDirectory = new File(Loader.getTempFileDirectory(), getSiteId());

        if (!tmpFileDirectory.exists())
            tmpFileDirectory.mkdirs();

        if (!tmpFileDirectory.isDirectory())
            SiteManager.getLogger().severe(
                    "The temp directory specified in the server configs is not a directory, File Uploads will FAIL until this problem is resolved.");

        if (!tmpFileDirectory.canWrite())
            SiteManager.getLogger().severe(
                    "The temp directory specified in the server configs is not writable, File Uploads will FAIL until this problem is resolved.");

        return tmpFileDirectory;
    }

    @Override
    public String toString() {
        return getSiteId() + "(Name:" + getName() + ",Title:" + title + ",Domain:" + getDomain() + ",SiteType:"
                + siteType + ",SourceDir:" + source + ")";
    }
}