001: package net.refractions.udig.internal.ui;
002:
003: import java.io.IOException;
004: import java.io.InputStream;
005: import java.net.Authenticator;
006: import java.net.MalformedURLException;
007: import java.net.URL;
008: import java.text.MessageFormat;
009: import java.util.PropertyResourceBundle;
010:
011: import javax.net.ssl.HttpsURLConnection;
012: import javax.net.ssl.SSLContext;
013: import javax.net.ssl.TrustManager;
014: import javax.net.ssl.X509TrustManager;
015:
016: import net.refractions.udig.core.internal.ExtensionPointProcessor;
017: import net.refractions.udig.core.internal.ExtensionPointUtil;
018: import net.refractions.udig.internal.ui.operations.OperationMenuFactory;
019: import net.refractions.udig.ui.MenuBuilder;
020: import net.refractions.udig.ui.UDIGMenuBuilder;
021: import net.refractions.udig.ui.internal.Messages;
022: import net.refractions.udig.ui.preferences.PreferenceConstants;
023:
024: import org.eclipse.core.runtime.CoreException;
025: import org.eclipse.core.runtime.FileLocator;
026: import org.eclipse.core.runtime.IConfigurationElement;
027: import org.eclipse.core.runtime.IExtension;
028: import org.eclipse.core.runtime.IStatus;
029: import org.eclipse.core.runtime.Path;
030: import org.eclipse.core.runtime.Platform;
031: import org.eclipse.core.runtime.Status;
032: import org.eclipse.core.runtime.preferences.InstanceScope;
033: import org.eclipse.jface.preference.IPreferenceStore;
034: import org.eclipse.jface.resource.ImageDescriptor;
035: import org.eclipse.ui.plugin.AbstractUIPlugin;
036: import org.osgi.framework.Bundle;
037: import org.osgi.framework.BundleContext;
038: import org.osgi.service.prefs.Preferences;
039:
040: /**
041: * The main plugin class to be used in the desktop.
042: */
043: public class UiPlugin extends AbstractUIPlugin {
044: // The shared instance.
045: private static UiPlugin plugin;
046:
047: /** Icons path (value "icons/") */
048: public final static String ICONS_PATH = "icons/";//$NON-NLS-1$
049:
050: public final static String ID = "net.refractions.udig.ui"; //$NON-NLS-1$
051:
052: public static final String DROP_ACTIONS_ID = ID + ".dropActions"; //$NON-NLS-1$
053:
054: public static final String DROP_TRANSFERS_ID = ID
055: + ".dropTransfers"; //$NON-NLS-1$
056:
057: public static final String MAPPINGS_FILENAME = "about.mappings"; //$NON-NLS-1$
058: private static final String UDIG_VERSION_KEY = "1"; //$NON-NLS-1$
059:
060: /** Managed Images instance */
061: private Images images = new Images();
062:
063: private URL iconsUrl;
064:
065: private OperationMenuFactory operationMenuFactory;
066: private MenuBuilder menuBuilder;
067:
068: private String version;
069:
070: /**
071: * The constructor.
072: */
073: public UiPlugin() {
074: super ();
075: plugin = this ;
076: }
077:
078: /**
079: * This method is called upon plug-in activation
080: *
081: * @param context
082: * @throws Exception
083: */
084: public void start(BundleContext context) throws Exception {
085: super .start(context);
086: iconsUrl = context.getBundle().getEntry(ICONS_PATH);
087: Authenticator.setDefault(new UDIGAuthenticator());
088: /*
089: * TODO Further code can nuke the previously set authenticator. Proper security access
090: * should be configured to prevent this.
091: */
092: disableCerts();
093: try {
094: loadVersion();
095:
096: java.lang.System
097: .setProperty(
098: "http.agent", "uDig " + getVersion() + " (http://udig.refractions.net)"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
099: java.lang.System
100: .setProperty(
101: "https.agent", "uDig " + getVersion() + " (http://udig.refractions.net)");//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
102: } catch (Throwable e) {
103: log("error determining version", e); //$NON-NLS-1$
104: }
105: }
106:
107: // this is completely temporary. It allows SSL and HTTPS connections to just accept all certificates.
108: private static void disableCerts() {
109: // Create a trust manager that does not validate certificate chains
110: TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
111: public java.security.cert.X509Certificate[] getAcceptedIssuers() {
112: return null;
113: }
114:
115: public void checkClientTrusted(
116: java.security.cert.X509Certificate[] certs,
117: String authType) {
118: }
119:
120: public void checkServerTrusted(
121: java.security.cert.X509Certificate[] certs,
122: String authType) {
123: }
124: } };
125:
126: // Install the all-trusting trust manager
127: try {
128: SSLContext sc = SSLContext.getInstance("SSL"); //$NON-NLS-1$
129: sc.init(null, trustAllCerts,
130: new java.security.SecureRandom());
131: HttpsURLConnection.setDefaultSSLSocketFactory(sc
132: .getSocketFactory());
133: } catch (Exception e) {
134: }
135:
136: // Now you can access an https URL without having the certificate in the truststore
137: try {
138: URL url = new URL("https://hostname/index.html"); //$NON-NLS-1$
139: url.toString();
140: } catch (MalformedURLException e) {
141: }
142: }
143:
144: private void loadVersion() throws IOException {
145:
146: Bundle pluginBundle = Platform.getProduct().getDefiningBundle();
147:
148: URL mappingsURL = FileLocator.find(pluginBundle, new Path(
149: MAPPINGS_FILENAME), null);
150: if (mappingsURL != null)
151: mappingsURL = FileLocator.resolve(mappingsURL);
152:
153: PropertyResourceBundle bundle = null;
154: if (mappingsURL != null) {
155: InputStream is = null;
156: try {
157: is = mappingsURL.openStream();
158: bundle = new PropertyResourceBundle(is);
159: } catch (IOException e) {
160: bundle = null;
161: String message = "Unable to parse version from about.mappings file. Defaulting to a blank string."; //$NON-NLS-1$
162: this .getLog().log(
163: new Status(IStatus.ERROR, ID, 0, message, e));
164: } finally {
165: try {
166: if (is != null)
167: is.close();
168: } catch (IOException e) {
169: }
170: }
171: }
172:
173: if (bundle != null) {
174: this .version = bundle.getString(UDIG_VERSION_KEY);
175: }
176: }
177:
178: public String getVersion() {
179: return version;
180: }
181:
182: /**
183: * Creates an image descriptor for later use.
184: */
185: synchronized ImageDescriptor create(String id) {
186: URL url = null;
187: try {
188: url = new URL(iconsUrl, id);
189: } catch (MalformedURLException e) {
190: // System.out.println( "Could not locate "+id );
191: return null;
192: }
193: ImageDescriptor image = ImageDescriptor.createFromURL(url);
194: getImageRegistry().put(id, image);
195: return image;
196: }
197:
198: /**
199: * Returns the shared instance.
200: */
201: public static UiPlugin getDefault() {
202: return plugin;
203: }
204:
205: /**
206: * Images instance for use with ImageConstants.
207: *
208: * @return Images for use with ImageConstants.
209: */
210: public Images getImages() {
211: return images;
212: }
213:
214: public OperationMenuFactory getOperationMenuFactory() {
215: if (operationMenuFactory == null) {
216: operationMenuFactory = new OperationMenuFactory();
217: }
218:
219: return operationMenuFactory;
220: }
221:
222: public void stop(BundleContext context) throws Exception {
223: // try{
224: //// if( preferences!=null )
225: //// preferences.flush();
226: // }catch (Throwable e) {
227: // log("Error saving preferences", e); //$NON-NLS-1$
228: // }
229: super .stop(context);
230:
231: plugin = null;
232: }
233:
234: /**
235: * Writes an info log in the plugin's log.
236: * <p>
237: * This should be used for user level messages.
238: * </p>
239: */
240: public static void log(String message2, Throwable e) {
241: String message = message2;
242: if (message == null)
243: message = ""; //$NON-NLS-1$
244: getDefault().getLog().log(
245: new Status(IStatus.INFO, ID, 0, message, e));
246: }
247:
248: /**
249: * Messages that only engage if getDefault().isDebugging()
250: * <p>
251: * It is much prefered to do this:<pre><code>
252: * private static final String RENDERING = "net.refractions.udig.project/render/trace";
253: * if( ProjectUIPlugin.getDefault().isDebugging() && "true".equalsIgnoreCase( RENDERING ) ){
254: * System.out.println( "your message here" );
255: *
256: */
257: private static void trace(String message, Throwable e) {
258: if (getDefault().isDebugging()) {
259: if (message != null)
260: System.out.println(message + "\n"); //$NON-NLS-1$
261: if (e != null)
262: e.printStackTrace(System.out);
263: }
264: }
265:
266: /**
267: * Messages that only engage if getDefault().isDebugging() and the trace option traceID is true.
268: * Available trace options can be found in the Trace class. (They must also be part of the .options file)
269: */
270: public static void trace(String traceID, Class caller,
271: String message, Throwable e) {
272: if (isDebugging(traceID)) {
273: trace(caller, message, e);
274: }
275: }
276:
277: /**
278: * Adds the name of the caller class to the message.
279: *
280: * @param caller class of the object doing the trace.
281: * @param message tracing message, may be null.
282: * @param e exception, may be null.
283: */
284: public static void trace(Class caller, String message, Throwable e) {
285: trace("Tracing - " + caller.getSimpleName() + ": " + message, e); //$NON-NLS-1$ //$NON-NLS-2$
286: }
287:
288: /**
289: * Performs the Platform.getDebugOption true check on the provided trace
290: * <p>
291: * Note: ProjectUIPlugin.getDefault().isDebugging() must also be on.
292: * <ul>
293: * <li>Trace.RENDER - trace rendering progress
294: * </ul>
295: * </p>
296: *
297: * @param trace currently only RENDER is defined
298: */
299: public static boolean isDebugging(final String trace) {
300: return getDefault().isDebugging()
301: && "true".equalsIgnoreCase(Platform.getDebugOption(trace)); //$NON-NLS-1$
302: }
303:
304: /**
305: * Get the MenuFactory which will create the menus for this plugin
306: *
307: * @return The MenuFactory singleton
308: */
309: public MenuBuilder getMenuFactory() {
310: if (menuBuilder == null) {
311: menuBuilder = lookupMenuBuilder();
312: }
313:
314: return menuBuilder;
315: }
316:
317: private MenuBuilder lookupMenuBuilder() {
318:
319: Class interfaceClass = MenuBuilder.class;
320: String prefConstant = PreferenceConstants.P_MENU_BUILDER;
321: String xpid = MenuBuilder.XPID;
322: String idField = MenuBuilder.ATTR_ID;
323: String classField = MenuBuilder.ATTR_CLASS;
324:
325: MenuBuilder mb = (MenuBuilder) lookupConfigurationObject(
326: interfaceClass, getPreferenceStore(), ID, prefConstant,
327: xpid, idField, classField);
328: if (mb != null) {
329: return mb;
330: }
331:
332: return new UDIGMenuBuilder();
333: }
334:
335: /**
336: * Looks a configuration object using the preference store and extension
337: * points to locate the class and instantiate it. If there is a problem,
338: * null is returned and the caller is expect to supply a default value of
339: * their own. Exceptions are not thrown, but messages will be logged.
340: *
341: * These configuration objects are typically defined in
342: * plugin_customization.ini files, and these values are loaded into the
343: * preference store. The parameter <tt>prefConstant</tt> is used to look
344: * up this value, and should be the key (prefixed by the plug-in name,
345: * net.refractions.udig.ui) used in the ini file.
346: *
347: * The returned object will either be an instances of
348: * <tt>interfaceClass</tt> or <tt>null</tt>.
349: *
350: * The parameter <tt>xpid</tt> is the extension point ID that the value
351: * specified in the ini file should point to. This extension point must
352: * contain an attribute used for an id, and an attribute used for the class
353: * which is an implementation of <tt>interfaceClass</tt>. <tt>idField</tt>
354: * indicates the name of the attribute for id, and <tt>classField</tt>
355: * indicates the name of the attribute for the class.
356: *
357: * Example:
358: * plugin_customization.ini
359: * <pre>
360: * net.refractions.udig.ui/workbenchConfiguration=net.refractions.udig.internal.ui.UDIGWorkbenchConfiguration
361: * </pre>
362: *
363: * <b><tt>store</tt></b>: net.refractions.udig.internal.ui.UiPlugin.getPreferenceStore()
364: * (this corresponds to the first part of the key)
365: *
366: * <b><tt>pluginID</tt></b>: "net.refractions.udig.ui"
367: *
368: * <b><tt>prefConstant</tt></b>: "workbenchConfiguration"
369: *
370: *
371: * <pre>
372: * <extension
373: * point="net.refractions.udig.ui.workbenchConfigurations">
374: * <workbenchConfiguration
375: * class="net.refractions.udig.internal.ui.UDIGWorkbenchConfiguration"
376: * id="net.refractions.udig.internal.ui.UDIGWorkbenchConfiguration"/>
377: * </extension>
378: * </pre>
379: *
380: * <b><tt>xpid</tt></b>: "net.refractions.udig.ui.workbenchConfigurations"
381: * <b><tt>idField</tt></b>: "id"
382: * <b><tt>classField</tt></b>: "class"
383: *
384: * This will return an instance of <tt>net.refractions.udig.ui.WorkbenchConfiguration</tt>,
385: * or null if it cannot find one (in which case, check the logs!).
386: *
387: * Make sure to be a good developer and use constants. Also make sure to
388: * use a default implementation if this returns null! The code should not
389: * explode!
390: *
391: * TODO It would be nice to simplify this API call.
392: *
393: * @param interfaceClass instance of the interface that will be instantiated and returned
394: * @param store the preference store used to lookup prefConstant
395: * @param pluginID the ID of the plug-in that the preference store lives
396: * @param prefConstant key used in plugin_customization.ini
397: * @param xpid extension point id key
398: * @param idField id attribute key used in extension point
399: * @param classField class attribute key used in extension point
400: */
401: public static Object lookupConfigurationObject(
402: Class interfaceClass, final IPreferenceStore store,
403: final String pluginID, final String prefConstant,
404: final String xpid, final String idField,
405: final String classField) {
406:
407: final String configurationID = store.getString(prefConstant);
408:
409: if (configurationID == null || configurationID.equals("")) { //$NON-NLS-1$
410:
411: MessageFormat format = new MessageFormat(
412: Messages.UDIGWorkbenchWindowAdvisor_implementationNotSpecified);
413:
414: Object[] args = new Object[] { interfaceClass.getName(),
415: pluginID, prefConstant, xpid };
416: StringBuffer message = format.format(args,
417: new StringBuffer(), null);
418:
419: log(message.toString(), null);
420: } else {
421: try {
422: final Object[] configObj = new Object[1];
423: final Throwable[] error = new Throwable[1];
424: ExtensionPointProcessor p = new ExtensionPointProcessor() {
425:
426: public void process(IExtension extension,
427: IConfigurationElement element)
428: throws Exception {
429: try {
430: if (element.getAttribute(idField) != null
431: && element.getAttribute(idField)
432: .equals(configurationID)) {
433: Object obj = element
434: .createExecutableExtension(classField);
435: configObj[0] = obj;
436: }
437: } catch (Exception e) {
438: configObj[0] = null;
439: error[0] = e;
440: }
441: }
442:
443: };
444: ExtensionPointUtil.process(getDefault(), xpid, p);
445:
446: if (configObj[0] != null) {
447: return configObj[0];
448: } else {
449: MessageFormat format = new MessageFormat(
450: Messages.UDIGWorkbenchWindowAdvisor_specifiedButNotFound);
451: Object[] args = new Object[] { configurationID,
452: interfaceClass.getName() };
453: StringBuffer message = format.format(args,
454: new StringBuffer(), null);
455: Throwable e = null;
456: if (error[0] != null) {
457: e = error[0];
458: }
459: log(message.toString(), e);
460: }
461: } catch (Exception e) {
462: log(
463: MessageFormat
464: .format(
465: Messages.UDIGWorkbenchWindowAdvisor_classNotFound,
466: new Object[] { configurationID },
467: interfaceClass.getName()), e);
468: }
469: }
470:
471: return null;
472: }
473:
474: /**
475: * Gets preferences that are user specific. You don't have to worry about the preferences
476: * changes interfering with preferences of another user's workspace.
477: *
478: * @return preferences that are user specific
479: * @throws CoreException
480: * @throws IOException
481: */
482: // public static synchronized IExportedPreferences getUserPreferences() throws CoreException, IOException {
483: // if (preferences == null) {
484: // preferences=new UDIGExportedPreferences(getDefault().getPreferenceStore(), "^^preference^root^^"); //$NON-NLS-1$
485: // }
486: // return preferences;
487: // }
488: public static Preferences getUserPreferences() {
489: return new InstanceScope().getNode(ID);
490: }
491: }
|