001: /*
002: * uDig - User Friendly Desktop Internet GIS client http://udig.refractions.net (C) 2004,
003: * Refractions Research Inc. This library is free software; you can redistribute it and/or modify it
004: * under the terms of the GNU Lesser General Public License as published by the Free Software
005: * Foundation; version 2.1 of the License. This library is distributed in the hope that it will be
006: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
007: * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
008: */
009: package net.refractions.udig.catalog.internal.wms;
010:
011: import static net.refractions.udig.catalog.internal.wms.Trace.REQUEST;
012:
013: import java.io.IOException;
014: import java.io.Serializable;
015: import java.net.URI;
016: import java.net.URL;
017: import java.text.MessageFormat;
018: import java.util.LinkedList;
019: import java.util.List;
020: import java.util.Map;
021: import java.util.concurrent.locks.Lock;
022:
023: import net.refractions.udig.catalog.CatalogPlugin;
024: import net.refractions.udig.catalog.IResolve;
025: import net.refractions.udig.catalog.IResolveChangeEvent;
026: import net.refractions.udig.catalog.IResolveDelta;
027: import net.refractions.udig.catalog.IService;
028: import net.refractions.udig.catalog.IServiceInfo;
029: import net.refractions.udig.catalog.internal.CatalogImpl;
030: import net.refractions.udig.catalog.internal.ResolveChangeEvent;
031: import net.refractions.udig.catalog.internal.ResolveDelta;
032: import net.refractions.udig.catalog.wms.internal.Messages;
033: import net.refractions.udig.ui.ErrorManager;
034: import net.refractions.udig.ui.UDIGDisplaySafeLock;
035:
036: import org.eclipse.core.runtime.IProgressMonitor;
037: import org.eclipse.core.runtime.SubProgressMonitor;
038: import org.eclipse.jface.resource.ImageDescriptor;
039: import org.eclipse.ui.plugin.AbstractUIPlugin;
040: import org.geotools.data.ows.GetCapabilitiesRequest;
041: import org.geotools.data.ows.GetCapabilitiesResponse;
042: import org.geotools.data.ows.Layer;
043: import org.geotools.data.ows.Specification;
044: import org.geotools.data.ows.WMSCapabilities;
045: import org.geotools.data.wms.WMS1_0_0;
046: import org.geotools.data.wms.WMS1_1_0;
047: import org.geotools.data.wms.WMS1_1_1;
048: import org.geotools.data.wms.WMSUtils;
049: import org.geotools.data.wms.WebMapServer;
050: import org.geotools.data.wms.request.GetFeatureInfoRequest;
051: import org.geotools.data.wms.request.GetMapRequest;
052: import org.geotools.data.wms.response.GetFeatureInfoResponse;
053: import org.geotools.data.wms.response.GetMapResponse;
054: import org.geotools.data.wms.xml.WMSSchema;
055: import org.geotools.ows.ServiceException;
056: import org.xml.sax.SAXException;
057:
058: /**
059: * Connect to a WMS.
060: *
061: * @author David Zwiers, Refractions Research
062: * @since 0.6
063: */
064: public class WMSServiceImpl extends IService {
065:
066: /**
067: * <code>WMS_URL_KEY</code> field
068: * Magic param key for Catalog WMS persistence.
069: */
070: public static final String WMS_URL_KEY = "net.refractions.udig.catalog.internal.wms.WMSServiceImpl.WMS_URL_KEY"; //$NON-NLS-1$
071: public static final String WMS_WMS_KEY = "net.refractions.udig.catalog.internal.wms.WMSServiceImpl.WMS_WMS_KEY"; //$NON-NLS-1$
072:
073: private Map<String, Serializable> params;
074:
075: private Throwable error;
076: private URL url;
077:
078: /**
079: * Construct <code>WMSServiceImpl</code>.
080: *
081: * @param url
082: * @param params
083: */
084: public WMSServiceImpl(URL url, Map<String, Serializable> params) {
085: this .params = params;
086: this .url = url;
087: //System.out.println("WMS "+url);
088: if (params.containsKey(WMS_WMS_KEY)) {
089: Object obj = params.get(WMS_WMS_KEY);
090:
091: if (obj instanceof WebMapServer) {
092: this .wms = (WebMapServer) obj;
093: }
094: }
095: }
096:
097: public Status getStatus() {
098: return error != null ? Status.BROKEN
099: : wms == null ? Status.NOTCONNECTED : Status.CONNECTED;
100: }
101:
102: private static final Lock dsLock = new UDIGDisplaySafeLock();
103:
104: /**
105: * Aquire the actual geotools WebMapServer instance.
106: * <p>
107: * Note this method is blocking and throws an IOException to indicate such.
108: * </p>
109: * @param theUserIsWatching
110: * @return WebMapServer instance
111: * @throws IOException
112: */
113: protected WebMapServer getWMS(IProgressMonitor theUserIsWatching)
114: throws IOException {
115: if (wms == null) {
116: dsLock.lock();
117: try {
118: if (wms == null) {
119: try {
120: if (theUserIsWatching != null) {
121: String message = MessageFormat
122: .format(
123: Messages.WMSServiceImpl_connecting_to,
124: new Object[] { url });
125: theUserIsWatching.beginTask(message, 100);
126: }
127: URL url1 = (URL) getConnectionParams().get(
128: WMS_URL_KEY);
129: if (theUserIsWatching != null)
130: theUserIsWatching.worked(5);
131: wms = new CustomWMS(url1);
132: if (theUserIsWatching != null)
133: theUserIsWatching.done();
134: } catch (IOException persived) {
135: error = persived;
136: throw persived;
137: } catch (Throwable nak) {
138:
139: IOException broken = new IOException(
140: MessageFormat
141: .format(
142: Messages.WMSServiceImpl_could_not_connect,
143: new Object[] { nak
144: .getLocalizedMessage() }));
145: broken.initCause(nak);
146: error = broken;
147: throw broken;
148: }
149: }
150: } finally {
151: dsLock.unlock();
152: }
153: }
154: return wms;
155: }
156:
157: private volatile WebMapServer wms = null;
158: private volatile WMSServiceInfo info;
159: protected Lock rLock = new UDIGDisplaySafeLock();
160:
161: public IServiceInfo getInfo(IProgressMonitor monitor)
162: throws IOException {
163: if (info == null) {
164: getWMS(monitor);
165: rLock.lock();
166: try {
167: if (info == null) {
168: info = new WMSServiceInfo(monitor);
169: }
170: } finally {
171: rLock.unlock();
172: }
173: }
174: return info;
175: }
176:
177: /*
178: * @see net.refractions.udig.catalog.IService#resolve(java.lang.Class, org.eclipse.core.runtime.IProgressMonitor)
179: */
180: public <T> T resolve(Class<T> adaptee, IProgressMonitor monitor)
181: throws IOException {
182: if (adaptee == null) {
183: return null;
184: }
185:
186: if (adaptee.isAssignableFrom(IServiceInfo.class)) {
187: return adaptee.cast(getInfo(monitor));
188: }
189:
190: if (adaptee.isAssignableFrom(List.class)) {
191: return adaptee.cast(members(monitor));
192: }
193:
194: if (adaptee.isAssignableFrom(WebMapServer.class)) {
195: return adaptee.cast(getWMS(monitor));
196: }
197:
198: return super .resolve(adaptee, monitor);
199: }
200:
201: /**
202: * @see net.refractions.udig.catalog.IService#getConnectionParams()
203: */
204: public Map<String, Serializable> getConnectionParams() {
205: return params;
206: }
207:
208: /*
209: * @see net.refractions.udig.catalog.IResolve#canResolve(java.lang.Class)
210: */
211: public <T> boolean canResolve(Class<T> adaptee) {
212: if (adaptee == null)
213: return false;
214:
215: return adaptee.isAssignableFrom(WebMapServer.class)
216: || super .canResolve(adaptee);
217: }
218:
219: public void dispose(IProgressMonitor monitor) {
220: if (members == null)
221: return;
222:
223: int steps = (int) ((double) 99 / (double) members.size());
224: for (IResolve resolve : members) {
225: try {
226: SubProgressMonitor subProgressMonitor = new SubProgressMonitor(
227: monitor, steps);
228: resolve.dispose(subProgressMonitor);
229: subProgressMonitor.done();
230: } catch (Throwable e) {
231: ErrorManager
232: .get()
233: .displayException(
234: e,
235: "Error disposing members of service: " + getIdentifier(), CatalogPlugin.ID); //$NON-NLS-1$
236: }
237: }
238: }
239:
240: /*
241: * @see net.refractions.udig.catalog.IResolve#members(org.eclipse.core.runtime.IProgressMonitor)
242: */
243: public List<WMSGeoResourceImpl> members(IProgressMonitor monitor)
244: throws IOException {
245:
246: if (members == null) {
247: getWMS(monitor);
248: rLock.lock();
249: try {
250: if (members == null) {
251: getWMS(monitor); // load ds
252: members = new LinkedList<WMSGeoResourceImpl>();
253: Layer[] layers = WMSUtils.getNamedLayers(getWMS(
254: monitor).getCapabilities());
255: /*
256: * Retrieved no layers from the WMS - something is wrong,
257: * either the WMS doesn't work, or it has no named layers.
258: */
259: if (layers != null) {
260: for (int i = 0; i < layers.length; i++) {
261: /*
262: * suppress layers that have children
263: * TODO some people might not like this behavior
264: * maybe we should make a preference for it.
265: * TODO should add hasChildren() to geotools
266: */
267: if (layers[i].getChildren().length == 0) {
268: Layer layer = layers[i];
269: members.add(new WMSGeoResourceImpl(
270: this , layer));
271: }
272: }
273: }
274: }
275: } finally {
276: rLock.unlock();
277: }
278: }
279: return members;
280: }
281:
282: private volatile List<WMSGeoResourceImpl> members = null;
283:
284: /*
285: * @see net.refractions.udig.catalog.IResolve#getMessage()
286: */
287: public Throwable getMessage() {
288: return error;
289: }
290:
291: /*
292: * @see net.refractions.udig.catalog.IResolve#getIdentifier()
293: */
294: public URL getIdentifier() {
295: return url;
296: }
297:
298: class WMSServiceInfo extends IServiceInfo {
299: WMSServiceInfo(IProgressMonitor monitor) {
300: try {
301: caps = getWMS(monitor).getCapabilities();
302: } catch (Throwable t) {
303: t.printStackTrace();
304: caps = null;
305: }
306:
307: keywords = caps == null ? null
308: : caps.getService() == null ? null : caps
309: .getService().getKeywordList();
310:
311: String[] t;
312: if (keywords == null) {
313: t = new String[2];
314: } else {
315: t = new String[keywords.length + 2];
316: System.arraycopy(keywords, 0, t, 2, keywords.length);
317: }
318: t[0] = "WMS"; //$NON-NLS-1$
319: t[1] = getIdentifier().toString();
320: keywords = t;
321: }
322:
323: private WMSCapabilities caps = null;
324:
325: public String getAbstract() {
326: return caps == null ? null
327: : caps.getService() == null ? null : caps
328: .getService().get_abstract();
329: }
330:
331: /*
332: * @see net.refractions.udig.catalog.IServiceInfo#getIcon()
333: */
334: public ImageDescriptor getIcon() {
335: //return CatalogUIPlugin.getDefault().getImages().getImageDescriptor( ISharedImages.WMS_OBJ );
336: return AbstractUIPlugin.imageDescriptorFromPlugin(
337: WmsPlugin.ID, "icons/obj16/wms_obj.gif"); //$NON-NLS-1$
338: }
339:
340: public String getDescription() {
341: return getIdentifier().toString();
342: }
343:
344: public URI getSchema() {
345: return WMSSchema.NAMESPACE;
346: }
347:
348: public URL getSource() {
349: return getIdentifier();
350: }
351:
352: public String getTitle() {
353: return (caps == null || caps.getService() == null) ? (getIdentifier() == null ? Messages.WMSServiceImpl_broken
354: : getIdentifier().toString())
355: : caps.getService().getTitle();
356: }
357: }
358:
359: public static class CustomWMS extends WebMapServer {
360:
361: /**
362: * @throws SAXException
363: * @throws ServiceException
364: * @param serverURL
365: * @throws IOException
366: */
367: public CustomWMS(URL serverURL) throws IOException,
368: ServiceException, SAXException {
369: super (serverURL);
370: if (WmsPlugin.isDebugging(REQUEST))
371: System.out
372: .println("Connection to WMS located at: " + serverURL); //$NON-NLS-1$
373: if (getCapabilities() == null) {
374: throw new IOException(
375: "Unable to parse capabilities document."); //$NON-NLS-1$
376: }
377: }
378:
379: public GetCapabilitiesResponse issueRequest(
380: GetCapabilitiesRequest arg0) throws IOException,
381: ServiceException {
382: WmsPlugin.log(
383: "GetCapabilities: " + arg0.getFinalURL(), null); //$NON-NLS-1$
384: return super .issueRequest(arg0);
385: }
386:
387: public GetFeatureInfoResponse issueRequest(
388: GetFeatureInfoRequest arg0) throws IOException,
389: ServiceException {
390: WmsPlugin
391: .log("GetFeatureInfo: " + arg0.getFinalURL(), null); //$NON-NLS-1$
392: return super .issueRequest(arg0);
393: }
394:
395: public GetMapResponse issueRequest(GetMapRequest arg0)
396: throws IOException, ServiceException {
397: WmsPlugin.log("GetMap: " + arg0.getFinalURL(), null); //$NON-NLS-1$
398: return super .issueRequest(arg0);
399: }
400:
401: protected void setupSpecifications() {
402: specs = new Specification[3];
403: specs[0] = new WMS1_0_0();
404: specs[1] = new WMS1_1_0();
405: specs[2] = new WMS1_1_1();
406: }
407: }
408: }
|