org.openbravo.service.system.SystemService.java Source code

Java tutorial

Introduction

Here is the source code for org.openbravo.service.system.SystemService.java

Source

/*
 *************************************************************************
 * The contents of this file are subject to the Openbravo  Public  License
 * Version  1.1  (the  "License"),  being   the  Mozilla   Public  License
 * Version 1.1  with a permitted attribution clause; you may not  use this
 * file except in compliance with the License. You  may  obtain  a copy of
 * the License at http://www.openbravo.com/legal/license.html 
 * Software distributed under the License  is  distributed  on  an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific  language  governing  rights  and  limitations
 * under the License. 
 * The Original Code is Openbravo ERP. 
 * The Initial Developer of the Original Code is Openbravo SLU 
 * All portions are Copyright (C) 2009-2014 Openbravo SLU 
 * All Rights Reserved. 
 * Contributor(s):  ______________________________________.
 ************************************************************************
 */

package org.openbravo.service.system;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.Vector;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.ddlutils.Platform;
import org.apache.ddlutils.PlatformFactory;
import org.apache.ddlutils.model.Database;
import org.apache.ddlutils.platform.ExcludeFilter;
import org.apache.log4j.Logger;
import org.hibernate.criterion.Restrictions;
import org.openbravo.base.model.Entity;
import org.openbravo.base.model.ModelProvider;
import org.openbravo.base.model.Property;
import org.openbravo.base.provider.OBProvider;
import org.openbravo.base.provider.OBSingleton;
import org.openbravo.base.session.OBPropertiesProvider;
import org.openbravo.base.structure.BaseOBObject;
import org.openbravo.dal.core.DalUtil;
import org.openbravo.dal.core.OBContext;
import org.openbravo.dal.core.SessionHandler;
import org.openbravo.dal.core.TriggerHandler;
import org.openbravo.dal.service.OBCriteria;
import org.openbravo.dal.service.OBDal;
import org.openbravo.ddlutils.task.DatabaseUtils;
import org.openbravo.ddlutils.util.DBSMOBUtil;
import org.openbravo.model.ad.module.Module;
import org.openbravo.model.ad.system.Client;
import org.openbravo.model.ad.system.ClientInformation;
import org.openbravo.model.common.enterprise.Organization;
import org.openbravo.scheduling.OBScheduler;
import org.openbravo.service.system.SystemValidationResult.SystemValidationType;
import org.quartz.SchedulerException;

/**
 * Provides utility like services.
 * 
 * @author Martin Taal
 */
public class SystemService implements OBSingleton {
    private static SystemService instance;
    private static final Logger log4j = Logger.getLogger(SystemService.class);

    public static synchronized SystemService getInstance() {
        if (instance == null) {
            instance = OBProvider.getInstance().get(SystemService.class);
        }
        return instance;
    }

    public static synchronized void setInstance(SystemService instance) {
        SystemService.instance = instance;
    }

    /**
     * Returns true if for a certain class there are objects which have changed.
     * 
     * @param clzs
     *          the type of objects which are checked
     * @param afterDate
     *          the timestamp to check
     * @return true if there is an object in the database which changed since afterDate, false
     *         otherwise
     */
    public boolean hasChanged(Class<?>[] clzs, Date afterDate) {
        for (Class<?> clz : clzs) {
            @SuppressWarnings("unchecked")
            final OBCriteria<?> obc = OBDal.getInstance().createCriteria((Class<BaseOBObject>) clz);
            obc.add(Restrictions.gt(Organization.PROPERTY_UPDATED, afterDate));
            // todo: count is slower than exists, is exists possible?
            if (obc.count() > 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * Validates a specific module, checks the javapackage, dependency on core etc. The database
     * changes of the module are not checked. This is a separate task.
     * 
     * @param module
     *          the module to validate
     * @param database
     *          the database to read the dbschema from
     * @return the validation result
     */
    public SystemValidationResult validateModule(Module module, Database database) {
        final ModuleValidator moduleValidator = new ModuleValidator();
        moduleValidator.setValidateModule(module);
        return moduleValidator.validate();
    }

    /**
     * Validates the database for a specific module.
     * 
     * @param module
     *          the module to validate
     * @param database
     *          the database to read the dbschema from
     * @return the validation result
     */
    public SystemValidationResult validateDatabase(Module module, Database database) {
        final DatabaseValidator databaseValidator = new DatabaseValidator();
        databaseValidator.setValidateModule(module);
        databaseValidator.setDatabase(database);
        databaseValidator.setDbsmExecution(true);
        return databaseValidator.validate();
    }

    /**
     * Prints the validation result grouped by validation type to the log.
     * 
     * @param log
     *          the log to which the validation result is printed
     * @param result
     *          the validation result containing both errors and warning
     * @return the errors are returned as a string
     */
    public String logValidationResult(Logger log, SystemValidationResult result) {
        for (SystemValidationType validationType : result.getWarnings().keySet()) {
            log.warn("\n");
            log.warn("+++++++++++++++++++++++++++++++++++++++++++++++++++");
            log.warn("Warnings for Validation type: " + validationType);
            log.warn("+++++++++++++++++++++++++++++++++++++++++++++++++++");
            final List<String> warnings = result.getWarnings().get(validationType);
            for (String warning : warnings) {
                log.warn(warning);
            }
        }

        final StringBuilder sb = new StringBuilder();
        for (SystemValidationType validationType : result.getErrors().keySet()) {
            sb.append("\n");
            sb.append("\n+++++++++++++++++++++++++++++++++++++++++++++++++++");
            sb.append("\nErrors for Validation type: " + validationType);
            sb.append("\n+++++++++++++++++++++++++++++++++++++++++++++++++++");
            final List<String> errors = result.getErrors().get(validationType);
            for (String err : errors) {
                sb.append("\n");
                sb.append(err);
            }
        }
        log.error(sb.toString());
        return sb.toString();
    }

    /**
     * Removes all data of a specific {@link Client}, the client is identified by the clientId
     * parameter.
     * 
     * NOTE: this method does not work yet. It is an initial implementation and not yet complete
     * 
     * @param clientId
     *          the id of the client to delete.
     * @deprecated Do not use, is a work in progress
     */
    public void removeAllClientData(String clientId) {
        // the idea was/is the following:
        // 0) compute the order of all entities based on their reference, something like the
        // low-level code in BOM computations: the entity nobody refers to has number 0, the
        // rule is that if there are two entities A and B and there is a reference path from A
        // to B (directly or through other entities) using only non-mandatory many-to-one references
        // then: A.referenceNumber < B.referenceNumber
        // Then the entities can be sorted ascending on the referenceNumber
        // the procedure is then:
        // 1) nullify all non-mandatory many-to-ones
        // 2) then remove the objects in order of the entity.referenceNumber
        // currently this does not work yet because step 1 fails because there are constraints
        // defined in the database which means that certain fields are conditionally mandatory.

        OBContext.setAdminMode();
        try {
            TriggerHandler.getInstance().disable();
            final Client client = OBDal.getInstance().get(Client.class, clientId);
            for (Entity e : ModelProvider.getInstance().getModel()) {
                if (!e.isClientEnabled()) {
                    continue;
                }
                nullifyManyToOnes(e, client);
            }
            OBDal.getInstance().flush();

            for (Entity e : ModelProvider.getInstance().getModel()) {
                if (!e.isClientEnabled()) {
                    continue;
                }
                final String hql;
                if (e.getName().equals(ClientInformation.ENTITY_NAME)) {
                    hql = "delete " + e.getName() + " where id=:clientId";
                } else {
                    hql = "delete " + e.getName() + " where client=:clientId";
                }
                SessionHandler.getInstance().getSession().createQuery(hql).setString("clientId", clientId)
                        .executeUpdate();
            }
            OBDal.getInstance().flush();
            TriggerHandler.getInstance().enable();
            OBDal.getInstance().commitAndClose();
        } finally {
            OBContext.restorePreviousMode();
        }
    }

    private void nullifyManyToOnes(Entity e, Client client) {
        final String updatePart = createNullifyNonMandatoryQuery(e);
        if (updatePart == null) {
            return;
        }
        final String hql;
        if (e.getName().equals(ClientInformation.ENTITY_NAME)) {
            hql = updatePart + " where id=:clientId";
        } else {
            hql = updatePart + " where client=:clientId";
        }
        try {
            SessionHandler.getInstance().getSession().createQuery(hql).setString("clientId", client.getId())
                    .executeUpdate();
        } catch (IllegalArgumentException ex) {
            // handle a special case, that the entity name or a property name
            // is a reserved hql word.
            if (ex.getMessage().indexOf("node to traverse cannot be null") != -1) {
                // in this case use an inefficient method
                nullifyPerObject(e, client);
            } else {
                throw ex;
            }
        }
    }

    private String createNullifyNonMandatoryQuery(Entity e) {
        final StringBuilder sb = new StringBuilder("update " + e.getClassName() + " e set ");
        boolean doNullifyProperty = false;
        for (Property p : e.getProperties()) {
            if (!p.isPrimitive() && !p.isOneToMany() && !p.isMandatory()) {
                if (doNullifyProperty) {
                    sb.append(", ");
                }
                sb.append("e." + p.getName() + " = null");
                doNullifyProperty = true;
            }
        }
        // no property found, don't do update
        if (!doNullifyProperty) {
            return null;
        }
        return sb.toString();
    }

    private void nullifyPerObject(Entity e, Client client) {
        final OBCriteria<BaseOBObject> obc = OBDal.getInstance().createCriteria(e.getName());
        obc.setFilterOnActive(false);
        obc.setFilterOnReadableClients(false);
        obc.setFilterOnReadableOrganization(false);
        obc.add(Restrictions.eq(Organization.PROPERTY_CLIENT, client));
        for (BaseOBObject bob : obc.list()) {
            for (Property p : e.getProperties()) {
                if (!p.isPrimitive() && !p.isOneToMany() && !p.isMandatory()) {
                    bob.set(p.getName(), null);
                }
            }
        }
    }

    /**
     * This process deletes a client from the database. During its execution, the Scheduler is
     * stopped, and all sessions active for other users are cancelled
     * 
     * @param client
     *          The client to be deleted
     */
    public void deleteClient(Client client) {
        try {
            long t1 = System.currentTimeMillis();
            Platform platform = getPlatform();
            Connection con = OBDal.getInstance().getConnection();
            killConnectionsAndSafeMode(con);
            try {
                if (OBScheduler.getInstance() != null && OBScheduler.getInstance().getScheduler() != null
                        && OBScheduler.getInstance().getScheduler().isStarted())
                    OBScheduler.getInstance().getScheduler().standby();
            } catch (Exception e) {
                throw new RuntimeException("Could not shutdown scheduler", e);
            }
            OBDal.getInstance().getConnection().commit();
            disableConstraints(platform);
            OBContext.setAdminMode(false);
            OBDal.getInstance().flush();
            OBDal.getInstance().getConnection().commit();
            String clientId = (String) DalUtil.getId(client);

            List<String> sqlCommands = new ArrayList<String>();

            List<Entity> entities = ModelProvider.getInstance().getModel();
            for (Entity entity : entities) {
                if ((entity.isClientEnabled() || entity.getName().equals("ADClient")) && !entity.isView()
                        && !entity.isDataSourceBased() && !entity.isHQLBased() && !entity.isVirtualEntity()) {
                    final String sql = "delete from " + entity.getTableName() + " where ad_client_id=?";
                    sqlCommands.add(sql);
                }
            }
            for (String command : sqlCommands) {
                PreparedStatement ps = null;
                try {
                    ps = con.prepareStatement(command);
                    ps.setString(1, clientId);
                    ps.executeUpdate();
                } finally {
                    if (ps != null && !ps.isClosed()) {
                        ps.close();
                    }
                }
            }
            PreparedStatement stpref = null;
            try {
                stpref = con.prepareStatement("DELETE FROM ad_preference p where visibleat_client_id=?");
                stpref.setString(1, clientId);
                stpref.executeUpdate();
            } finally {
                if (stpref != null && !stpref.isClosed()) {
                    stpref.close();
                }
            }
            PreparedStatement stpers = null;
            try {
                stpers = con
                        .prepareStatement("DELETE FROM obuiapp_uipersonalization p where visibleat_client_id=?");
                stpers.setString(1, clientId);
                stpers.executeUpdate();
            } finally {
                if (stpers != null && !stpers.isClosed()) {
                    stpers.close();
                }
            }
            con.commit();
            OBDal.getInstance().commitAndClose();
            enableConstraints(platform);
            Connection con2 = platform.borrowConnection();
            try {
                resetSafeMode(con2);
            } finally {
                platform.returnConnection(con2);
            }
            log4j.info("Deletion of client " + clientId + " took " + (System.currentTimeMillis() - t1)
                    + " miliseconds");
        } catch (Exception e) {
            log4j.error("exception when deleting the client: ", e);
        } finally {
            OBContext.restorePreviousMode();
            // We restart the scheduler
            try {
                if (OBScheduler.getInstance() != null && OBScheduler.getInstance().getScheduler() != null) {
                    OBScheduler.getInstance().getScheduler().start();
                }
            } catch (SchedulerException e) {
                log4j.error("There was an error while restarting the scheduler", e);
            }
        }
    }

    private void resetSafeMode(Connection con) {

        try {
            PreparedStatement ps2 = null;
            try {
                ps2 = con.prepareStatement("UPDATE AD_SYSTEM_INFO SET SYSTEM_STATUS='RB70'");
                ps2.executeUpdate();
            } finally {
                if (ps2 != null && !ps2.isClosed()) {
                    ps2.close();
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Couldn't reset the safe mode", e);
        }
    }

    private void killConnectionsAndSafeMode(Connection con) {
        try {
            PreparedStatement updateSession = null;
            try {
                updateSession = con.prepareStatement("UPDATE AD_SESSION SET SESSION_ACTIVE='N' WHERE CREATEDBY<>?");
                updateSession.setString(1, OBContext.getOBContext().getUser().getId());
                updateSession.executeUpdate();
            } finally {
                if (updateSession != null && !updateSession.isClosed()) {
                    updateSession.close();
                }
            }
            PreparedStatement ps2 = null;
            try {
                ps2 = con.prepareStatement("UPDATE AD_SYSTEM_INFO SET SYSTEM_STATUS='RB80'");
                ps2.executeUpdate();
            } finally {
                if (ps2 != null && !ps2.isClosed()) {
                    ps2.close();
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Couldn't destroy concurrent sessions", e);
        }
    }

    /**
     * Returns a dbsourcemanager Platform object
     * 
     * @return A Platform object built following the configuration set in the Openbravo.properties
     *         file
     */
    public Platform getPlatform() {
        Properties obProp = OBPropertiesProvider.getInstance().getOpenbravoProperties();
        // We disable check constraints before inserting reference data
        String driver = obProp.getProperty("bbdd.driver");
        String url = obProp.getProperty("bbdd.rdbms").equals("POSTGRE")
                ? obProp.getProperty("bbdd.url") + "/" + obProp.getProperty("bbdd.sid")
                : obProp.getProperty("bbdd.url");
        String user = obProp.getProperty("bbdd.user");
        String password = obProp.getProperty("bbdd.password");
        BasicDataSource datasource = DBSMOBUtil.getDataSource(driver, url, user, password);
        Platform platform = PlatformFactory.createNewPlatformInstance(datasource);
        return platform;
    }

    private void disableConstraints(Platform platform) throws FileNotFoundException, IOException {
        log4j.info("Disabling constraints...");
        ExcludeFilter excludeFilter = DBSMOBUtil.getInstance().getExcludeFilter(
                new File(OBPropertiesProvider.getInstance().getOpenbravoProperties().getProperty("source.path")));
        Database xmlModel = platform.loadModelFromDatabase(excludeFilter);
        Connection con = null;
        try {
            con = platform.borrowConnection();
            log4j.info("   Disabling foreign keys");
            platform.disableAllFK(con, xmlModel, false);
            log4j.info("   Disabling triggers");
            platform.disableAllTriggers(con, xmlModel, false);
            log4j.info("   Disabling check constraints");
            platform.disableCheckConstraints(con, xmlModel, null);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (con != null) {
                platform.returnConnection(con);
            }
        }
    }

    private void enableConstraints(Platform platform) {
        Properties obProp = OBPropertiesProvider.getInstance().getOpenbravoProperties();
        String obDir = obProp.getProperty("source.path");

        Vector<File> dirs = new Vector<File>();
        dirs.add(new File(obDir, "/src-db/database/model/"));
        File modules = new File(obDir, "/modules");

        for (int j = 0; j < modules.listFiles().length; j++) {
            final File dirF = new File(modules.listFiles()[j], "/src-db/database/model/");
            if (dirF.exists()) {
                dirs.add(dirF);
            }
        }
        File[] fileArray = new File[dirs.size()];
        for (int i = 0; i < dirs.size(); i++) {
            fileArray[i] = dirs.get(i);
        }
        Database xmlModel = DatabaseUtils.readDatabase(fileArray);
        platform.deleteAllInvalidConstraintRows(xmlModel, false);
        log4j.info("Enabling constraints...");
        Connection con = null;
        try {
            con = platform.borrowConnection();
            log4j.info("   Enabling check constraints");
            platform.enableCheckConstraints(con, xmlModel, null);
            log4j.info("   Enabling triggers");
            platform.enableAllTriggers(con, xmlModel, false);
            log4j.info("   Enabling foreign keys");
            platform.enableAllFK(con, xmlModel, false);
        } finally {
            if (con != null) {
                platform.returnConnection(con);
            }
        }
    }
}