org.nuclos.client.console.NuclosConsole.java Source code

Java tutorial

Introduction

Here is the source code for org.nuclos.client.console.NuclosConsole.java

Source

//Copyright (C) 2010  Novabit Informationssysteme GmbH
//
//This file is part of Nuclos.
//
//Nuclos is free software: you can redistribute it and/or modify
//it under the terms of the GNU Affero General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//Nuclos 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 Affero General Public License for more details.
//
//You should have received a copy of the GNU Affero General Public License
//along with Nuclos.  If not, see <http://www.gnu.org/licenses/>.
package org.nuclos.client.console;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.rmi.RemoteException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import javax.security.auth.login.LoginException;

import org.apache.commons.lang.NullArgumentException;
import org.nuclos.client.attribute.AttributeCache;
import org.nuclos.client.attribute.AttributeDelegate;
import org.nuclos.client.genericobject.GenericObjectMetaDataCache;
import org.nuclos.client.layout.LayoutDelegate;
import org.nuclos.client.masterdata.MasterDataDelegate;
import org.nuclos.client.nuclet.generator.NucletGenerator;
import org.nuclos.client.report.ReportDelegate;
import org.nuclos.client.rule.RuleCache;
import org.nuclos.client.rule.RuleDelegate;
import org.nuclos.client.rule.TimelimitRuleDelegate;
import org.nuclos.client.security.NuclosRemoteServerSession;
import org.nuclos.client.updatejobs.MigrateSearchFilterPreferences;
import org.nuclos.common.ApplicationProperties;
import org.nuclos.common.ConsoleConstants;
import org.nuclos.common.NuclosEntity;
import org.nuclos.common.NuclosFatalException;
import org.nuclos.common.Priority;
import org.nuclos.common.collection.CollectionUtils;
import org.nuclos.common.dal.DalSupportForMD;
import org.nuclos.common.dal.vo.EntityObjectVO;
import org.nuclos.common2.DateUtils;
import org.nuclos.common2.IOUtils;
import org.nuclos.common2.LangUtils;
import org.nuclos.common2.exception.CommonBusinessException;
import org.nuclos.common2.exception.CommonFatalException;
import org.nuclos.common2.exception.CommonFinderException;
import org.nuclos.common2.exception.CommonPermissionException;
import org.nuclos.common2.exception.CommonRemoteException;
import org.nuclos.server.attribute.valueobject.LayoutVO;
import org.nuclos.server.common.ejb3.PreferencesFacadeRemote;
import org.nuclos.server.common.valueobject.PreferencesVO;
import org.nuclos.server.console.ejb3.ConsoleFacadeRemote;
import org.nuclos.server.masterdata.ejb3.MasterDataFacadeRemote;
import org.nuclos.server.masterdata.valueobject.MasterDataVO;
import org.nuclos.server.report.ejb3.DatasourceFacadeRemote;
import org.nuclos.server.report.ejb3.SchedulerControlFacadeRemote;
import org.nuclos.server.report.valueobject.ReportVO;
import org.nuclos.server.report.valueobject.ReportVO.ReportType;
import org.nuclos.server.ruleengine.ejb3.TimelimitRuleFacadeRemote;
import org.nuclos.server.ruleengine.valueobject.RuleEventUsageVO;
import org.nuclos.server.ruleengine.valueobject.RuleVO;
import org.nuclos.server.ruleengine.valueobject.RuleWithUsagesVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

/**
 * Management console for Nucleus.
 * @todo the JNDI URL is read from jndi.properties currently. It should be given on the command line instead.
 * <br>
 * <br>Created by Novabit Informationssysteme GmbH <br>Please visit <a href="http://www.novabit.de">www.novabit.de</a>
 *
 * @author   <a href="mailto:Boris.Sander@novabit.de">Boris Sander</a>
 * @version 01.00.00
 */
public class NuclosConsole extends ConsoleConstants {

    /**
     * this list is used by NuclosConsoleGui to show the available commands
     */
    public static final List<String> LSTCOMMANDS = Arrays.asList(CMD_INVALIDATE_ATTRIBUTECACHE,
            CMD_INVALIDATE_METADATACACHE, CMD_SHOWDEVELOPERCOMMANDS, CMD_REFRESHVIEWS, CMD_UNSCHEDULE_JOB,
            CMD_SHOWJOBS, CMD_SHOWREPORTS, CMD_IMPORTLAYOUTS, CMD_EXPORTLAYOUTS, CMD_IMPORTRULES, CMD_EXPORTRULES,
            CMD_SETUSERPREFERENCES, CMD_RESETUSERPREFERENCES, CMD_VALIDATEOBJECTGENERATIONS,
            CMD_CHECKMASTERDATAVALUES, CMD_COMPILEDBOBJECTS, CMD_SENDMESSAGE, CMD_KILLSESSION,
            CMD_IMPORTTIMELIMITRULES, CMD_EXPORTTIMELIMITRULES, CMD_EXECUTE_TIMELIMITRULE_NOW,
            CMD_NUCLET_GENERATION_FROM_XLSX, CMD_NUCLET_GENERATION_CREATE_EMPTY_XLSX_FILE, CMD_REBUILD_CONSTRAINTS);

    private static NuclosConsole INSTANCE;

    private static String sUserName;

    // 

    // Spring injection

    private AttributeCache attributeCache;

    private SchedulerControlFacadeRemote schedulerControlFacadeRemote;

    private TimelimitRuleFacadeRemote timelimitRuleFacadeRemote;

    private PreferencesFacadeRemote preferencesFacadeRemote;

    private MasterDataFacadeRemote masterDataFacadeRemote;

    private DatasourceFacadeRemote datasourceFacadeRemote;

    private ConsoleFacadeRemote consoleFacadeRemote;

    private NuclosRemoteServerSession nuclosRemoteServerSession;

    // end of Spring injection

    public static NuclosConsole getInstance() {
        if (INSTANCE == null) {
            throw new IllegalStateException("too early");
        }
        return INSTANCE;
    }

    protected static NuclosConsole newNuclosConsole() {
        try {
            final String sClassName = LangUtils.defaultIfNull(
                    ApplicationProperties.getInstance().getConsoleClassName(), NuclosConsole.class.getName());

            INSTANCE = (NuclosConsole) Class.forName(sClassName).newInstance();
            return INSTANCE;
        } catch (Exception ex) {
            throw new CommonFatalException("Console could not be created.", ex);
        }
    }

    protected NuclosConsole() {
    }

    @Autowired
    @Qualifier("attributeCache")
    final void setAttributeCache(AttributeCache attributeCache) {
        this.attributeCache = attributeCache;
    }

    @Autowired
    final void setSchedulerControlFacadeRemote(SchedulerControlFacadeRemote schedulerControlFacadeRemote) {
        this.schedulerControlFacadeRemote = schedulerControlFacadeRemote;
    }

    @Autowired
    final void setTimelimitRuleFacadeRemote(TimelimitRuleFacadeRemote timelimitRuleFacadeRemote) {
        this.timelimitRuleFacadeRemote = timelimitRuleFacadeRemote;
    }

    @Autowired
    final void setPreferencesFacadeRemote(PreferencesFacadeRemote preferencesFacadeRemote) {
        this.preferencesFacadeRemote = preferencesFacadeRemote;
    }

    @Autowired
    final void setMasterDataFacadeRemote(MasterDataFacadeRemote masterDataFacadeRemote) {
        this.masterDataFacadeRemote = masterDataFacadeRemote;
    }

    @Autowired
    final void setDatasourceFacadeRemote(DatasourceFacadeRemote datasourceFacadeRemote) {
        this.datasourceFacadeRemote = datasourceFacadeRemote;
    }

    @Autowired
    final void setConsoleFacadeRemote(ConsoleFacadeRemote consoleFacadeRemote) {
        this.consoleFacadeRemote = consoleFacadeRemote;
    }

    @Autowired
    final void setNuclosRemoteServerSession(NuclosRemoteServerSession NuclosRemoteServerSession) {
        this.nuclosRemoteServerSession = nuclosRemoteServerSession;
    }

    private String login(String sUser, String sPassword) throws LoginException {
        sUserName = nuclosRemoteServerSession.login(sUser, sPassword);
        return sUserName;
    }

    private void logout() throws LoginException {
        nuclosRemoteServerSession.logout();
    }

    public String invalidateAllCaches() {
        return consoleFacadeRemote.invalidateAllCaches();
    }

    /**
     * Invalidates the attribute cache
     */
    private void invalidateAttributeCache() {
        System.out.println("Invalidating attribute cache...");
        AttributeDelegate.getInstance().invalidateCache();
        System.out.println("done");
    }

    /**
     * Invalidates the rule cache
     */
    private void invalidateRuleCache() {
        System.out.println("Invalidating rule cache...");
        RuleDelegate.getInstance().invalidateCache();
        System.out.println("done");
    }

    /**
     * Refreshes the dynamic generic object views
     */
    private void refreshViews() {
        System.out.println("Refreshing generic object views...");
        LayoutDelegate.getInstance().refreshAll();
        System.out.println("done");
    }

    /**
     * delete a scheduled job
     * @param sJobName
     */
    private void unscheduleJob(String sJobName) throws CommonBusinessException, RemoteException {
        schedulerControlFacadeRemote.deleteJob(sJobName);
        System.out.println("Successfully deleted job: " + sJobName);
        System.out.println(schedulerControlFacadeRemote.getSchedulerSummary());
    }

    private void executeTimelimitJobNow(String sRuleName) throws RemoteException, CommonBusinessException {
        try {
            timelimitRuleFacadeRemote.executeTimelimitRule(sRuleName);
        } catch (RuntimeException ex) {
            throw new CommonBusinessException(ex.getMessage());
        }
    }

    /**
     * get a list of all scheduled jobs
     */
    private void showJobs() throws RemoteException {
        System.out.println(schedulerControlFacadeRemote.getSchedulerSummary());
    }

    /**
     * List of all reports defined in the application
     * @throws CommonPermissionException
     */
    private void showReports() throws CommonPermissionException {
        for (final ReportVO report : getReports()) {
            if (report.getType() == ReportType.REPORT) {
                System.out.println(report.getName());
            }
        }
    }

    /**
     * @param sReportName
     * @return Does a report with the given name exist?
     * @throws CommonPermissionException
     */
    private boolean reportExists(String sReportName) throws CommonPermissionException {
        boolean result = false;
        for (ReportVO report : getReports()) {
            if (report.getName().compareTo(sReportName) == 0) {
                result = true;
                break;
            }
        }
        return result;
    }

    /**
     * @return all reports from the server
     * @throws CommonPermissionException
     */
    private Collection<ReportVO> getReports() throws CommonPermissionException {
        return ReportDelegate.getInstance().getReports();
    }

    private void importLayouts(String sInputDir) throws CommonBusinessException {
        final File fileInputDir = testForNonEmptyDirectory(sInputDir);

        try {
            LayoutDelegate.getInstance().importLayouts(readLayouts(fileInputDir));
        } catch (IOException ex) {
            throw new NuclosFatalException(ex);
        }
    }

    /**
     * @param fileInputDir
     * @return Collection<LayoutVO>
     * @throws IOException
     */
    private Collection<LayoutVO> readLayouts(File fileInputDir) throws IOException {
        final Collection<LayoutVO> result = new ArrayList<LayoutVO>();

        final FileFilter filter = new SuffixFileFilter(".layoutml");
        for (File file : fileInputDir.listFiles(filter)) {
            final String sLayoutName = getNameWithoutSuffix(file);
            final String sLayoutML = readFromTextFile(file);
            result.add(new LayoutVO(sLayoutName, sLayoutName, sLayoutML));
        }
        return result;
    }

    private String getNameWithoutSuffix(File file) {
        final int iDotPosition = file.getName().lastIndexOf('.');
        assert iDotPosition >= 0;
        return file.getName().substring(0, iDotPosition);
    }

    /**
     * Export all Layouts
     * @throws CommonBusinessException
     */
    public void exportLayouts(String sOutputDir) throws CommonBusinessException {
        exportLayouts(sOutputDir, NuclosEntity.LAYOUT);
    }

    private void exportLayouts(String sOutputDir, final NuclosEntity entity) throws CommonBusinessException {
        final File fileOutputDir = testForEmptyDirectory(sOutputDir);

        System.out.println("Exporting Layouts to " + sOutputDir + "...");

        try {
            for (MasterDataVO mdvoLayout : MasterDataDelegate.getInstance().getMasterData(entity.getEntityName())) {
                final String sLayoutName = mdvoLayout.getField("name", String.class);
                final String sLayoutML = mdvoLayout.getField("layoutML", String.class);
                // no need to write layouts containing no data
                if (sLayoutML != null) {
                    final String sFileName = sLayoutName + ".layoutml";
                    final File fileLayout = new File(fileOutputDir, sFileName);
                    IOUtils.writeToTextFile(fileLayout, sLayoutML, "ISO-8859-1");
                }
            }
        } catch (IOException ex) {
            throw new CommonFatalException(ex);
        }

        System.out.println("Layouts successfully exported.");
    }

    private void exportRules(String sOutputDir) throws CommonBusinessException {
        final File fileOutputDir = testForEmptyDirectory(sOutputDir);

        try {
            for (RuleVO rulevo : RuleCache.getInstance().getAllRules()) {
                final String sFileName = rulevo.getRule() + ".txt";
                IOUtils.writeToTextFile(new File(fileOutputDir, sFileName), rulevo.getSource(), "ISO-8859-1");
            }
        } catch (IOException e) {
            throw new CommonFatalException(e);
        }

        /** @todo   */
    }

    private void exportTimelimitRules(String sOutputDir) throws CommonBusinessException {
        final File fileOutputDir = testForEmptyDirectory(sOutputDir);

        try {
            for (RuleVO rulevo : TimelimitRuleDelegate.getInstance().getAllTimelimitRules()) {
                final String sFileName = rulevo.getRule() + ".txt";
                IOUtils.writeToTextFile(new File(fileOutputDir, sFileName), rulevo.getSource(), "ISO-8859-1");
            }
        } catch (IOException e) {
            throw new CommonFatalException(e);
        }
    }

    // Only used for exports, so target directory will be created if necessary
    private File testForEmptyDirectory(String sOutputDir) throws CommonBusinessException {
        final File fileOutputDir = testForDirectory(sOutputDir, true);

        for (File file : fileOutputDir.listFiles())
            if (!file.isHidden())
                throw new CommonBusinessException("The output directory must be empty.");

        return fileOutputDir;
    }

    // Only used for imports, so target directory must exist and will not be created
    private File testForNonEmptyDirectory(String sOutputDir) throws CommonBusinessException {
        final File fileOutputDir = testForDirectory(sOutputDir, false);

        if (fileOutputDir.listFiles().length == 0) {
            throw new CommonBusinessException("The input directory is empty.");
        }
        return fileOutputDir;
    }

    /**
     * Check for existence of a directory and create it, if desired.
     * @param sOutputDir path of directory
     * @param bCreate create directory if true
     * @return directory as file
     * @throws CommonBusinessException
     */
    private File testForDirectory(String sOutputDir, boolean bCreate) throws CommonBusinessException {
        final File fileOutputDir = new File(sOutputDir);
        if (!fileOutputDir.exists() && bCreate) {
            fileOutputDir.mkdir();
        }

        if (!fileOutputDir.isDirectory()) {
            throw new CommonBusinessException("The specified path does not denote a directory.");
        }
        return fileOutputDir;
    }

    private void importRules(String sInputDir) throws CommonBusinessException {
        final File fileInputDir = testForNonEmptyDirectory(sInputDir);

        try {
            System.out.println("Importing rules from " + sInputDir + "...");
            RuleDelegate.getInstance().importRules(readRules(fileInputDir));
            System.out.println("Done.");
        } catch (IOException ex) {
            throw new NuclosFatalException(ex);
        }
    }

    private void importTimelimitRules(String sInputDir) throws CommonBusinessException {
        final File fileInputDir = testForNonEmptyDirectory(sInputDir);

        try {
            System.out.println("Importing TimelimitRules from " + sInputDir + "...");
            TimelimitRuleDelegate.getInstance().importTimelimitRules(readTimelimitRules(fileInputDir));
            System.out.println("Done.");
        } catch (IOException ex) {
            throw new NuclosFatalException(ex);
        }
    }

    private Collection<RuleVO> readTimelimitRules(File fileInputDir) throws IOException {
        final Collection<RuleVO> result = new ArrayList<RuleVO>();

        for (File file : fileInputDir.listFiles(new SuffixFileFilter(".txt"))) {
            final String sRuleName = getNameWithoutSuffix(file);
            final String sRuleSource = readFromTextFile(file);
            final RuleVO rulevo = new RuleVO(sRuleName, sRuleName, sRuleSource, null, Boolean.TRUE);

            result.add(rulevo);
        }
        return result;
    }

    private Collection<RuleWithUsagesVO> readRules(File fileInputDir) throws IOException {
        final Collection<RuleWithUsagesVO> result = new ArrayList<RuleWithUsagesVO>();

        for (File file : fileInputDir.listFiles(new SuffixFileFilter(".txt"))) {
            final String sRuleName = getNameWithoutSuffix(file);
            final String sRuleSource = readFromTextFile(file);
            final RuleVO rulevo = new RuleVO(sRuleName, sRuleName, sRuleSource, null, true);
            result.add(new RuleWithUsagesVO(rulevo, new ArrayList<RuleEventUsageVO>()));
        }
        return result;
    }

    private void setUserPreferences(String sUserName, String sFileName) {
        final File filePreferencesXml = new File(sFileName);
        try {
            final byte[] bytes = IOUtils.readFromBinaryFile(filePreferencesXml);
            internalSetUserPreferences(sUserName, new PreferencesVO(bytes));
        } catch (IOException ex) {
            throw new NuclosFatalException(ex);
        }
    }

    private void resetUserPreferences(String sUserName) {
        internalSetUserPreferences(sUserName, null);
    }

    private void internalSetUserPreferences(String sUserName, PreferencesVO prefsvo) {
        try {
            preferencesFacadeRemote.setPreferencesForUser(sUserName, prefsvo);
        } catch (CommonFinderException ex) {
            throw new NuclosFatalException(ex);
        } catch (RuntimeException ex) {
            throw new NuclosFatalException(ex);
        }
    }

    /**
     * reads the contents of a text file, using the default encoding.
     *
     * @param file a File that must have a size < 2GB.
     * @return a String containing the contents of the file.
     * @throws java.io.IOException
     * @todo move to IOUtils
     * @todo Is it really true that it's not possible with BufferedReader.readLine()?
     */
    public String readFromTextFile(File file) throws IOException {
        final StringBuilder sb = new StringBuilder();
        final Reader br = new BufferedReader(new FileReader(file));

        try {
            appendText(br, sb);
        } finally {
            br.close();
        }
        return sb.toString();
    }

    /**
     * @param reader
     * @param sb
     * @throws IOException
     * @see #readFromTextFile(File)
     */
    private void appendText(Reader reader, StringBuilder sb) throws IOException {
        int i;
        boolean bLastCharWasCR = false;
        while ((i = reader.read()) != -1) {
            final char c = (char) i;
            if (bLastCharWasCR) {
                bLastCharWasCR = false;
                if (c == '\n') {
                    sb.append(c);
                    continue;
                } else {
                    sb.append('\n');
                }
            }
            bLastCharWasCR = (c == '\r');
            if (!bLastCharWasCR) {
                sb.append(c);
            }
        }
    }

    private String getUsage() {
        final StringBuilder sb = new StringBuilder();

        sb.append("Usage: NuclosConsole <username> <password> <serverconfig> <command>\n");
        sb.append(
                "The configuration for the application server must be one contained in the file nuclos-client.properties, which must be in the CLASSPATH.\n");
        sb.append("<command> is one of the following:\n");
        sb.append("==========================================================================\n");
        sb.append("\t-invalidateAttributeCache\n");
        sb.append("\t\t\tInvalidates the attribute cache.\n");
        sb.append("\t-refreshViews\n");
        sb.append("\t\t\tRefreshes the dynamic leased object views.\n");
        sb.append("\t-scheduleReportJob <ReportName> [-hour <hh>] [-minute <mm]>\n");
        sb.append("\t\t\tSchedules <ReportName> daily at specified hour and specified minute.\n");
        sb.append("\t-scheduleTimelimitJob -hour <hh> [-minute <mm>]\n");
        sb.append("\t\t\tSchedules the Timelimit job daily at specified hour and specified minute.\n");
        sb.append("\t-unscheduleJob <Job Name>\n");
        sb.append("\t\t\tRemoves scheduling for <Job Name>. Use -showJobs to get a list of scheduled jobs.\n");
        sb.append("\t-showJobs\n");
        sb.append("\t\t\tLists names of all scheduled jobs. Use these names for -unscheduleJob.\n");
        sb.append("\t-showReports\n");
        sb.append("\t\t\tLists all reports defined in the application.\n");
        sb.append("\t-importLayouts <directory>\n");
        sb.append("\t\t\tImports all layouts contained in the given directory (on the client side).\n");
        sb.append("\t-exportLayouts <directory>\n");
        sb.append("\t\t\tExports all layouts to the given output directory (on the client side).\n");
        sb.append("\t-importRules <input directory>\n");
        sb.append(
                "\t\t\tImports all business rules (without usages) contained in the given directory (on the client side).\n");
        sb.append("\t-exportRules <output directory>\n");
        sb.append(
                "\t\t\tExports all business rules (without usages) to an empty output directory (on the client side).\n");
        sb.append("\t-executeTimelimitRule <rule name>\n");
        sb.append("\t\t\tExecutes a timelimit rule immediately.\n");
        sb.append("\t-importTimelimitRules <input directory>\n");
        sb.append("\t\t\tImports all timelimit rules contained in the given directory (on the client side).\n");
        sb.append("\t-exportTimelimitRules <output directory>\n");
        sb.append("\t\t\tExports all timelimit rules to an empty output directory (on the client side).\n");
        sb.append("\t-setUserPreferences <user name> <file name>\n");
        sb.append(
                "\t\t\tStores the contents of the file with the given name as the preferences for the user with the given name.\n");
        sb.append("\t-resetUserPreferences <user name>\n");
        sb.append("\t\t\tResets the preferences for the user with the given name.\n");
        sb.append("\t-compileAllRules\n");
        sb.append("\t\t\tCompiles all rules.\n");
        sb.append("\t-compilealltimelimitrules\n");
        sb.append("\t\t\tCompiles all Timelimit rules.\n");
        sb.append("\t-checkMasterDataValues\n");
        sb.append(
                "\t\t\tChecks all masterdata records against masterdata meta information (field length, not null etc.). Will take a long time!\n");
        sb.append("\t-developer\n");
        sb.append("\t\t\tShows commands only relevant for developing.\n");
        sb.append(
                "\t\t\tPrefix, suffix and extension may contain wildcards, but prefix must not be or contain *.\n");
        sb.append("\t\t\tSpecification of logfile path and/or name without extension, default is ImportLog.\n");
        sb.append(
                "\t\t\tIndication of path for logging is recommended. Path or Filenames with blanks have to be quoted.\n");
        sb.append(
                "\t\t\tIf no remark text is given, default is \"Migration Altakten\". Quote text if more than one word.\n");
        sb.append("\t\t\tDefault for deletion of source files ist false.\n");
        sb.append("\t-removeimporteddocuments [-remark <text>] [-logfile <base file name or path>]\n");
        sb.append(
                "\t\t\tRemoves prior added documents from assets, optional filtert by the given remark during import.\n");
        sb.append(
                "\t\t\tIf no remark text is given, default is \"Migration Altakten\". Quote text if more than one word.\n");
        sb.append("\t\t\tSpecification of logfile path and/or name without extension, default is Remove.\n");
        sb.append(
                "\t\t\tIndication of path for logging is recommended. Path or Filenames with blanks have to be quoted.\n");
        sb.append("\t-migrationService [-logfile <filename>] [-undo]\n");
        sb.append("\t\t\tMigrate entries from assetinformation to assetservice.\n");
        sb.append("\t\t\tUse -undo to revert all changes.\n");
        sb.append("\t-compileDbObjects\n");
        sb.append("\t\t\tcompile all invalid db functins and views.\n");
        sb.append(
                "\t-sendMessage -message <message text> [-user <username>] [-priority <high, normal, low>] [-author <author>]\n");
        sb.append("\t\t\tsend a message to the specified user (or all users if no one is specified).\n");
        sb.append("\t-killSession [-user <username>]\n");
        sb.append("\t\t\tkill the client of the specified user (or all users if no one is specified).\n");
        sb.append("\t-invalidatAllCaches\n");
        sb.append("\t\t\tinvalide all server side caches.\n");
        sb.append("\t-rebuildConstraints\n");
        sb.append("\t\t\rebuilds all unique and foreign constraints.\n");

        return sb.toString();
    }

    /**
     * directly called by GUI (already logged in) - called by console's main method
     * @param asArgs
     * @param bCalledByConsole
     */
    public void parseAndInvoke(String[] asArgs, boolean bCalledByConsole) throws Exception {
        final long lStartTime = new Date().getTime();
        if (asArgs == null || asArgs.length == 0) {
            System.out.println(getUsage());
            return;
        }
        final String sCommand = asArgs[0];
        final String sCommandLowerCase = sCommand.toLowerCase();

        parseAndInvoke(sCommand, sCommandLowerCase, asArgs, bCalledByConsole);

        System.out.println("NuclosConsole finished in " + (DateUtils.now().getTime() - lStartTime) + " ms");
    }

    protected void parseAndInvoke(String sCommand, String sCommandLowerCase, String[] asArgs,
            boolean bCalledByConsole) throws Exception {
        if (sCommandLowerCase.equals(CMD_INVALIDATE_ATTRIBUTECACHE)) {
            invalidateAttributeCache();
        } else if (sCommandLowerCase.equals(CMD_INVALIDATE_RULECACHE)) {
            invalidateRuleCache();
        } else if (sCommandLowerCase.equals(CMD_REFRESHVIEWS)) {
            refreshViews();
        } else if (sCommandLowerCase.equals(CMD_UNSCHEDULE_JOB)) {
            if (asArgs.length < 2) {
                System.out.println("Missing argument <ReportName> for command " + sCommand + "\n");
                System.out.println(getUsage());
                if (bCalledByConsole) {
                    System.exit(-1);
                } else {
                    return;
                }
            }
            unscheduleJob(asArgs[1]);
        } else if (sCommandLowerCase.equals(CMD_SHOWJOBS)) {
            showJobs();
        } else if (sCommandLowerCase.equals(CMD_SHOWREPORTS)) {
            showReports();
        } else if (sCommandLowerCase.equals(CMD_IMPORTLAYOUTS)) {
            checkForImportExportParameters(asArgs, false);

            final String sInputDir = asArgs[1];
            importLayouts(sInputDir);
        } else if (sCommandLowerCase.equals(CMD_EXPORTLAYOUTS)) {
            checkForImportExportParameters(asArgs, true);

            final String sOutputDir = asArgs[1];
            exportLayouts(sOutputDir);
        } else if (sCommandLowerCase.equals(CMD_IMPORTRULES)) {
            checkForImportExportParameters(asArgs, false);

            final String sInputDir = asArgs[1];
            importRules(sInputDir);
        } else if (sCommandLowerCase.equals(CMD_EXPORTRULES)) {
            checkForImportExportParameters(asArgs, true);

            final String sOutputDir = asArgs[1];
            exportRules(sOutputDir);
        } else if (sCommandLowerCase.equals(CMD_IMPORTTIMELIMITRULES)) {
            checkForImportExportParameters(asArgs, false);

            final String sInputDir = asArgs[1];
            importTimelimitRules(sInputDir);
        } else if (sCommandLowerCase.equals(CMD_EXPORTTIMELIMITRULES)) {
            checkForImportExportParameters(asArgs, true);

            final String sOutputDir = asArgs[1];
            exportTimelimitRules(sOutputDir);
        } else if (sCommandLowerCase.equals(CMD_EXECUTE_TIMELIMITRULE_NOW)) {
            if (asArgs.length < 2) {
                System.out.println("Missing argument <rule name> for command " + sCommand + "\n");
                System.out.println(getUsage());
                if (bCalledByConsole) {
                    System.exit(-1);
                } else {
                    return;
                }
            }
            executeTimelimitJobNow(asArgs[1]);
        } else if (sCommandLowerCase.equals(CMD_SETUSERPREFERENCES)) {
            if (asArgs.length < 3) {
                System.out.println(getUsage());
            }
            final String sUserName = asArgs[1];
            final String sFileName = asArgs[2];
            setUserPreferences(sUserName, sFileName);
        } else if (sCommandLowerCase.equals(CMD_RESETUSERPREFERENCES)) {
            if (asArgs.length < 2) {
                System.out.println(getUsage());
            }
            final String sUserName = asArgs[1];
            resetUserPreferences(sUserName);
        } else if (sCommandLowerCase.equals("-dosomethinguseful")) {
            doSomethingUseful(asArgs);
        } else if (sCommandLowerCase.equals(CMD_SHOWDEVELOPERCOMMANDS)) {
            printDeveloperCommands();
        } else if (sCommandLowerCase.equals(CMD_INVALIDATE_METADATACACHE)) {
            System.out.println("Invalidating metadata cache...");
            masterDataFacadeRemote.revalidateMasterDataMetaCache();
            System.out.println("done");
        } else if (sCommandLowerCase.equals(CMD_INVALIDATE_DATASOURCECACHE)) {
            System.out.println("Invalidating datasource cache...");
            datasourceFacadeRemote.invalidateCache();
            System.out.println("done");
        } else if (sCommandLowerCase.equals(CMD_VALIDATEOBJECTGENERATIONS)) {
            validateObjectGenerations();
        } else if (sCommandLowerCase.equals(CMD_CHECKMASTERDATAVALUES)) {
            String filename = "masterdata.csv";
            if (asArgs.length < 2) {
                System.out.println("No file name specified. Default is: " + filename + "\n");
            } else {
                filename = asArgs[1];
            }
            checkMasterDataValues(filename);
        } else if (sCommandLowerCase.equals(CMD_COMPILEDBOBJECTS)) {
            compileDBObjects();
        } else if (sCommandLowerCase.equals(CMD_REBUILD_CONSTRAINTS.toLowerCase())) {
            rebuildConstraints();
        } else if (sCommandLowerCase.equals(CMD_SENDMESSAGE)) {
            String sMessage = null;
            String sUser = null;
            String sAuthor = "Administrator";
            Priority priority = Priority.HIGH;

            if (asArgs.length >= 2) {
                for (int i = 1; i < asArgs.length; i++) {
                    if (asArgs[i].equalsIgnoreCase("-user") && i + 1 < asArgs.length) {
                        sUser = asArgs[i + 1];
                    } else if (asArgs[i].equalsIgnoreCase("-priority") && i + 1 < asArgs.length) {
                        String sPrio = asArgs[i + 1];
                        if (sPrio.equalsIgnoreCase("hoch") || sPrio.equalsIgnoreCase("high")) {
                            priority = Priority.HIGH;
                        } else if (sPrio.equalsIgnoreCase("normal")) {
                            priority = Priority.NORMAL;
                        } else if (sPrio.equalsIgnoreCase("niedrig") || sPrio.equalsIgnoreCase("low")) {
                            priority = Priority.LOW;
                        } else {
                            throw new CommonBusinessException("Unknown priority \"" + sPrio + "\". Use one of: "
                                    + Priority.HIGH + ", " + Priority.NORMAL + ", " + Priority.LOW);
                        }
                    } else if (asArgs[i].equalsIgnoreCase("-message") && i + 1 < asArgs.length) {
                        sMessage = asArgs[i + 1];
                    } else if (asArgs[i].equalsIgnoreCase("-author") && i + 1 < asArgs.length) {
                        sAuthor = asArgs[i + 1];
                    }
                }
                if (sMessage != null) {
                    sendMessage(sMessage, sUser, priority, sAuthor);
                } else {
                    throw new CommonBusinessException("Missing argument -message\n");
                }

            } else {
                throw new CommonBusinessException("Missing arguments for sendMessage\n" + getUsage());
            }
        } else if (sCommandLowerCase.equals(CMD_KILLSESSION)) {
            String sUser = null;
            if (asArgs.length >= 2) {
                for (int i = 1; i < asArgs.length; i++) {
                    if (asArgs[i].equalsIgnoreCase("-user") && i + 1 < asArgs.length) {
                        sUser = asArgs[i + 1];
                    }
                }
            }
            killSession(sUser);
        } else if (sCommandLowerCase.equals(CMD_INVALIDATEALLCACHES)) {
            invalidatAllCaches();
        } else if (sCommandLowerCase.equals(CMD_MIGRATESEARCHFILTER)) {
            MigrateSearchFilterPreferences.getInstance().migrate(sUserName);
        } else if (sCommandLowerCase.equals(CMD_NUCLET_GENERATION_CREATE_EMPTY_XLSX_FILE.toLowerCase())) {
            NucletGenerator generator = new NucletGenerator();
            if (asArgs.length == 2) {
                generator.createEmptyXLSXFile(asArgs[1]);
            } else {
                generator.createEmptyXLSXFile();
            }
        } else if (sCommandLowerCase.equals(CMD_NUCLET_GENERATION_FROM_XLSX.toLowerCase())) {
            NucletGenerator generator = new NucletGenerator();
            if (asArgs.length == 2) {
                generator.generateNucletFromXLSX(asArgs[1]);
            } else {
                generator.generateNucletFromXLSX();
            }
        } else {
            throw new CommonBusinessException("Unknown command: " + sCommand + "\n" + getUsage());
        }
    }

    private void invalidatAllCaches() throws CommonRemoteException, RemoteException, CommonFatalException {
        System.out.println(consoleFacadeRemote.invalidateAllCaches());
    }

    private void killSession(String user) throws RemoteException {
        consoleFacadeRemote.killSession(user);
    }

    private void sendMessage(String sMessage, String sUser, Priority priority, String sAuthor)
            throws RemoteException {
        consoleFacadeRemote.sendClientNotification(sMessage, sUser, priority, sAuthor);
    }

    /**
     * Check the given parameters for import or export use cases. Give explicit error description if necessary.
     * @param args all arguments to the use case
     * @param bDirectoryMayBeEmpty
     * @throws CommonBusinessException
     */
    private void checkForImportExportParameters(String[] args, boolean bDirectoryMayBeEmpty)
            throws CommonBusinessException {
        if (args.length < 2) {
            throw new CommonBusinessException(
                    "Name of " + (bDirectoryMayBeEmpty ? "empty " : "") + "directory must be given.");
        }
        if (args.length > 2) {
            throw new CommonBusinessException("Too many parameters - maybe enclosing quotation marks missing?");
        }
    }

    private void compileDBObjects() throws RemoteException, SQLException {
        consoleFacadeRemote.compileInvalidDbObjects();
    }

    private void rebuildConstraints() {
        String[] result = consoleFacadeRemote.rebuildConstraints();
        if (result != null) {
            for (String s : result) {
                System.out.println(s);
            }
        }
    }

    /**
     * Validate all masterdata entries against their meta information (length, format, min, max etc.).
     * @param filename the name of the csv file to which the results are written.
     */
    private void checkMasterDataValues(String filename) {
        // iterate over masterdata meta cache; masterdatafacade.get(entity) to get all; validate each cvo
        MasterDataDelegate.getInstance().checkMasterDataValues(filename);
    }

    /**
     * Check and eliminate attributes from object generation which are not valid in the context
     * todo: check validity of subentities here also!
     */
    private void validateObjectGenerations() {
        System.out.println("validateObjectGenerations start ...");
        attributeCache.fill();

        final GenericObjectMetaDataCache lometacache = GenericObjectMetaDataCache.getInstance();
        for (MasterDataVO mdvoGeneration : MasterDataDelegate.getInstance()
                .getMasterData(NuclosEntity.GENERATION.getEntityName())) {
            final Collection<String> collSourceAttributeNames = lometacache.getAttributeNamesByModuleId(
                    mdvoGeneration.getField("sourceModuleId", Integer.class), Boolean.FALSE);
            final Collection<String> collTargetAttributeNames = lometacache.getAttributeNamesByModuleId(
                    mdvoGeneration.getField("targetModuleId", Integer.class), Boolean.FALSE);
            final Collection<String> collAttributeNames = CollectionUtils.intersection(collSourceAttributeNames,
                    collTargetAttributeNames);
            final Collection<EntityObjectVO> coll = MasterDataDelegate.getInstance().getDependantMasterData(
                    NuclosEntity.GENERATIONATTRIBUTE.getEntityName(), "generation", mdvoGeneration.getId());
            for (EntityObjectVO mdvoAttribute : coll) {
                if (!collAttributeNames.contains(mdvoAttribute.getField("attribute", String.class))) {
                    try {
                        MasterDataDelegate.getInstance().remove(NuclosEntity.GENERATIONATTRIBUTE.getEntityName(),
                                DalSupportForMD.wrapEntityObjectVO(mdvoAttribute), null);
                    } catch (CommonBusinessException e) {
                        // Ok! (tp)
                        e.printStackTrace();
                    }
                }
            }
        }
        System.out.println("validateObjectGenerations finished");
    }

    private void printDeveloperCommands() {
        System.out.println("Developer commands:\n");
        System.out.println("\t-invalidatemasterdatametacache");
        System.out.println(
                "\t\t\tFills the masterdata cache of the server newly with values, so it is not necessary to restart the server (but the client still)");
    }

    /**
     * This is a temporary hack that may be used for quick migrations etc.
     * @param asArgs
     * @todo create a server-side pendant doSomethingUseful(asArgs) in your favorite FacadeBean.
     */
    private void doSomethingUseful(String[] asArgs) {
        // today, we're generating stupid sql inserts out of metadata information:
    }

    /**
     * called if real console is required (shell)
     * @param asArgs
     */
    public static void main(String[] asArgs) {
        final NuclosConsole dut = NuclosConsole.getInstance();
        try {
            if (asArgs.length < 4) {
                System.out.println("Missing command.\n\n");
                System.out.println(dut.getUsage());
                System.exit(-1);
            }

            dut.login(asArgs[0], asArgs[1]);
            try {
                final String[] asParamsWithoutLoginInfo = new String[asArgs.length - 3];
                for (int i = 3; i < asArgs.length; i++) {
                    asParamsWithoutLoginInfo[i - 3] = asArgs[i];
                }
                NuclosConsole.getInstance().parseAndInvoke(asParamsWithoutLoginInfo, true);
            } finally {
                dut.logout();
            }
            System.exit(0);
        } catch (CommonBusinessException ex) {
            // Ok! (tp)
            System.err.println(ex.getMessage());
            ex.printStackTrace();
            System.exit(-1);
        } catch (Exception ex) {
            // Ok! (tp)
            ex.printStackTrace(System.err);
            System.exit(-1);
        }
    }

    /**
     * A FileFilter accepting all files that end with the given suffix.
     */
    private static class SuffixFileFilter implements FileFilter {
        private final String sSuffix;

        /**
         * @param sSuffix
         * @precondition sSuffix.startsWith(".")
         */
        SuffixFileFilter(String sSuffix) {
            if (sSuffix == null) {
                throw new NullArgumentException("sSuffix");
            }
            if (!sSuffix.startsWith(".")) {
                throw new IllegalArgumentException("sSuffix");
            }
            this.sSuffix = sSuffix;
        }

        @Override
        public boolean accept(File file) {
            return file.getName().toLowerCase().endsWith(sSuffix);
        }
    }

} // class NuclosConsole