Package org.locationtech.udig.catalog

Source Code of org.locationtech.udig.catalog.CatalogPlugin

/*
*    uDig - User Friendly Desktop Internet GIS client
*    http://udig.refractions.net
*    (C) 2012, Refractions Research Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD
* License v1.0 (http://udig.refractions.net/files/bsd3-v10.html).
*/
package org.locationtech.udig.catalog;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.locationtech.udig.catalog.internal.CatalogImpl;
import org.locationtech.udig.catalog.internal.Messages;
import org.locationtech.udig.catalog.internal.ResolveManager;
import org.locationtech.udig.catalog.internal.ResolveManager2;
import org.locationtech.udig.catalog.internal.ServiceFactoryImpl;
import org.locationtech.udig.core.internal.ExtensionPointProcessor;
import org.locationtech.udig.core.internal.ExtensionPointUtil;
import org.locationtech.udig.ui.PreShutdownTask;
import org.locationtech.udig.ui.ProgressManager;
import org.locationtech.udig.ui.ShutdownTaskList;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
import org.osgi.framework.BundleContext;
import org.osgi.service.prefs.BackingStoreException;

/**
* The main plugin class to be used in the desktop.
*/
public class CatalogPlugin extends Plugin {

    private static final String EXTENSION_POINT_ICATALOG = "org.locationtech.udig.catalog.ICatalog";

    public static final String ID = "org.locationtech.udig.catalog"; //$NON-NLS-1$

    // The shared instance.
    private static CatalogPlugin plugin;

    // Resource bundle.
    private ResourceBundle resourceBundle;

    /** Local catalog used as a repository to manage servies that are known the the client */
    private CatalogImpl local;

    /**
     * Cache of catalogues (possibly remote) that may be searched for additional spatial information
     */
    private List<ISearch> catalogs;

    /**
     * Service factory ued to construct new IService instances allowing connection to spatial
     * information.
     */
    private IServiceFactory serviceFactory;

    /**
     * Plugin preferenceStore (from bundle) used to store catalog information.
     */
    private IPreferenceStore preferenceStore;

    /**
     * ResolveManager used to allow plugins to teach new resolve targets to existing IService and
     * IGeoResoruce implementations.
     */
    private volatile IResolveManager resolveManager;


    /** Lock used to protect map of available services; for the last call? */
    private Lock registeredLock = new ReentrantLock();

    /**
     * Map of ServiceExtension by "id", access control policed by above "lock".
     */
    Map<String, ServiceExtension> registered = null; // lazy creation
   
    /**
     * The constructor. See {@link #start(BundleContext)} for the initialization of the plugin.
     */
    public CatalogPlugin() {
        super();
        plugin = this;
    }

    /**
     * This method is called upon plug-in activation
     */
    public void start( BundleContext context ) throws Exception {
        super.start(context);
        local = new CatalogImpl();
        catalogs = Collections.emptyList();
        serviceFactory = new ServiceFactoryImpl();
        resolveManager = new ResolveManager2();
       
        // ensure a preference store is around so we can save to it in the shutdown hook
        preferenceStore = new ScopedPreferenceStore(new InstanceScope(), getBundle().getSymbolicName());

        try {
            if (Display.getCurrent() != null) {
                CatalogPlugin.trace("Restoring Local Catalog", null);
            }
            plugin.restoreFromPreferences();
        } catch (Throwable e) {
            CatalogPlugin.log("Unable to restore catalog:"+e, e);
            handlerLoadingError(e);
        }
        addSaveLocalCatalogShutdownHook();
    }

    private void addSaveLocalCatalogShutdownHook() {
        ShutdownTaskList.instance().addPreShutdownTask(new PreShutdownTask(){

            public int getProgressMonitorSteps() {
                try {
                    return getLocalCatalog().members(ProgressManager.instance().get()).size();
                } catch (IOException e) {
                    return 0;
                }
            }

            public boolean handlePreShutdownException( Throwable t, boolean forced ) {
                CatalogPlugin.log("Error storing local catalog", t); //$NON-NLS-1$
                return true;
            }

            public boolean preShutdown( IProgressMonitor monitor, IWorkbench workbench,
                    boolean forced ) throws Exception {
                ISearch[] toDispose = getCatalogs();
                monitor.beginTask(Messages.CatalogPlugin_SavingCatalog, 4 + (4 * toDispose.length));
                SubProgressMonitor subProgressMonitor = new SubProgressMonitor(monitor, 4);
                storeToPreferences(subProgressMonitor);
                subProgressMonitor.done();
                for( ISearch catalog : toDispose ) {
                    subProgressMonitor = new SubProgressMonitor(monitor, 4);
                    catalog.dispose(subProgressMonitor);
                    subProgressMonitor.done();
                }
                return true;
            }

        });
    }

    /**
     * Opens a dialog warning the user that an error occurred while loading the local catalog
     *
     * @param e the exception that occurred
     */
    private void handlerLoadingError( Throwable e ) {
        try {
            File backup = new File(getLocalCatalogFile().getParentFile(), "corruptedLocalCatalog"); //$NON-NLS-1$
            copy(getLocalCatalogFile(), backup);
        } catch (IOException ioe) {
            log("Coulding make a back up of the corrupted local catalog", ioe); //$NON-NLS-1$
        }
        boolean addShutdownHook = MessageDialog.openQuestion(Display.getDefault().getActiveShell(),
                Messages.CatalogPlugin_ErrorLoading, Messages.CatalogPlugin__ErrorLoadingMessage);
        if (addShutdownHook) {
            addSaveLocalCatalogShutdownHook();
        }
    }

    private void copy( File file, File backup ) throws IOException {
        FileChannel in = new FileInputStream(file).getChannel(), out = new FileOutputStream(backup)
                .getChannel();
        final int BSIZE = 1024;
        ByteBuffer buffer = ByteBuffer.allocate(BSIZE);
        while( in.read(buffer) != -1 ) {
            buffer.flip(); // Prepare for writing
            out.write(buffer);
            buffer.clear(); // Prepare for reading
        }
    }

    /**
     * Cleanup after shared images.
     * <p>
     *
     * @see addSaveLocalCatalogShutdownHook
     * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
     * @param context
     * @throws Exception
     */
    public void stop( BundleContext context ) throws Exception {
        super.stop(context);
        plugin = null;
        resourceBundle = null;
    }

    /**
     * Load the getLocalCatalogFile() into the local catalog() and restore any external catalogs.
     */
    public void restoreFromPreferences() {
        try {
            if( getLocalCatalog() instanceof CatalogImpl){
                CatalogImpl localCatalog = (CatalogImpl) getLocalCatalog();
                File catalogFile = getLocalCatalogFile();
                IServiceFactory serviceFactory = CatalogPlugin.getDefault().getServiceFactory();
                localCatalog.loadFromFile(catalogFile, serviceFactory);
            }
        } catch (Throwable t) {
            CatalogPlugin.log("Trouble restoring local catalog:"+t, t);
        }
        try {
            loadCatalogs();
        }
        catch (Throwable t) {
            CatalogPlugin.log("Trouble connectin remote catalogs:"+t, t);
        }
    }
    /**
     * Go through and load the "external" catalogs ... this is used to
     * populate getCatalogs() and the result is cached.
     */
    private List<ISearch> loadCatalogs() {
        final List<ISearch> availableCatalogs = new LinkedList<ISearch>();
        ExtensionPointUtil.process(getDefault(),
            EXTENSION_POINT_ICATALOG, new ExtensionPointProcessor(){
                public void process( IExtension extension, IConfigurationElement element )
                        throws Exception {
                    ISearch externalCatalog = (ISearch) element.createExecutableExtension("class");
                    availableCatalogs.add(externalCatalog); //$NON-NLS-1$                
                }
            }
        );
      return availableCatalogs;
    }

    public void storeToPreferences( IProgressMonitor monitor ) throws BackingStoreException,
            IOException {
        ((CatalogImpl) getLocalCatalog()).saveToFile(getLocalCatalogFile(), getServiceFactory(),
                monitor);
    }
    /**
     * File used to load/save the local catalog.
     *
     * @return
     * @throws IOException
     */
    private File getLocalCatalogFile() throws IOException {
        // working directory for the application as a file
        File userLocation = new File(FileLocator.toFileURL(Platform.getInstanceLocation().getURL())
                .getFile());
        // will create the file if needed
        if (!userLocation.exists())
            userLocation.mkdirs();
        // local catalog saved in working directory/.localCatalog
        File catalogLocation = new File(userLocation, ".localCatalog"); //$NON-NLS-1$
        return catalogLocation;
    }

    /**
     * Returns the shared instance.
     */
    public static CatalogPlugin getDefault() {
        return plugin;
    }
    /**
     * Add a catalog listener for changed to this catalog.
     *
     * @param listener
     */
    public static void addListener( IResolveChangeListener listener ) {
        ((CatalogImpl) getDefault().getLocalCatalog()).addCatalogListener(listener);
    }

    /**
     * Remove a catalog listener that was interested in this catalog.
     *
     * @param listener
     */
    public static void removeListener( IResolveChangeListener listener ) {
        ((CatalogImpl) getDefault().getLocalCatalog()).removeCatalogListener(listener);
    }

    /**
     * Returns the string from the plugin's resource bundle, or 'key' if not found.
     */
    public static String getResourceString( String key ) {
        ResourceBundle bundle = CatalogPlugin.getDefault().getResourceBundle();
        try {
            return (bundle != null) ? bundle.getString(key) : key;
        } catch (MissingResourceException e) {
            return key;
        }
    }

    /**
     * Returns the plugin's resource bundle,
     */
    public ResourceBundle getResourceBundle() {
        try {
            if (resourceBundle == null)
                resourceBundle = ResourceBundle
                        .getBundle("org.locationtech.udig.catalog.CatalogPluginResources"); //$NON-NLS-1$
        } catch (MissingResourceException x) {
            resourceBundle = null;
        }
        return resourceBundle;
    }
    /**
     * Available catalogs supporting the ISearch interface. Please note that not all of these
     * catalogs are "local"; although the first [0] one is the same as getLocal().
     *
     * @return Returns the catalogs found ... local one is slot[0].
     */
  public synchronized ISearch[] getCatalogs() {
    if (catalogs == null) {
      // look up all catalogs and populate catalogs
      catalogs = loadCatalogs();
    }
    List<ISearch> c = new ArrayList<ISearch>();
    c.add(local);
    c.addAll(catalogs);
    return c.toArray(new ISearch[c.size()]);
  }

    public synchronized void addSearchCatalog(ISearch searchCatalog) {
      // force init
      getCatalogs();
     
      catalogs.add(searchCatalog);
    }
    /**
     * @return the local catalog. Equivalent to getCatalogs()[0]
     */
    public ICatalog getLocalCatalog() {
        return local;
    }
    /**
     * Access to the local repository which you can add/remove services to.
     * <p>
     * The local repository is used to reflect what servies are known to the application. In
     * particular it is used to track all services that are "connected" so that the application can
     * clean up (ie call dispose) on these services when it is being shutdown.
     * <p>
     * This repository is persisted (currently the the contents are saved into the catalog plugin
     * preferencestore, although a successful geoserver implementation indicates that the use of a
     * hibernate is also appropriate).
     *
     * @return Local repository used to manage known serivces and all connected services.
     */
    public IRepository getLocal() {
        return local;
    }
    /**
     * Service factory used to connect to new services.
     * <p>
     * Serivces hold on to live connections; please take care to call dispose() or to add the
     * service to the local repository so that the service is disposed when the application shuts
     * down.
     *
     * @return Returns the serviceFactory.
     */
    public IServiceFactory getServiceFactory() {
        return serviceFactory;
    }
    /**
     * List of registered {@link ServiceExtension}s.
     * @return Registered {@link ServiceExtension}
     */
    public List<ServiceExtension> getServiceExtensions(){
        List<ServiceExtension> list = new ArrayList<ServiceExtension>( getRegisteredExtensions().values() );
        return Collections.unmodifiableList( list );
    }
    /**
     * Register a service extension by hand.
     *
     * @param id
     * @param extension
     */
    public void register( String id, ServiceExtension extension ){
        try {
            registeredLock.lock();
            getRegisteredExtensions();
            if( registered.containsKey(id)){
                ServiceExtension existing = registered.get(id);
                String exsistingName = existing != null ? existing.getClass().getSimpleName() : null;
                throw new IllegalStateException("ServiceExtension "+id+" already registered with "+exsistingName );
            }
           
            registered.put( id, extension );
        } finally {
            registeredLock.unlock();
        }
    }
    /**
     * Internal method to lazily process the {@link #CATALOG_SERVICE_EXTENSION}
     * @return Map of {@link ServiceExtension} by id
     */
    Map<String, ServiceExtension> getRegisteredExtensions() {
        try {
            registeredLock.lock();
            if (registered == null) { // load available
                // we are going to sort the map so that "generic" fallback datastores are selected last
                //
                registered = new HashMap<String, ServiceExtension>();
                ExtensionPointUtil.process(CatalogPlugin.getDefault(),
                        ServiceExtension.EXTENSION_ID, new ExtensionPointProcessor() { //$NON-NLS-1$
                            public void process(IExtension extension, IConfigurationElement element) throws Exception {
                                // extentionIdentifier used to report any problems;
                                // in the event of failure we want to be able to report
                                // who had the problem
                                // String extensionId = extension.getUniqueIdentifier();
                                String id = element.getAttribute("id");
                                ServiceExtension se = (ServiceExtension) element.createExecutableExtension("class");
                                if (id == null || id.length() == 0) {
                                    id = se.getClass().getSimpleName();
                                }
                                registered.put(id, se);
                            }
                        });
            }
            return registered;
        } finally {
            registeredLock.unlock();
        }
    }
    /** Look up a specific implementation; used mostly for test cases */
    public <E extends ServiceExtension> E serviceImplementation( Class<E> implementation ) {
        for( Map.Entry<String, ServiceExtension> entry : getRegisteredExtensions().entrySet() ) {
            String id = entry.getKey();
            ServiceExtension serviceExtension = entry.getValue();

            if (id == null || serviceExtension == null)
                continue;
            if (implementation.isInstance(serviceExtension)) {
                return implementation.cast(serviceExtension);
            }
        }
        return null;
    }
    /** Look up a specific implementation; used mostly for test cases */
    public ServiceExtension serviceImplementation( String serviceExtensionId ) {
        for( Map.Entry<String, ServiceExtension> entry : getRegisteredExtensions().entrySet() ) {
            String id = entry.getKey();
            ServiceExtension serviceExtension = entry.getValue();

            if (id == null || serviceExtension == null)
                continue;
            if (serviceExtensionId.equalsIgnoreCase(id)) {
                return serviceExtension;
            }
        }
        return null;
    }
    /**
     * Attempts to turn data into a URL. If data is an array or Collection, it will return the first
     * successful URL it can find. This is a utility method. Feel free to move it to another class.
     * In the future, it might be nice to have it return a List of the URLs it found, not just the
     * first one.
     *
     * @param data
     * @return a URL if it can find one, or null otherwise
     * @deprecated Please use ID.cast( data ).toURL();
     */
    public static URL locateURL( Object data ) {
        ID id = org.locationtech.udig.catalog.ID.cast(data);
        if (id == null) {
            return null;
        }
        return id.toURL();
    }

    /**
     * Helper method to log a message in the plugin's log.
     * <p>
     * This will be a user visable ERROR iff:
     * <ul>
     * <li>t is an Exception we are assuming it is human readable or if a message is provided</li>
     * </ul>
     *
     * @param message log message
     * @param t Throwable causing the message; if this is an exception an error will be logged
     */
    public static void log( String message, Throwable t ) {
        String msg = message == null ? "" : message;
        int status = t instanceof Exception || message != null ? IStatus.ERROR : IStatus.INFO;
        getDefault().getLog().log(new Status(status, ID, IStatus.OK, msg, t));
    }
    /**
     * Messages that only engage if getDefault().isDebugging()
     * <p>
     * It is much prefered to do this:
     *
     * <pre><code>
     * private static final String RENDERING = &quot;org.locationtech.udig.project/render/trace&quot;;
     * if (ProjectUIPlugin.getDefault().isDebugging() &amp;&amp; &quot;true&quot;.equalsIgnoreCase(RENDERING)) {
     *     System.out.println(&quot;your message here&quot;);
     * }
     */
    public static void trace( String message, Throwable e ) {
        if (getDefault().isDebugging()) {
            if (message != null)
                System.out.println(message);
            if (e != null)
                e.printStackTrace();
        }
    }
    /**
     * Performs the Platform.getDebugOption true check on the provided trace
     * <p>
     * Note: ProjectUIPlugin.getDefault().isDebugging() must also be on.
     * <ul>
     * <li>Trace.RENDER - trace rendering progress
     * </ul>
     * </p>
     *
     * @param trace currently only RENDER is defined
     */
    public static boolean isDebugging( final String trace ) {
        return getDefault().isDebugging()
                && "true".equalsIgnoreCase(Platform.getDebugOption(trace)); //$NON-NLS-1$   
    }

    /**
     * Returns the preference store for this UI plug-in. This preference store is used to hold
     * persistent settings for this plug-in in the context of a workbench. Some of these settings
     * will be user controlled, whereas others may be internal setting that are never exposed to the
     * user.
     * <p>
     * If an error occurs reading the preference store, an empty preference store is quietly
     * created, initialized with defaults, and returned.
     * </p>
     * <p>
     * <strong>NOTE:</strong> As of Eclipse 3.1 this method is no longer referring to the core
     * runtime compatibility layer and so plug-ins relying on Plugin#initializeDefaultPreferences
     * will have to access the compatibility layer themselves.
     * </p>
     *
     * @return the preference store
     */
    public IPreferenceStore getPreferenceStore() {
        return preferenceStore;
    }
    /**
     * ResolveManager used to allow plugins to teach new resolve targets to existing IService and
     * IGeoResoruce implementations.
     *
     * @return IResolveManager
     */
    public IResolveManager getResolveManager() {
        return resolveManager;
    }

}
TOP

Related Classes of org.locationtech.udig.catalog.CatalogPlugin

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.