/* * uDig - User Friendly Desktop Internet GIS client * http://udig.refractions.net * (C) 2004, Refractions Research Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. * */ package net.refractions.udig.catalog.wfs.cache; import java.io.IOException; import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import net.refractions.udig.catalog.CatalogPlugin; import net.refractions.udig.catalog.IResolve; import net.refractions.udig.catalog.IResolveChangeEvent; import net.refractions.udig.catalog.IResolveDelta; import net.refractions.udig.catalog.IService; import net.refractions.udig.catalog.IServiceInfo; import net.refractions.udig.catalog.internal.CatalogImpl; import net.refractions.udig.catalog.internal.ResolveChangeEvent; import net.refractions.udig.catalog.internal.ResolveDelta; import net.refractions.udig.catalog.wfs.cache.internal.Messages; import net.refractions.udig.ui.ErrorManager; import net.refractions.udig.ui.UDIGDisplaySafeLock; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.geotools.data.wfs.WFSDataStore; import org.geotools.data.wfs.WFSDataStoreFactory; import org.geotools.xml.wfs.WFSSchema; /** * Handle for a WFS service. * * @author David Zwiers, Refractions Research * @since 0.6 */ public class WFScServiceImpl extends IService { private URL identifier = null; private Map<String, Serializable> params = null; protected Lock rLock = new UDIGDisplaySafeLock(); /** * The key for the type of cache to use. * <p> * The associated value should be one of: * <li>CACHE_MEMORY</li> * <li>CACHE_DISK</li> * <li>CACHE_NONE</li> */ public static final String CACHE_TYPE_KEY = "CACHE_TYPE"; //$NON-NLS-1$ /** * The location of the cache if a disk cache. */ public static final String CACHE_DIR_KEY = "CACHE_DIR"; //$NON-NLS-1$ /** * The size of the page size. */ public static final String CACHE_PAGE_SIZE_KEY = "CACHE_PAGE_SIZE"; //$NON-NLS-1$ /** * The size of the grid cache. * <p> * If the feature source has a lot of big feature you'll want the index size to be large. If the * feature source has a lot of small features you'll want the index size to be smaller. The * <i>ideal</i> size is twice the mean size of the data. * </p> * <p> * This value should be the total number of tiles excpected. If you want 10x10 grid then this * connection parameter should be set to 100. * </p> */ public static final String GRID_CACHE_SIZE_KEY = "GRID_CACHE_SIZE"; //$NON-NLS-1$ /** * The maximum number of features allowed in the cache. * <p> * If not specified the default is Integer.MAX_VALUE; * </p> */ public static final String CACHE_FEATURE_SIZE_KEY = "CACHE_FEATURE_SIZE"; //$NON-NLS-1$ /** * Key for In Memeory cache. */ public static final String CACHE_MEMORY = "inmemory"; //$NON-NLS-1$ /** * Key for OnDisk cache */ public static final String CACHE_DISK = "ondisk"; //$NON-NLS-1$ /** * Key for no cache */ public static final String CACHE_NONE = "nocache"; //$NON-NLS-1$ public WFScServiceImpl( URL identifier, Map<String, Serializable> dsParams ) { this.identifier = identifier; this.params = dsParams; } /* * Required adaptions: <ul> <li>IServiceInfo.class <li>List.class <IGeoResource> </ul> * @see net.refractions.udig.catalog.IService#resolve(java.lang.Class, * org.eclipse.core.runtime.IProgressMonitor) */ public <T> T resolve( Class<T> adaptee, IProgressMonitor monitor ) throws IOException { if (adaptee == null) return null; if (adaptee.isAssignableFrom(WFScDataStore.class)) { return adaptee.cast(getDS(monitor)); } return super.resolve(adaptee, monitor); } /* * @see net.refractions.udig.catalog.IResolve#canResolve(java.lang.Class) */ public <T> boolean canResolve( Class<T> adaptee ) { if (adaptee == null) return false; return adaptee.isAssignableFrom(WFSDataStore.class) || super.canResolve(adaptee); } public void dispose( IProgressMonitor monitor ) { if (members != null) { int steps = (int) ((double) 99 / (double) members.size()); for( IResolve resolve : members ) { try { SubProgressMonitor subProgressMonitor = new SubProgressMonitor(monitor, steps); resolve.dispose(subProgressMonitor); subProgressMonitor.done(); } catch (Throwable e) { ErrorManager.get().displayException(e,"Error disposing members of service: " + getIdentifier(), CatalogPlugin.ID); //$NON-NLS-1$ } } } if (this.ds != null){ this.ds.dispose(); } } /* * @see net.refractions.udig.catalog.IResolve#members(org.eclipse.core.runtime.IProgressMonitor) */ public List<WFScGeoResourceImpl> resources( IProgressMonitor monitor ) throws IOException { if (members == null) { rLock.lock(); try { if (members == null) { getDS(monitor); // load ds members = new LinkedList<WFScGeoResourceImpl>(); String[] typenames = ds.getTypeNames(); if (typenames != null) for( int i = 0; i < typenames.length; i++ ) { try { members.add(new WFScGeoResourceImpl(this, typenames[i])); } catch (Exception e) { WfsCachePlugin.log("", e); //$NON-NLS-1$ } } } } finally { rLock.unlock(); } } return members; } private volatile List<WFScGeoResourceImpl> members = null; /* * @see net.refractions.udig.catalog.IService#getInfo(org.eclipse.core.runtime.IProgressMonitor) */ public IServiceInfo createInfo( IProgressMonitor monitor ) throws IOException { getDS(monitor); // load ds if (info == null && ds != null) { rLock.lock(); try { if (info == null) { info = new IServiceWFSInfo(ds); IResolveDelta delta = new ResolveDelta(this, IResolveDelta.Kind.CHANGED); ((CatalogImpl) CatalogPlugin.getDefault().getLocalCatalog()) .fire(new ResolveChangeEvent(this, IResolveChangeEvent.Type.POST_CHANGE, delta)); } } finally { rLock.unlock(); } } return info; } private volatile IServiceInfo info = null; /* * @see net.refractions.udig.catalog.IService#getConnectionParams() */ public Map<String, Serializable> getConnectionParams() { return params; } private Throwable msg = null; private volatile WFSDataStoreFactory dsf; // private volatile WFSDataStore ds = null; private volatile WFScDataStore ds = null; private static final Lock dsLock = new UDIGDisplaySafeLock(); WFScDataStore getDS(IProgressMonitor monitor) throws IOException { if (ds == null) { if (monitor == null) monitor = new NullProgressMonitor(); monitor.beginTask(Messages.WFScServiceImpl_task_name, 3); dsLock.lock(); monitor.worked(1); try { if (ds == null) { if (dsf == null) { dsf = new WFSDataStoreFactory(); } monitor.worked(1); if (dsf.canProcess(params)) { monitor.worked(1); try { String cacheType = (String)params.get(WFScServiceImpl.CACHE_TYPE_KEY); //lets try to connect to the WFSDataStore //HACK: explicitly ask for WFS 1.0 URL url = (URL)params.get(WFSDataStoreFactory.URL.key); url = WFSDataStoreFactory.createGetCapabilitiesRequest(url); params = new HashMap<String, Serializable>(params); params.put(WFSDataStoreFactory.URL.key, url); IOException ex = null; try{ ds = new WFScDataStore((WFSDataStore) dsf.createDataStore(params), params); }catch (IOException e){ ex = e; } //lets check the cache and see if we can find anything cached if (ds == null && ex != null && cacheType != null && cacheType.equals(WFScServiceImpl.CACHE_DISK)){ try{ ds = new DiskCacheDataStore(params); }catch (IOException e){ WfsCachePlugin.log("Cannot connect to wfs server, will try to find a local cache of the data.", e); //$NON-NLS-1$ ex = e; } if (ds == null){ if (ex != null) throw ex; else throw new IOException("Unable to connecto to WFS server or cache."); //$NON-NLS-1$ } } monitor.worked(1); } catch (IOException e) { msg = e; throw e; } } } } finally { dsLock.unlock(); monitor.done(); } IResolveDelta delta = new ResolveDelta(this, IResolveDelta.Kind.CHANGED); ((CatalogImpl) CatalogPlugin.getDefault().getLocalCatalog()) .fire(new ResolveChangeEvent(this, IResolveChangeEvent.Type.POST_CHANGE, delta)); } return ds; } /* * @see net.refractions.udig.catalog.IResolve#getStatus() */ public Status getStatus() { return msg != null ? Status.BROKEN : ds == null ? Status.NOTCONNECTED : Status.CONNECTED; } /* * @see net.refractions.udig.catalog.IResolve#getMessage() */ public Throwable getMessage() { return msg; } /* * @see net.refractions.udig.catalog.IResolve#getIdentifier() */ public URL getIdentifier() { return identifier; } private class IServiceWFSInfo extends IServiceInfo { private WFScDataStore ds; IServiceWFSInfo( WFScDataStore resource ) { super(); this.ds = resource; icon = AbstractUIPlugin.imageDescriptorFromPlugin(WfsCachePlugin.PLUGIN_ID, "icons/obj16/wfs_obj.16"); //$NON-NLS-1$ } /* * @see net.refractions.udig.catalog.IServiceInfo#getAbstract() */ public String getAbstract() { return ds.getInfo().getDescription(); } /* * @see net.refractions.udig.catalog.IServiceInfo#getKeywords() */ public Set<String> getKeywords() { return ds.getInfo().getKeywords(); } /* * @see net.refractions.udig.catalog.IServiceInfo#getSchema() */ public URI getSchema() { return WFSSchema.NAMESPACE; } public String getDescription() { return getIdentifier().toString(); } public URI getSource() { try { return getIdentifier().toURI(); } catch (URISyntaxException e) { // This would be bad throw (RuntimeException) new RuntimeException().initCause(e); } } public String getTitle() { String title = ds.getInfo().getTitle(); if (title == null) { title = getIdentifier() == null ? Messages.WFScServiceImpl_broken : getIdentifier() .toString(); } else { title += " (WFS " + ds.getInfo().getVersion() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ } title = "Cached " + title; //$NON-NLS-1$ return title; } } }