001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.launching;
011:
012: import java.io.ByteArrayInputStream;
013: import java.io.File;
014: import java.io.IOException;
015: import java.net.URL;
016: import java.util.HashMap;
017: import java.util.Iterator;
018: import java.util.Map;
019:
020: import javax.xml.parsers.DocumentBuilder;
021:
022: import org.eclipse.core.runtime.CoreException;
023: import org.eclipse.core.runtime.IProgressMonitor;
024: import org.eclipse.core.runtime.IStatus;
025: import org.eclipse.core.runtime.NullProgressMonitor;
026: import org.eclipse.core.runtime.Path;
027: import org.eclipse.core.runtime.Preferences;
028: import org.eclipse.core.runtime.Status;
029: import org.eclipse.debug.core.ILaunchManager;
030: import org.eclipse.debug.core.Launch;
031: import org.eclipse.debug.core.model.IProcess;
032: import org.eclipse.debug.core.model.IStreamsProxy;
033: import org.eclipse.jdt.core.JavaCore;
034: import org.eclipse.jdt.internal.launching.LaunchingMessages;
035: import org.eclipse.jdt.internal.launching.LaunchingPlugin;
036: import org.w3c.dom.Document;
037: import org.w3c.dom.Element;
038: import org.w3c.dom.Node;
039: import org.w3c.dom.NodeList;
040: import org.xml.sax.SAXException;
041:
042: /**
043: * Abstract implementation of a VM install.
044: * <p>
045: * Clients implementing VM installs must subclass this class.
046: * </p>
047: */
048: public abstract class AbstractVMInstall implements IVMInstall,
049: IVMInstall2, IVMInstall3 {
050:
051: private IVMInstallType fType;
052: private String fId;
053: private String fName;
054: private File fInstallLocation;
055: private LibraryLocation[] fSystemLibraryDescriptions;
056: private URL fJavadocLocation;
057: private String fVMArgs;
058: // system properties are cached in user preferences prefixed with this key, followed
059: // by vm type, vm id, and system property name
060: private static final String PREF_VM_INSTALL_SYSTEM_PROPERTY = "PREF_VM_INSTALL_SYSTEM_PROPERTY"; //$NON-NLS-1$
061: // whether change events should be fired
062: private boolean fNotify = true;
063:
064: /**
065: * Constructs a new VM install.
066: *
067: * @param type The type of this VM install.
068: * Must not be <code>null</code>
069: * @param id The unique identifier of this VM instance
070: * Must not be <code>null</code>.
071: * @throws IllegalArgumentException if any of the required
072: * parameters are <code>null</code>.
073: */
074: public AbstractVMInstall(IVMInstallType type, String id) {
075: if (type == null)
076: throw new IllegalArgumentException(
077: LaunchingMessages.vmInstall_assert_typeNotNull);
078: if (id == null)
079: throw new IllegalArgumentException(
080: LaunchingMessages.vmInstall_assert_idNotNull);
081: fType = type;
082: fId = id;
083: }
084:
085: /* (non-Javadoc)
086: * Subclasses should not override this method.
087: * @see IVMInstall#getId()
088: */
089: public String getId() {
090: return fId;
091: }
092:
093: /* (non-Javadoc)
094: * Subclasses should not override this method.
095: * @see IVMInstall#getName()
096: */
097: public String getName() {
098: return fName;
099: }
100:
101: /* (non-Javadoc)
102: * Subclasses should not override this method.
103: * @see IVMInstall#setName(String)
104: */
105: public void setName(String name) {
106: if (!name.equals(fName)) {
107: PropertyChangeEvent event = new PropertyChangeEvent(this ,
108: IVMInstallChangedListener.PROPERTY_NAME, fName,
109: name);
110: fName = name;
111: if (fNotify) {
112: JavaRuntime.fireVMChanged(event);
113: }
114: }
115: }
116:
117: /* (non-Javadoc)
118: * Subclasses should not override this method.
119: * @see IVMInstall#getInstallLocation()
120: */
121: public File getInstallLocation() {
122: return fInstallLocation;
123: }
124:
125: /* (non-Javadoc)
126: * Subclasses should not override this method.
127: * @see IVMInstall#setInstallLocation(File)
128: */
129: public void setInstallLocation(File installLocation) {
130: if (!installLocation.equals(fInstallLocation)) {
131: PropertyChangeEvent event = new PropertyChangeEvent(
132: this ,
133: IVMInstallChangedListener.PROPERTY_INSTALL_LOCATION,
134: fInstallLocation, installLocation);
135: fInstallLocation = installLocation;
136: if (fNotify) {
137: JavaRuntime.fireVMChanged(event);
138: }
139: }
140: }
141:
142: /* (non-Javadoc)
143: * Subclasses should not override this method.
144: * @see IVMInstall#getVMInstallType()
145: */
146: public IVMInstallType getVMInstallType() {
147: return fType;
148: }
149:
150: /* (non-Javadoc)
151: * @see IVMInstall#getVMRunner(String)
152: */
153: public IVMRunner getVMRunner(String mode) {
154: return null;
155: }
156:
157: /* (non-Javadoc)
158: * @see org.eclipse.jdt.launching.IVMInstall#getLibraryLocations()
159: */
160: public LibraryLocation[] getLibraryLocations() {
161: return fSystemLibraryDescriptions;
162: }
163:
164: /* (non-Javadoc)
165: * @see org.eclipse.jdt.launching.IVMInstall#setLibraryLocations(org.eclipse.jdt.launching.LibraryLocation[])
166: */
167: public void setLibraryLocations(LibraryLocation[] locations) {
168: if (locations == fSystemLibraryDescriptions) {
169: return;
170: }
171: LibraryLocation[] newLocations = locations;
172: if (newLocations == null) {
173: newLocations = getVMInstallType()
174: .getDefaultLibraryLocations(getInstallLocation());
175: }
176: LibraryLocation[] prevLocations = fSystemLibraryDescriptions;
177: if (prevLocations == null) {
178: prevLocations = getVMInstallType()
179: .getDefaultLibraryLocations(getInstallLocation());
180: }
181:
182: if (newLocations.length == prevLocations.length) {
183: int i = 0;
184: boolean equal = true;
185: while (i < newLocations.length && equal) {
186: equal = newLocations[i].equals(prevLocations[i]);
187: i++;
188: }
189: if (equal) {
190: // no change
191: return;
192: }
193: }
194:
195: PropertyChangeEvent event = new PropertyChangeEvent(this ,
196: IVMInstallChangedListener.PROPERTY_LIBRARY_LOCATIONS,
197: prevLocations, newLocations);
198: fSystemLibraryDescriptions = locations;
199: if (fNotify) {
200: JavaRuntime.fireVMChanged(event);
201: }
202: }
203:
204: /* (non-Javadoc)
205: * @see org.eclipse.jdt.launching.IVMInstall#getJavadocLocation()
206: */
207: public URL getJavadocLocation() {
208: return fJavadocLocation;
209: }
210:
211: /* (non-Javadoc)
212: * @see org.eclipse.jdt.launching.IVMInstall#setJavadocLocation(java.net.URL)
213: */
214: public void setJavadocLocation(URL url) {
215: if (url == fJavadocLocation) {
216: return;
217: }
218: if (url != null && fJavadocLocation != null) {
219: if (url.equals(fJavadocLocation)) {
220: // no change
221: return;
222: }
223: }
224:
225: PropertyChangeEvent event = new PropertyChangeEvent(this ,
226: IVMInstallChangedListener.PROPERTY_JAVADOC_LOCATION,
227: fJavadocLocation, url);
228: fJavadocLocation = url;
229: if (fNotify) {
230: JavaRuntime.fireVMChanged(event);
231: }
232: }
233:
234: /**
235: * Whether this VM should fire property change notifications.
236: *
237: * @param notify
238: * @since 2.1
239: */
240: protected void setNotify(boolean notify) {
241: fNotify = notify;
242: }
243:
244: /* (non-Javadoc)
245: * @see java.lang.Object#equals(java.lang.Object)
246: * @since 2.1
247: */
248: public boolean equals(Object object) {
249: if (object instanceof IVMInstall) {
250: IVMInstall vm = (IVMInstall) object;
251: return getVMInstallType().equals(vm.getVMInstallType())
252: && getId().equals(vm.getId());
253: }
254: return false;
255: }
256:
257: /* (non-Javadoc)
258: * @see java.lang.Object#hashCode()
259: * @since 2.1
260: */
261: public int hashCode() {
262: return getVMInstallType().hashCode() + getId().hashCode();
263: }
264:
265: /* (non-Javadoc)
266: * @see org.eclipse.jdt.launching.IVMInstall#getDefaultVMArguments()
267: * @since 3.0
268: */
269: public String[] getVMArguments() {
270: String args = getVMArgs();
271: if (args == null) {
272: return null;
273: }
274: ExecutionArguments ex = new ExecutionArguments(args, ""); //$NON-NLS-1$
275: return ex.getVMArgumentsArray();
276: }
277:
278: /* (non-Javadoc)
279: * @see org.eclipse.jdt.launching.IVMInstall#setDefaultVMArguments(java.lang.String[])
280: * @since 3.0
281: */
282: public void setVMArguments(String[] vmArgs) {
283: if (vmArgs == null) {
284: setVMArgs(null);
285: } else {
286: StringBuffer buf = new StringBuffer();
287: for (int i = 0; i < vmArgs.length; i++) {
288: String string = vmArgs[i];
289: buf.append(string);
290: buf.append(" "); //$NON-NLS-1$
291: }
292: setVMArgs(buf.toString().trim());
293: }
294: }
295:
296: /* (non-Javadoc)
297: * @see org.eclipse.jdt.launching.IVMInstall2#getVMArgs()
298: */
299: public String getVMArgs() {
300: return fVMArgs;
301: }
302:
303: /* (non-Javadoc)
304: * @see org.eclipse.jdt.launching.IVMInstall2#setVMArgs(java.lang.String)
305: */
306: public void setVMArgs(String vmArgs) {
307: if (fVMArgs == null) {
308: if (vmArgs == null) {
309: // No change
310: return;
311: }
312: } else if (fVMArgs.equals(vmArgs)) {
313: // No change
314: return;
315: }
316: PropertyChangeEvent event = new PropertyChangeEvent(this ,
317: IVMInstallChangedListener.PROPERTY_VM_ARGUMENTS,
318: fVMArgs, vmArgs);
319: fVMArgs = vmArgs;
320: if (fNotify) {
321: JavaRuntime.fireVMChanged(event);
322: }
323: }
324:
325: /* (non-Javadoc)
326: * Subclasses should override.
327: * @see org.eclipse.jdt.launching.IVMInstall2#getJavaVersion()
328: */
329: public String getJavaVersion() {
330: return null;
331: }
332:
333: /* (non-Javadoc)
334: * @see org.eclipse.jdt.launching.IVMInstall3#evaluateSystemProperties(java.lang.String[], org.eclipse.core.runtime.IProgressMonitor)
335: */
336: public Map evaluateSystemProperties(String[] properties,
337: IProgressMonitor monitor) throws CoreException {
338: //locate the launching support jar - it contains the main program to run
339: if (monitor == null) {
340: monitor = new NullProgressMonitor();
341: }
342: Map map = new HashMap();
343:
344: // first check cache (preference store) to avoid launching VM
345: Preferences preferences = JavaRuntime.getPreferences();
346: boolean cached = true;
347: for (int i = 0; i < properties.length; i++) {
348: String property = properties[i];
349: String key = getSystemPropertyKey(property);
350: if (preferences.contains(key)) {
351: String value = preferences.getString(key);
352: map.put(property, value);
353: } else {
354: map.clear();
355: cached = false;
356: break;
357: }
358: }
359: if (!cached) {
360: // launch VM to evaluate properties
361: File file = LaunchingPlugin.getFileInPlugin(new Path(
362: "lib/launchingsupport.jar")); //$NON-NLS-1$
363: if (file.exists()) {
364: String javaVersion = getJavaVersion();
365: boolean hasXMLSupport = false;
366: if (javaVersion != null) {
367: hasXMLSupport = true;
368: if (javaVersion.startsWith(JavaCore.VERSION_1_1)
369: || javaVersion
370: .startsWith(JavaCore.VERSION_1_2)
371: || javaVersion
372: .startsWith(JavaCore.VERSION_1_3)) {
373: hasXMLSupport = false;
374: }
375: }
376: String mainType = null;
377: if (hasXMLSupport) {
378: mainType = "org.eclipse.jdt.internal.launching.support.SystemProperties"; //$NON-NLS-1$
379: } else {
380: mainType = "org.eclipse.jdt.internal.launching.support.LegacySystemProperties"; //$NON-NLS-1$
381: }
382: VMRunnerConfiguration config = new VMRunnerConfiguration(
383: mainType,
384: new String[] { file.getAbsolutePath() });
385: IVMRunner runner = getVMRunner(ILaunchManager.RUN_MODE);
386: if (runner == null) {
387: abort(
388: LaunchingMessages.AbstractVMInstall_0,
389: null,
390: IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
391: }
392: config.setProgramArguments(properties);
393: Launch launch = new Launch(null,
394: ILaunchManager.RUN_MODE, null);
395: if (monitor.isCanceled()) {
396: return map;
397: }
398: monitor.beginTask(
399: LaunchingMessages.AbstractVMInstall_1, 2);
400: runner.run(config, launch, monitor);
401: IProcess[] processes = launch.getProcesses();
402: if (processes.length != 1) {
403: abort(
404: LaunchingMessages.AbstractVMInstall_0,
405: null,
406: IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
407: }
408: IProcess process = processes[0];
409: try {
410: int total = 0;
411: int max = JavaRuntime.getPreferences().getInt(
412: JavaRuntime.PREF_CONNECT_TIMEOUT);
413: while (!process.isTerminated()) {
414: try {
415: if (total > max) {
416: break;
417: }
418: Thread.sleep(50);
419: total += 50;
420: } catch (InterruptedException e) {
421: }
422: }
423: } finally {
424: if (!launch.isTerminated()) {
425: launch.terminate();
426: }
427: }
428: monitor.worked(1);
429: if (monitor.isCanceled()) {
430: return map;
431: }
432:
433: monitor.subTask(LaunchingMessages.AbstractVMInstall_3);
434: IStreamsProxy streamsProxy = process.getStreamsProxy();
435: String text = null;
436: if (streamsProxy != null) {
437: text = streamsProxy.getOutputStreamMonitor()
438: .getContents();
439: }
440: if (text != null && text.length() > 0) {
441: try {
442: DocumentBuilder parser = LaunchingPlugin
443: .getParser();
444: Document document = parser
445: .parse(new ByteArrayInputStream(text
446: .getBytes()));
447: Element envs = document.getDocumentElement();
448: NodeList list = envs.getChildNodes();
449: int length = list.getLength();
450: for (int i = 0; i < length; ++i) {
451: Node node = list.item(i);
452: short type = node.getNodeType();
453: if (type == Node.ELEMENT_NODE) {
454: Element element = (Element) node;
455: if (element.getNodeName().equals(
456: "property")) { //$NON-NLS-1$
457: String name = element
458: .getAttribute("name"); //$NON-NLS-1$
459: String value = element
460: .getAttribute("value"); //$NON-NLS-1$
461: map.put(name, value);
462: }
463: }
464: }
465: } catch (SAXException e) {
466: abort(
467: LaunchingMessages.AbstractVMInstall_4,
468: e,
469: IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
470: } catch (IOException e) {
471: abort(
472: LaunchingMessages.AbstractVMInstall_4,
473: e,
474: IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
475: }
476: } else {
477: abort(
478: LaunchingMessages.AbstractVMInstall_0,
479: null,
480: IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
481: }
482: monitor.worked(1);
483: } else {
484: abort(
485: LaunchingMessages.AbstractVMInstall_0,
486: null,
487: IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
488: }
489: // cache for future reference
490: Iterator keys = map.keySet().iterator();
491: while (keys.hasNext()) {
492: String property = (String) keys.next();
493: String value = (String) map.get(property);
494: String key = getSystemPropertyKey(property);
495: preferences.setValue(key, value);
496: }
497: }
498: monitor.done();
499: return map;
500: }
501:
502: /**
503: * Generates a key used to cache system property for this VM in this plug-ins
504: * preference store.
505: *
506: * @param property system property name
507: * @return preference store key
508: */
509: private String getSystemPropertyKey(String property) {
510: StringBuffer buffer = new StringBuffer();
511: buffer.append(PREF_VM_INSTALL_SYSTEM_PROPERTY);
512: buffer.append("."); //$NON-NLS-1$
513: buffer.append(getVMInstallType().getId());
514: buffer.append("."); //$NON-NLS-1$
515: buffer.append(getId());
516: buffer.append("."); //$NON-NLS-1$
517: buffer.append(property);
518: return buffer.toString();
519: }
520:
521: /**
522: * Throws a core exception with an error status object built from the given
523: * message, lower level exception, and error code.
524: *
525: * @param message the status message
526: * @param exception lower level exception associated with the error, or
527: * <code>null</code> if none
528: * @param code error code
529: * @throws CoreException the "abort" core exception
530: * @since 3.2
531: */
532: protected void abort(String message, Throwable exception, int code)
533: throws CoreException {
534: throw new CoreException(new Status(IStatus.ERROR,
535: LaunchingPlugin.getUniqueIdentifier(), code, message,
536: exception));
537: }
538:
539: }
|