net.sf.logsaw.ui.impl.LogResourceManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.logsaw.ui.impl.LogResourceManagerImpl.java

Source

/*******************************************************************************
 * Copyright (c) 2010, 2011 LogSaw project and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    LogSaw project committers - initial API and implementation
 *******************************************************************************/
package net.sf.logsaw.ui.impl;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import net.sf.logsaw.core.CorePlugin;
import net.sf.logsaw.core.config.IConfigChangedListener;
import net.sf.logsaw.core.config.IConfigOption;
import net.sf.logsaw.core.config.IConfigOptionVisitor;
import net.sf.logsaw.core.config.IConfigurableObject;
import net.sf.logsaw.core.config.model.StringConfigOption;
import net.sf.logsaw.core.dialect.ILogDialect;
import net.sf.logsaw.core.dialect.ILogDialectFactory;
import net.sf.logsaw.core.logresource.ILogResource;
import net.sf.logsaw.core.logresource.simple.SimpleLogResourceFactory;
import net.sf.logsaw.index.IIndexService;
import net.sf.logsaw.index.IndexPlugin;
import net.sf.logsaw.index.SynchronizationResult;
import net.sf.logsaw.ui.IGenericCallback;
import net.sf.logsaw.ui.ILogResourceManager;
import net.sf.logsaw.ui.Messages;
import net.sf.logsaw.ui.UIPlugin;

import org.apache.commons.io.FileUtils;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.progress.IProgressConstants2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Philipp Nanz
 */
public class LogResourceManagerImpl implements ILogResourceManager {

    private static transient Logger logger = LoggerFactory.getLogger(LogResourceManagerImpl.class);

    private static final String LOG_RESOURCE_STATE_FILE = "logResources.xml"; //$NON-NLS-1$

    private static final int COMPAT_VERSION = 2;
    private static final String ELEM_ROOT = "logResources"; //$NON-NLS-1$
    private static final String ATTRIB_COMPAT = "compat"; //$NON-NLS-1$
    private static final String ELEM_LOG_RESOURCE = "logResource"; //$NON-NLS-1$
    private static final String ELEM_NAME = "name"; //$NON-NLS-1$
    private static final String ELEM_DIALECT = "dialect"; //$NON-NLS-1$
    private static final String ATTRIB_FACTORY = "factory"; //$NON-NLS-1$
    private static final String ELEM_PK = "pk"; //$NON-NLS-1$
    private static final String ELEM_OPTION = "option"; //$NON-NLS-1$
    private static final String ATTRIB_KEY = "key"; //$NON-NLS-1$
    private static final String ATTRIB_LABEL = "label"; //$NON-NLS-1$
    private static final String ATTRIB_VISIBLE = "visible"; //$NON-NLS-1$
    private static final String ATTRIB_TYPE = "type"; //$NON-NLS-1$
    private static final String VALUE_TYPE_STRING = "string"; //$NON-NLS-1$

    private static final QualifiedName QN_RESULT = new QualifiedName(
            LogResourceManagerImpl.class.getCanonicalName(), "synchronizationResult"); //$NON-NLS-1$

    private IConfigChangedListener configChangedListener = new IConfigChangedListener() {

        /* (non-Javadoc)
         * @see net.sf.logsaw.core.framework.support.IConfigChangedListener#configChanged(net.sf.logsaw.core.config.IConfigurableObject, net.sf.logsaw.core.config.IConfigOption)
         */
        @Override
        public void configChanged(IConfigurableObject subject, IConfigOption<?> option) {
            try {
                saveState();
            } catch (CoreException e) {
                UIPlugin.logAndShowError(e, false);
            }
        }
    };

    private IPath stateFile;
    private Set<ILogResource> logSet;
    private Map<ILogResource, Job> syncInProgressMap = Collections
            .synchronizedMap(new HashMap<ILogResource, Job>());

    /**
     * Constructor.
     * @param stateLocation the state location
     */
    public LogResourceManagerImpl(IPath stateLocation) {
        this.stateFile = stateLocation.append(LOG_RESOURCE_STATE_FILE);
        try {
            loadState();
        } catch (CoreException e) {
            UIPlugin.logAndShowError(e, false);
        }
    }

    /* (non-Javadoc)
     * @see net.sf.logsaw.ui.ILogResourceManager#close()
     */
    @Override
    public void close() throws CoreException {
        // Wait for all jobs to finish
        List<Job> l = new ArrayList<Job>(syncInProgressMap.values());
        for (Job job : l) {
            job.cancel();
            try {
                job.join();
            } catch (InterruptedException e) {
                logger.error(e.getLocalizedMessage(), e);
            }
        }
    }

    /* (non-Javadoc)
     * @see net.sf.logsaw.ui.ILogResourceManager#add(net.sf.logsaw.core.framework.ILogResource)
     */
    @Override
    public void add(ILogResource log) throws CoreException {
        Assert.isNotNull(log, "log"); //$NON-NLS-1$
        // Check if resource already managed
        if (logSet.contains(log)) {
            throw new CoreException(new Status(IStatus.ERROR, UIPlugin.PLUGIN_ID, NLS.bind(
                    Messages.LogResourceManager_error_resourceAlreadyManaged, new Object[] { log.toString() })));
        }

        // Create PK
        IndexPlugin.getDefault().getIndexService().createIndex(log);

        // Register log resource
        registerLogResource(log);
    }

    /* (non-Javadoc)
     * @see net.sf.logsaw.ui.ILogResourceManager#remove(net.sf.logsaw.core.framework.ILogResource[])
     */
    @Override
    public void remove(final ILogResource log) throws CoreException {
        Assert.isNotNull(log, "log"); //$NON-NLS-1$
        Assert.isTrue(logSet.contains(log), "Log is not managed"); //$NON-NLS-1$

        // Delete index folders
        IndexPlugin.getDefault().getIndexService().deleteIndex(log);

        // Unregister log resource
        unregisterLogResource(log);
    }

    /* (non-Javadoc)
     * @see net.sf.logsaw.ui.ILogResourceManager#getAll()
     */
    @Override
    public ILogResource[] getAll() {
        return logSet.toArray(new ILogResource[logSet.size()]);
    }

    /* (non-Javadoc)
     * @see net.sf.logsaw.ui.ILogResourceManager#synchronize(net.sf.logsaw.core.framework.ILogResource, net.sf.logsaw.ui.IGenericCallback)
     */
    @Override
    public void synchronize(final ILogResource log, final IGenericCallback<SynchronizationResult> callback)
            throws CoreException {
        Assert.isNotNull(log, "log"); //$NON-NLS-1$
        Assert.isTrue(logSet.contains(log), "Log is not managed"); //$NON-NLS-1$

        // The index is updated asynchronously
        Job job = new Job(NLS.bind(Messages.LogResourceManager_indexSynchronizingJob_name, log.getName())) {

            /* (non-Javadoc)
             * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
             */
            @Override
            protected IStatus run(IProgressMonitor monitor) {
                IIndexService idx = IndexPlugin.getDefault().getIndexService();
                try {
                    // Do the indexing
                    setProperty(QN_RESULT, idx.synchronize(log, monitor));
                } catch (CoreException e) {
                    return e.getStatus();
                }
                if (monitor.isCanceled()) {
                    // Job was canceled
                    return Status.CANCEL_STATUS;
                }
                return Status.OK_STATUS;
            }
        };
        job.setPriority(Job.LONG);
        // Enable progress in taskbar on Windows 7
        job.setProperty(IProgressConstants2.SHOW_IN_TASKBAR_ICON_PROPERTY, true);
        job.addJobChangeListener(new JobChangeAdapter() {
            /* (non-Javadoc)
             * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
             */
            @Override
            public void done(IJobChangeEvent event) {
                syncInProgressMap.remove(log);
            }
        });
        if (callback != null) {
            job.addJobChangeListener(new JobChangeAdapter() {
                /* (non-Javadoc)
                 * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
                 */
                @Override
                public void done(IJobChangeEvent event) {
                    SynchronizationResult result = (SynchronizationResult) event.getJob().getProperty(QN_RESULT);
                    if (event.getResult().isOK()) {
                        callback.doCallback(result);
                    } else if (event.getResult().getSeverity() == IStatus.CANCEL) {
                        callback.doCallback(result);
                    }
                }
            });
        }
        syncInProgressMap.put(log, job);
        job.schedule();
    }

    /* (non-Javadoc)
     * @see net.sf.logsaw.ui.ILogResourceManager#isJobInProgress(net.sf.logsaw.core.framework.ILogResource)
     */
    @Override
    public boolean isJobInProgress(ILogResource log) {
        return syncInProgressMap.containsKey(log);
    }

    /* (non-Javadoc)
     * @see net.sf.logsaw.ui.ILogResourceManager#saveState()
     */
    @Override
    public synchronized void saveState() throws CoreException {
        XMLMemento rootElem = XMLMemento.createWriteRoot(ELEM_ROOT);
        rootElem.putInteger(ATTRIB_COMPAT, COMPAT_VERSION);
        for (ILogResource log : logSet) {
            IMemento logElem = rootElem.createChild(ELEM_LOG_RESOURCE);
            logElem.createChild(ELEM_NAME).putTextData(log.getName());
            IMemento dialectElem = logElem.createChild(ELEM_DIALECT);
            dialectElem.putString(ATTRIB_FACTORY, log.getDialect().getFactory().getId());
            // Save config options of dialect
            saveConfigOptions(dialectElem,
                    (IConfigurableObject) log.getDialect().getAdapter(IConfigurableObject.class));
            logElem.createChild(ELEM_PK).putTextData(log.getPK());
            // Save config options of resource
            saveConfigOptions(logElem, (IConfigurableObject) log.getAdapter(IConfigurableObject.class));
        }
        try {
            // Save to state file
            rootElem.save(new BufferedWriter(
                    new OutputStreamWriter(FileUtils.openOutputStream(stateFile.toFile()), "UTF-8"))); //$NON-NLS-1$
        } catch (IOException e) {
            // Unexpected exception; wrap with CoreException
            throw new CoreException(new Status(IStatus.ERROR, UIPlugin.PLUGIN_ID,
                    NLS.bind(Messages.LogResourceManager_error_failedToUpdateState,
                            new Object[] { e.getLocalizedMessage() }),
                    e));
        }
    }

    private void registerLogResource(ILogResource log) {
        logSet.add(log);
        log.addConfigChangedListener(configChangedListener);
    }

    private void unregisterLogResource(ILogResource log) {
        log.removeConfigChangedListener(configChangedListener);
        logSet.remove(log);
    }

    private void saveConfigOptions(final IMemento parentElem, final IConfigurableObject co) throws CoreException {
        if (co == null) {
            // Nothing to save
            return;
        }
        List<IConfigOption<?>> options = co.getAllConfigOptions();
        for (IConfigOption<?> option : options) {
            option.visit(new IConfigOptionVisitor() {

                @Override
                public void visit(StringConfigOption opt, String value) throws CoreException {
                    // Store as child element
                    IMemento optElem = parentElem.createChild(ELEM_OPTION);
                    optElem.putString(ATTRIB_KEY, opt.getKey());
                    optElem.putString(ATTRIB_LABEL, opt.getLabel());
                    optElem.putBoolean(ATTRIB_VISIBLE, opt.isVisible());
                    optElem.putString(ATTRIB_TYPE, VALUE_TYPE_STRING);
                    optElem.putTextData(co.getConfigValue(opt));
                }
            }, null);
        }
    }

    private synchronized void loadState() throws CoreException {
        logSet = new CopyOnWriteArraySet<ILogResource>();
        if (!stateFile.toFile().exists()) {
            // Not exists yet
            return;
        }
        IMemento rootElem = null;
        try {
            rootElem = XMLMemento.createReadRoot(new BufferedReader(
                    new InputStreamReader(FileUtils.openInputStream(stateFile.toFile()), "UTF-8"))); //$NON-NLS-1$
        } catch (IOException e) {
            // Unexpected exception; wrap with CoreException
            throw new CoreException(new Status(IStatus.ERROR, UIPlugin.PLUGIN_ID,
                    NLS.bind(Messages.LogResourceManager_error_failedToLoadState,
                            new Object[] { e.getLocalizedMessage() }),
                    e));
        }
        // Check if we can read this
        Integer compat = rootElem.getInteger(ATTRIB_COMPAT);
        if ((compat == null) || (compat.intValue() != COMPAT_VERSION)) {
            throw new CoreException(new Status(IStatus.WARNING, UIPlugin.PLUGIN_ID,
                    Messages.LogResourceManager_warn_stateFileIncompatible));
        }

        List<IStatus> statuses = new ArrayList<IStatus>();
        for (IMemento logElem : rootElem.getChildren(ELEM_LOG_RESOURCE)) {
            String name = null;
            try {
                name = logElem.getChild(ELEM_NAME).getTextData();
                IMemento dialectElem = logElem.getChild(ELEM_DIALECT);
                String dialectFactory = dialectElem.getString(ATTRIB_FACTORY);
                ILogDialectFactory factory = CorePlugin.getDefault().getLogDialectFactory(dialectFactory);
                ILogDialect dialect = factory.createLogDialect();

                // Restore config options of dialect
                loadConfigOptions(dialectElem, (IConfigurableObject) dialect.getAdapter(IConfigurableObject.class));
                String pk = logElem.getChild(ELEM_PK).getTextData();

                // TODO Dynamic factory for log resource
                ILogResource log = SimpleLogResourceFactory.getInstance().createLogResource();
                log.setDialect(dialect);
                log.setName(name);
                log.setPK(pk);

                // Restore config options of resource
                loadConfigOptions(logElem, (IConfigurableObject) log.getAdapter(IConfigurableObject.class));

                // Unlock if necessary
                if (IndexPlugin.getDefault().getIndexService().unlock(log)) {
                    logger.warn("Unlocked log resource " + log.getName()); //$NON-NLS-1$
                }
                // Register log resource
                registerLogResource(log);
            } catch (Exception e) {
                statuses.add(new Status(IStatus.ERROR, UIPlugin.PLUGIN_ID, NLS.bind(
                        Messages.LogResourceManager_error_failedToRestoreLogResource, new Object[] { name }), e));
            }
        }
        if (!statuses.isEmpty()) {
            MultiStatus multiStatus = new MultiStatus(UIPlugin.PLUGIN_ID, 0,
                    statuses.toArray(new IStatus[statuses.size()]),
                    Messages.LogResourceManager_error_someLogResourcesCouldNotBeRestored, null);
            throw new CoreException(multiStatus);
        }
        logger.info("Loaded " + logSet.size() + " log resource(s)"); //$NON-NLS-1$ //$NON-NLS-2$
    }

    private void loadConfigOptions(final IMemento parentElem, final IConfigurableObject co) throws CoreException {
        if (co == null) {
            // Nothing to restore
            return;
        }
        IMemento[] optElems = parentElem.getChildren(ELEM_OPTION);
        for (final IMemento optElem : optElems) {
            IConfigOption<?> opt = null;
            String key = optElem.getString(ATTRIB_KEY);
            String label = optElem.getString(ATTRIB_LABEL);
            boolean visible = optElem.getBoolean(ATTRIB_VISIBLE);
            String type = optElem.getString(ATTRIB_TYPE);
            if (type.equals(VALUE_TYPE_STRING)) {
                opt = new StringConfigOption(key, label, visible);
            }
            Assert.isNotNull(opt, "Config option type not supported: " + type); //$NON-NLS-1$
            // Configure the option
            opt.visit(new IConfigOptionVisitor() {

                @Override
                public void visit(StringConfigOption opt, String value) throws CoreException {
                    // Restore
                    co.configure(opt, optElem.getTextData());
                }
            }, null);
        }
        Assert.isTrue(co.isConfigured(), "Object should be configured by now"); //$NON-NLS-1$
    }
}