001: /*
002: * uDig - User Friendly Desktop Internet GIS client
003: * http://udig.refractions.net
004: * (C) 2004, Refractions Research Inc.
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: */
017: package net.refractions.udig.catalog;
018:
019: import java.io.IOException;
020: import java.io.Serializable;
021: import java.util.List;
022: import java.util.Map;
023:
024: import net.refractions.udig.catalog.internal.Messages;
025: import net.refractions.udig.ui.ErrorManager;
026:
027: import org.eclipse.core.runtime.IProgressMonitor;
028: import org.eclipse.core.runtime.NullProgressMonitor;
029: import org.eclipse.core.runtime.SubProgressMonitor;
030:
031: /**
032: * Represents a geo spatial service handle. Follows the same design as IResource.
033: * <p>
034: * Represents a spatial service, which may be lazily loaded. The existance of this object does not
035: * ensure that the advertized data is guaranteed to exist, nor does this interface guarantee that
036: * the service exists based on this object's existance. We should also note the resource management
037: * is left to the user, and that resolve() is not guaranteed to return the same instance object from
038: * two subsequent calls, but may. This is merely a handle to some information about a service, and a
039: * method of aquiring an instance of the service ...
040: * </p>
041: * <p>
042: * NOTE: This may be the result of communications with a metadata service, and as such this service
043: * described may not be running right now. Remember to check the service status.
044: * </p>
045: *
046: * <h2>Implementing an IService</h2>
047: *
048: * Implement the abstract methods and you are good to go.
049: *
050: * <h2>Extending an IService</h2>
051: *
052: * You may want to implement your own IService in order to provide a handle for a new kind of <i>API</i>.
053: * <ol>
054: * <li>New method:<pre><code>
055: * public API getAPI( ProgressMonitor monitor){
056: * if (monitor == null) monitor = new NullProgressMonitor();
057: *
058: * monitor.beingTask("Connect to API",2);
059: * try {
060: * String server = getConnectionParams().get("server");
061: * monitor.worked(1);
062: * return new API( s );
063: * }
064: * finally {
065: * monitor.done();
066: * }
067: * }
068: * </pre></code>
069: * (note the use of NullProgressMonitor)
070: * </li>
071: *
072: * <li>Optional: Customize resolve method to advertise your new API "dynamically"<pre><code>
073: * public <T> boolean canResolve( Class<T> adaptee ) {
074: * return adaptee != null
075: * && (adaptee.isAssignableFrom(API.class) || super.canResolve(adaptee));
076: * }
077: * public <T> T resolve( Class<T> adaptee, IProgressMonitor monitor ) throws IOException {
078: * if (monitor == null) monitor = new NullProgressMonitor();
079: * if (adaptee == null)
080: * throw new NullPointerException("No adaptor specified" );
081: *
082: * if (adaptee.isAssignableFrom(API.class)) {
083: * return adaptee.cast(getAPI(monitor));
084: * }
085: * return super.resolve(adaptee, monitor);
086: * }
087: * </code></pre>
088: * (note the call to super)
089: * </li>
090: * <li>Optional: cache your API as the "connection"<pre><code>
091: * API api = null;
092: * Throwable msg = null;
093: * public synchronized API getAPI( ProgressMonitor monitor){
094: * if( api != null ) return api;
095: *
096: * if (monitor == null) monitor = new NullProgressMonitor();
097: *
098: * monitor.beingTask("Connect to API",2);
099: * try {
100: * String server = getConnectionParams().get("server");
101: * monitor.worked(1);
102: * api = new API( s );
103: * monitor.worked(1);
104: * return api;
105: * }
106: * finally {
107: * monitor.done();
108: * }
109: * }
110: * public Status getStatus() {
111: * return msg != null? Status.BROKEN : api == null? Status.NOTCONNECTED : Status.CONNECTED;
112: * }
113: * public Throwable getMessage(){
114: * return msg;
115: * }
116: * public synchronized void dispose( ProgressMonitor monitor ){
117: * if( api != null ){
118: * api.dispose();
119: * api = null;
120: * }
121: * if( msg != null ) msg = null;
122: * }
123: * </code></pre>
124: * (Note the use of getMessage and getStatus)
125: * </li>
126: * </ol>
127: *
128: * @author David Zwiers, Refractions Research
129: * @since 0.6
130: * @see IServiceInfo
131: * @see IServiceFactory
132: */
133: public abstract class IService implements IResolve {
134: /**
135: * Will attempt to morph into the adaptee, and return that object. Harded coded to capture the
136: * IService contract.
137: * <p>
138: * That is we *must* resolve the following:
139: * <ul>
140: * <li>IService: this
141: * <li>IServiceInfo.class: getInfo
142: * <li>ICatalog.class: parent
143: * </ul>
144: * Recomended adaptations:
145: * <ul>
146: * <li>ImageDescriptor.class: for a custom icon
147: * <li>List.class: members
148: * </ul>
149: * May Block.
150: * <p>
151: * Example implementation:<pre><code>
152: * public <T> T resolve( Class<T> adaptee, IProgressMonitor monitor ) throws IOException {
153: * if (monitor == null) monitor = new NullProgressMonitor();
154: * if (adaptee == null)
155: * throw new NullPointerException("No adaptor specified" );
156: *
157: * if (adaptee.isAssignableFrom(API.class)) {
158: * return adaptee.cast(getAPI(monitor));
159: * }
160: * return super.resolve(adaptee, monitor);
161: * }
162: * </code></pre>
163: * @param adaptee
164: * @param monitor
165: * @return instance of adaptee, or null if unavailable (IServiceInfo and List<IGeoResource>
166: * must be supported)
167: * @see IServiceInfo
168: * @see IGeoResource
169: * @see IResolve#resolve(Class, IProgressMonitor)
170: */
171: public <T> T resolve(Class<T> adaptee, IProgressMonitor monitor)
172: throws IOException {
173: if (monitor == null)
174: monitor = new NullProgressMonitor();
175:
176: if (adaptee == null) {
177: throw new NullPointerException("No adaptor specified"); //$NON-NLS-1$
178: }
179: if (adaptee.isAssignableFrom(IServiceInfo.class)) {
180: return adaptee.cast(getInfo(monitor));
181: }
182: if (adaptee.isAssignableFrom(IService.class)) {
183: monitor.done();
184: return adaptee.cast(this );
185: }
186: // if (adaptee.isAssignableFrom(List.class)) {
187: // return adaptee.cast(members(monitor));
188: // }
189: if (adaptee.isAssignableFrom(ICatalog.class)) {
190: return adaptee.cast(parent(monitor));
191: }
192: IResolveManager rm = CatalogPlugin.getDefault()
193: .getResolveManager();
194: if (rm.canResolve(this , adaptee)) {
195: return rm.resolve(this , adaptee, monitor);
196: }
197: return null; // no adapter found (check to see if ResolveAdapter is registered?)
198: }
199:
200: /**
201: * Harded coded to capture the IService contract.
202: * <p>
203: * That is we *must* resolve the following:
204: * <ul>
205: * <li>IService: this
206: * <li>IServiceInfo.class: getInfo
207: * <li>List.class: members
208: * <li>ICatalog.class: parent
209: * </ul>
210: * <p>
211: * Here is an implementation example (for something that can adapt to DataStore):
212: *
213: * <pre><code>
214: * public <T> boolean canResolve( Class<T> adaptee ) {
215: * return adaptee != null
216: * && (adaptee.isAssignableFrom(DataStore.class) || super.canResolve(adaptee));
217: * }
218: * </code></pre>
219: */
220: public <T> boolean canResolve(Class<T> adaptee) {
221: return adaptee != null
222: && (adaptee.isAssignableFrom(IService.class) || // this
223: adaptee.isAssignableFrom(IServiceInfo.class) || // getInfo
224: adaptee.isAssignableFrom(List.class) || // members
225: adaptee.isAssignableFrom(ICatalog.class) || // parent
226: CatalogPlugin.getDefault().getResolveManager()
227: .canResolve(this , adaptee));
228: }
229:
230: /**
231: * Returns LocalCatalog by defaul, subclass must override iff a custom catalog is used.
232: */
233: public ICatalog parent(IProgressMonitor monitor) {
234: return CatalogPlugin.getDefault().getLocalCatalog();
235: }
236:
237: /**
238: * Return list of IGeoResources managed by this service.
239: * <p>
240: * Many file based serivces will just contain a single IGeoResource.
241: * </p>
242: */
243: public abstract List<? extends IGeoResource> members(
244: IProgressMonitor monitor) throws IOException;
245:
246: /**
247: * @return IServiceInfo resolve(IServiceInfo.class,IProgressMonitor monitor);
248: * @see IService#resolve(Class, IProgressMonitor)
249: */
250: public abstract IServiceInfo getInfo(IProgressMonitor monitor)
251: throws IOException;
252:
253: /**
254: * Accessor to the set of params used to create this entry. There is no guarantee that these
255: * params created a usable service (@see getStatus() ). These params may have been modified
256: * within the factory during creation. This method is intended to be used for cloning (@see
257: * IServiceFactory) or for persistence between sessions.
258: *
259: * <p>
260: * <b>IMPORTANT:</b> Because of the serialization currently used only types that can be reconstructed from their toString() representation
261: * can be used. For example:
262: * <pre><code>
263: * valueThatIsSaved=url.toString().
264: * URL restoredValue=new URL(valueThatIsSaved);
265: * </code></pre>
266: * Also only classes that this plugin can load can be loaded so custom classes from downstream plugins cannot be used.
267: * It is recommended that only "normal" types be used like Integer, URL, Float, etc...
268: *
269: * </p>
270: * This restriction will be lifted in the future. (Except for the loading issue that is a design issue that we will live with.)
271: * @see IServiceFactory
272: * @return
273: */
274: public abstract Map<String, Serializable> getConnectionParams();
275:
276: /**
277: * This should represent the identifier
278: *
279: * @see Object#equals(java.lang.Object)
280: * @param arg0
281: * @return
282: */
283: public final boolean equals(Object arg0) {
284: if (arg0 != null && arg0 instanceof IService) {
285: IService service = (IService) arg0;
286: if (getIdentifier() != null
287: && service.getIdentifier() != null)
288: return URLUtils.urlToString(getIdentifier(), false)
289: .equals(
290: URLUtils.urlToString(service
291: .getIdentifier(), false));
292: }
293: return false;
294: }
295:
296: /**
297: * This should represent the identified
298: *
299: * @see Object#hashCode()
300: * @return
301: */
302: public final int hashCode() {
303: if (getIdentifier() != null)
304: return URLUtils.urlToString(getIdentifier(), false)
305: .hashCode();
306: return super .hashCode();
307: }
308:
309: /**
310: * Indicate class and id.
311: *
312: * @return string representing this IResolve
313: */
314: public String toString() {
315: StringBuffer buf = new StringBuffer();
316: String classname = getClass().getName();
317: String name = classname
318: .substring(classname.lastIndexOf('.') + 1);
319: buf.append(name);
320: buf.append("("); //$NON-NLS-1$
321: buf.append(getIdentifier());
322: buf.append(")"); //$NON-NLS-1$
323: return buf.toString();
324: }
325:
326: public void dispose(IProgressMonitor monitor) {
327: monitor.beginTask(Messages.IService_dispose, 100);
328: List<? extends IResolve> members;
329: try {
330: members = members(new SubProgressMonitor(monitor, 1));
331: } catch (Throwable e) {
332: ErrorManager
333: .get()
334: .displayException(
335: e,
336: "Error disposing members of service: " + getIdentifier(), CatalogPlugin.ID); //$NON-NLS-1$
337: return;
338: }
339: int steps = (int) ((double) 99 / (double) members.size());
340: for (IResolve resolve : members) {
341: try {
342: SubProgressMonitor subProgressMonitor = new SubProgressMonitor(
343: monitor, steps);
344: resolve.dispose(subProgressMonitor);
345: subProgressMonitor.done();
346: } catch (Throwable e) {
347: ErrorManager
348: .get()
349: .displayException(
350: e,
351: "Error disposing members of service: " + getIdentifier(), CatalogPlugin.ID); //$NON-NLS-1$
352: }
353: }
354: }
355: }
|