com.freedomotic.environment.impl.EnvironmentRepositoryImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.freedomotic.environment.impl.EnvironmentRepositoryImpl.java

Source

/**
 *
 * Copyright (c) 2009-2016 Freedomotic team http://freedomotic.com
 *
 * This file is part of Freedomotic
 *
 * This Program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2, or (at your option) any later version.
 *
 * This Program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * Freedomotic; see the file COPYING. If not, see
 * <http://www.gnu.org/licenses/>.
 */
package com.freedomotic.environment.impl;

import com.freedomotic.app.Freedomotic;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import org.apache.commons.io.FileUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.freedomotic.environment.EnvironmentLogic;
import com.freedomotic.environment.EnvironmentRepository;
import com.freedomotic.exceptions.RepositoryException;
import com.freedomotic.model.environment.Environment;
import com.freedomotic.model.environment.Zone;
import com.freedomotic.persistence.FreedomXStream;
import com.freedomotic.persistence.XmlPreprocessor;
import com.freedomotic.settings.AppConfig;
import com.freedomotic.settings.Info;
import com.freedomotic.things.EnvObjectLogic;
import com.freedomotic.things.ThingRepository;
import com.freedomotic.util.SerialClone;
import com.freedomotic.util.UidGenerator;
import com.google.inject.Inject;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.XStreamException;

/**
 * Repository to manage the {@link Environment} loaded from filesystem
 *
 * @author Enrico Nicoletti
 */
class EnvironmentRepositoryImpl implements EnvironmentRepository {

    private static final Logger LOG = LoggerFactory.getLogger(EnvironmentRepositoryImpl.class.getName());

    // Dependencies
    private final AppConfig appConfig;
    private final EnvironmentPersistenceFactory environmentPersistenceFactory;
    private final ThingRepository thingsRepository;
    private File defaultEnvironmentFile;
    private static final String ENVIRONMENT_FILE_EXTENSION = ".xenv";

    // The Environments cache
    private static final List<EnvironmentLogic> environments = new ArrayList<>();

    /**
     * Creates a new repository
     *
     * @param appConfig needed to get the default environment
     * @param environmentPersistenceFactory creates the right environment loader
     * to manage the environment persistence
     * @throws com.freedomotic.exceptions.RepositoryException
     */
    @Inject
    EnvironmentRepositoryImpl(AppConfig appConfig, ThingRepository thingsRepository,
            EnvironmentPersistenceFactory environmentPersistenceFactory) throws RepositoryException {
        this.appConfig = appConfig;
        this.thingsRepository = thingsRepository;
        this.environmentPersistenceFactory = environmentPersistenceFactory;
    }

    @Override
    public void initFromDefaultFolder() throws RepositoryException {

        String envFilePath = appConfig.getProperty("KEY_ROOM_XML_PATH");
        this.defaultEnvironmentFile = new File(Info.PATHS.PATH_ENVIRONMENTS_FOLDER + File.separator + envFilePath);

        File defaultEnvironmentFolder = getDefaultEnvironmentFolder();
        this.init(defaultEnvironmentFolder);
    }

    /**
     * Reads environment data from filesystem using the
     * {@link EnvironmentPersistence} class
     *
     * @throws RepositoryException
     */
    @Override
    public synchronized void init(File folder) throws RepositoryException {

        EnvironmentPersistence environmentPersistence = environmentPersistenceFactory.create(folder);
        Collection<Environment> loadedPojo = environmentPersistence.loadAll();
        this.deleteAll();
        for (Environment env : loadedPojo) {
            EnvironmentLogic envLogic = new EnvironmentLogic();
            envLogic.setPojo(env);
            envLogic.setSource(new File(folder + "/" + env.getUUID() + ENVIRONMENT_FILE_EXTENSION));
            // activate this environment
            this.create(envLogic);
        }

        List<EnvObjectLogic> loadedThings = thingsRepository.loadAll(findAll().get(0).getObjectFolder());
        for (EnvObjectLogic thing : loadedThings) {
            // stores the thing in repository. Important, otherwise it will be not visible in the environment
            thingsRepository.create(thing);
        }
    }

    /**
     *
     *
     * @return @throws RepositoryException
     */
    private File getDefaultEnvironmentFolder() throws RepositoryException {

        File folder = this.defaultEnvironmentFile.getParentFile();

        if (folder == null) {
            throw new RepositoryException(
                    "Application configuration does not specify the default environment to load."
                            + " A 'KEY_ROOM_XML_PATH' property is expected");
        }

        if (!folder.exists()) {
            throw new RepositoryException("Folder \"" + folder + "\" doesn't exist. Cannot load default "
                    + "environment from \"" + this.defaultEnvironmentFile.getAbsolutePath() + "\"");
        } else if (!folder.isDirectory()) {
            throw new RepositoryException(
                    "Environment folder \"" + folder.getAbsolutePath() + "\" is supposed to be a directory");
        }
        return folder;
    }

    /**
     *
     * @param folder
     * @throws RepositoryException
     */
    @RequiresPermissions("environments:save")
    @Override
    public void saveEnvironmentsToFolder(File folder) throws RepositoryException {

        if (environments.isEmpty()) {
            LOG.warn("There is no environment to persist. Folder \"{}\" will not be altered",
                    folder.getAbsolutePath());
            return;
        }
        if (folder.exists() && !folder.isDirectory()) {
            throw new RepositoryException(
                    "\"" + folder.getAbsoluteFile() + "\" is not a valid environment folder. Skipped");
        }
        try {
            for (EnvironmentLogic environment : environments) {
                String uuid = environment.getPojo().getUUID();

                if ((uuid == null) || uuid.isEmpty()) {
                    environment.getPojo().setUUID(UUID.randomUUID().toString());
                }

                String fileName = environment.getPojo().getUUID() + ENVIRONMENT_FILE_EXTENSION;
                save(environment, new File(folder + "/" + fileName));
            }
        } catch (IOException e) {
            throw new RepositoryException(e.getCause());
        }

        // save environment's things
        if (appConfig.getBooleanProperty("KEY_OVERRIDE_OBJECTS_ON_EXIT", false)) {
            try {
                thingsRepository.saveAll(findAll().get(0).getObjectFolder());
            } catch (RepositoryException ex) {
                LOG.error("Cannot save objects into \"{}\"", findAll().get(0).getObjectFolder().getAbsolutePath(),
                        Freedomotic.getStackTraceInfo(ex));
            }
        }

    }

    /**
     *
     *
     * @param folder
     * @throws RepositoryException
     */
    private static void deleteEnvFiles(File folder) throws RepositoryException {
        if ((folder == null) || !folder.isDirectory()) {
            throw new IllegalArgumentException("Unable to delete environment files in a null or not valid folder");
        }

        // this filter only returns thing files
        FileFilter objectFileFileter = new FileFilter() {
            @Override
            public boolean accept(File file) {
                if (file.isFile() && file.getName().endsWith(ENVIRONMENT_FILE_EXTENSION)) {
                    return true;
                } else {
                    return false;
                }
            }
        };

        File[] files = folder.listFiles(objectFileFileter);

        for (File file : files) {
            boolean deleted = file.delete();

            if (!deleted) {
                throw new RepositoryException("Unable to delete file " + file.getAbsoluteFile());
            }
        }
    }

    /**
     * Loads all objects file filesystem folder and adds the objects to the
     * list.
     *
     * @param folder
     * @param makeUnique
     * @return
     * @throws com.freedomotic.exceptions.RepositoryException
     * @deprecated
     */
    @Deprecated
    private boolean loadEnvironmentsFromDir(File folder, boolean makeUnique) throws RepositoryException {
        if (folder == null) {
            throw new RepositoryException("Cannot load environments from a null folder");
        }
        environments.clear();

        // This filter only returns env files
        FileFilter envFileFilter = new FileFilter() {
            @Override
            public boolean accept(File file) {
                return file.isFile() && file.getName().endsWith(ENVIRONMENT_FILE_EXTENSION);
            }
        };

        File[] files = folder.listFiles(envFileFilter);

        for (File file : files) {
            try {
                EnvironmentLogic envLogic = loadEnvironmentFromFile(file);
                if (envLogic != null) {
                    add(envLogic, false);
                }
            } catch (RepositoryException re) {
                LOG.error("Cannot add environment from file \"{}\"", file.getAbsolutePath(),
                        Freedomotic.getStackTraceInfo(re));
            }
        }

        // Load all objects in this environment
        thingsRepository.loadAll(EnvironmentRepositoryImpl.getEnvironments().get(0).getObjectFolder());

        // TODO: this return value makes no sense
        return true;
    }

    /**
     * Adds an environment. You can use EnvObjectPersistance.MAKE_UNIQUE to
     * create an object that will surely be unique. Beware this means it is
     * created with defensive copy of the object in input and name, protocol,
     * address and UUID are reset to a default value.
     *
     * @param obj the environment to add
     * @param MAKE_UNIQUE can be true or false. Creates a defensive copy
     * reference to the object in input.
     * @return a pointer to the newly created environment object
     */
    @RequiresPermissions("environments:create")
    @Deprecated
    private static EnvironmentLogic add(final EnvironmentLogic obj, boolean MAKE_UNIQUE) {
        if ((obj == null) || (obj.getPojo() == null) || (obj.getPojo().getName() == null)
                || obj.getPojo().getName().isEmpty()) {
            throw new IllegalArgumentException("This is not a valid environment");
        }

        EnvironmentLogic envLogic = obj;

        if (MAKE_UNIQUE) {

            envLogic = new EnvironmentLogic();

            //defensive copy to not affect the passed object with the changes
            Environment pojoCopy = SerialClone.clone(obj.getPojo());
            pojoCopy.setName(obj.getPojo().getName() + "-" + UidGenerator.getNextStringUid());
            pojoCopy.setUUID(""); // force to assign a new random and unique UUID
            // force to assign a new random and unique UUID to every zone
            for (Zone z : pojoCopy.getZones()) {
                z.setUuid(UUID.randomUUID().toString());
            }

            //should be the last called after using setters on envLogic.getPojo()
            envLogic.setPojo(pojoCopy);
        }

        envLogic.init();
        environments.add(envLogic);
        return envLogic;
    }

    /**
     *
     * @param input
     */
    @RequiresPermissions("environments:delete")
    @Deprecated
    private void remove(EnvironmentLogic input) {
        for (EnvObjectLogic obj : thingsRepository.findByEnvironment(input)) {
            thingsRepository.delete(obj);
        }

        environments.remove(input);
        this.removeEnvironmentFile(input.getSource());
        input.clear();
    }

    private boolean removeEnvironmentFile(File environmentFile) {
        if (environmentFile == null) {
            LOG.warn("No file defined in this environment, so no deletion occurs.");
            return false;
        }

        if (environmentFile.exists()) {
            try {
                FileUtils.forceDelete(environmentFile);
                return true;
            } catch (IOException e) {
                LOG.warn("Error while removing file \"{}\", please try manually.",
                        environmentFile.getAbsolutePath(), Freedomotic.getStackTraceInfo(e));
                return false;
            }
        } else {
            LOG.warn("File \"{}\" does not exist", environmentFile.getAbsolutePath());
            return false;
        }
    }

    /**
     *
     */
    @RequiresPermissions("environments:delete")
    @Override
    public void deleteAll() {
        try {
            for (EnvironmentLogic el : environments) {
                delete(el);
            }
        } catch (Exception e) {
            LOG.error(Freedomotic.getStackTraceInfo(e));
        } finally {
            environments.clear();
        }
    }

    /**
     * Saves an environment to file.
     *
     * @param env environment to save
     * @param file file to save the environment
     * @throws IOException
     */
    private void save(EnvironmentLogic env, File file) throws IOException {
        try {
            EnvironmentPersistence environmentPersistence = environmentPersistenceFactory
                    .create(file.getParentFile());
            environmentPersistence.persist(env.getPojo());
        } catch (RepositoryException ex) {
            LOG.error(Freedomotic.getStackTraceInfo(ex));
        }

    }

    /**
     *
     * @param env
     * @param folder
     * @throws IOException
     */
    @RequiresPermissions("environments:save")
    @Override
    public void saveAs(EnvironmentLogic env, File folder) throws IOException {
        LOG.info("Serializing new environment to \"{}\"", folder);
        save(env, new File(folder + "/" + env.getPojo().getUUID() + ENVIRONMENT_FILE_EXTENSION));
    }

    /**
     *
     * @param file
     * @throws RepositoryException
     * @deprecated
     */
    @Deprecated
    @Override
    public EnvironmentLogic loadEnvironmentFromFile(final File file) throws RepositoryException {
        LOG.info("Loading environment from file \"{}\"", file.getAbsolutePath());
        XStream xstream = FreedomXStream.getXstream();

        //validate the object against a predefined DTD
        String xml;

        try {
            xml = XmlPreprocessor.validate(file, Info.PATHS.PATH_CONFIG_FOLDER + "/validator/environment.dtd");
        } catch (IOException ex) {
            throw new RepositoryException(ex.getMessage(), ex);
        }

        Environment pojo = null;

        try {
            pojo = (Environment) xstream.fromXML(xml);
        } catch (XStreamException e) {
            throw new RepositoryException("XML parsing error. Readed XML is \n" + xml, e);
        }

        EnvironmentLogic envLogic = new EnvironmentLogic();

        if (pojo == null) {
            throw new IllegalStateException("Object data cannot be null at this stage");
        }

        envLogic.setPojo(pojo);
        envLogic.setSource(file);
        LOG.info("Environment \"" + envLogic.getPojo().getName() + "\" loaded");
        return envLogic;
    }

    /**
     *
     * @return
     */
    @Deprecated
    @RequiresPermissions("environments:read")
    private static List<EnvironmentLogic> getEnvironments() {
        return environments;
    }

    /**
     *
     * @param UUID
     * @return
     */
    @RequiresPermissions("environments:read")
    @Deprecated
    private static EnvironmentLogic getEnvByUUID(String UUID) {
        //     if (auth.isPermitted("environments:read:" + UUID)) {
        for (EnvironmentLogic env : environments) {
            if (env.getPojo().getUUID().equals(UUID)) {
                return env;
            }
        }
        //   }
        return null;
    }

    @Override
    @RequiresPermissions("environments:read")
    public List<EnvironmentLogic> findAll() {
        return getEnvironments();
    }

    @Override
    @RequiresPermissions("environments:read")
    public List<EnvironmentLogic> findByName(String name) {
        List<EnvironmentLogic> el = new ArrayList<>();
        for (EnvironmentLogic e : findAll()) {
            if (e.getPojo().getName().equalsIgnoreCase(name)) {
                el.add(e);
            }
        }
        return el;
    }

    @Override
    @RequiresPermissions("environments:read")
    public EnvironmentLogic findOne(String uuid) {
        return getEnvByUUID(uuid);
    }

    @Override
    @RequiresPermissions("environments:create")
    public boolean create(EnvironmentLogic item) {
        try {
            add(item, false);
            return true;
        } catch (Exception e) {
            LOG.error(Freedomotic.getStackTraceInfo(e));
            return false;
        }
    }

    @Override
    @RequiresPermissions("environments:delete")
    public boolean delete(EnvironmentLogic item) {

        if (environments.isEmpty()) {
            LOG.warn("There are no environments to delete");
            return false;
        }

        if (item.getSource() != null && environments.contains(item) && this.defaultEnvironmentFile.getAbsolutePath()
                .equalsIgnoreCase(item.getSource().getAbsolutePath())) {
            LOG.info("Default environment cannot be deleted \"{}\"", item.getSource().getAbsolutePath());
            return false;
        }

        try {
            remove(item);
            return true;
        } catch (Exception e) {
            LOG.error(Freedomotic.getStackTraceInfo(e));
            return false;
        }
    }

    @Override
    @RequiresPermissions("environments:delete")
    public boolean delete(String uuid) {
        return delete(findOne(uuid));
    }

    @Override
    @RequiresPermissions("environments:update")
    public EnvironmentLogic modify(String uuid, EnvironmentLogic data) {
        try {
            delete(uuid);
            data.getPojo().setUUID(uuid);
            create(data);
            return data;
        } catch (Exception e) {
            LOG.error(Freedomotic.getStackTraceInfo(e));
            return null;
        }

    }

    @Override
    @RequiresPermissions("environments:create")
    public EnvironmentLogic copy(EnvironmentLogic env) {
        return add(env, true);
    }

}