Java tutorial
/******************************************************************************* * 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$ } }