001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU General
007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
008: * License("CDDL") (collectively, the "License"). You may not use this file except in
009: * compliance with the License. You can obtain a copy of the License at
010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
011: * License for the specific language governing permissions and limitations under the
012: * License. When distributing the software, include this License Header Notice in
013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
014: * designates this particular file as subject to the "Classpath" exception as
015: * provided by Sun in the GPL Version 2 section of the License file that
016: * accompanied this code. If applicable, add the following below the License Header,
017: * with the fields enclosed by brackets [] replaced by your own identifying
018: * information: "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Contributor(s):
021: *
022: * The Original Software is NetBeans. The Initial Developer of the Original Software
023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
024: * Rights Reserved.
025: *
026: * If you wish your version of this file to be governed by only the CDDL or only the
027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
029: * you do not indicate a single choice of license, a recipient has the option to
030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
031: * to extend the choice of license to its licensees as provided above. However, if
032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
033: * the option applies only if the new code is made subject to such option by the
034: * copyright holder.
035: */
036:
037: package org.netbeans.installer.utils.applications;
038:
039: import java.io.File;
040: import java.io.IOException;
041: import java.util.HashMap;
042: import java.util.Map;
043: import java.util.regex.Matcher;
044: import java.util.regex.Pattern;
045: import org.netbeans.installer.utils.LogManager;
046: import org.netbeans.installer.utils.ResourceUtils;
047: import org.netbeans.installer.utils.helper.ExecutionResults;
048: import org.netbeans.installer.utils.FileProxy;
049: import org.netbeans.installer.utils.ErrorManager;
050: import org.netbeans.installer.utils.StringUtils;
051: import org.netbeans.installer.utils.SystemUtils;
052: import org.netbeans.installer.utils.exceptions.DownloadException;
053: import org.netbeans.installer.utils.exceptions.NativeException;
054: import org.netbeans.installer.utils.helper.Version;
055: import org.netbeans.installer.utils.system.WindowsNativeUtils;
056: import static org.netbeans.installer.utils.system.windows.WindowsRegistry.HKLM;
057: import static org.netbeans.installer.utils.system.windows.WindowsRegistry.HKCU;
058: import static org.netbeans.installer.utils.system.windows.WindowsRegistry.SEPARATOR;
059: import org.netbeans.installer.utils.system.windows.WindowsRegistry;
060:
061: /**
062: *
063: * @author Kirill Sorokin
064: */
065: public class JavaUtils {
066: /////////////////////////////////////////////////////////////////////////////////
067: // Static
068: private static Map<File, JavaInfo> knownJdks = new HashMap<File, JavaInfo>();
069:
070: public static boolean isJavaHome(File javaHome) {
071: if (!javaHome.exists() || !javaHome.isDirectory()) {
072: return false;
073: }
074:
075: File probe = getExecutable(javaHome);
076: if (!probe.exists() || !probe.isFile()) {
077: return false;
078: }
079:
080: // check for lib subdir
081: probe = new File(javaHome, "lib");
082: if (!probe.exists() || !probe.isDirectory()) {
083: return false;
084: }
085:
086: // now try to deduct whether this is a jre or a jdk (for macos all java
087: // installations would be considered jre, which is ok for the validation
088: // purposes)
089: probe = new File(javaHome, "jre");
090: if (probe.exists()) {
091: probe = new File(javaHome, "lib/dt.jar");
092: if (!probe.exists() || !probe.isFile()) {
093: return false;
094: }
095: } else {
096: probe = new File(javaHome, "lib/charsets.jar");
097: if (!probe.exists() || !probe.isFile()) {
098: // if the probe does not exist, this may mean that we're on macos,
099: // in this case additionally check for 'jce.jar'
100: probe = new File(javaHome, "lib/jce.jar");
101: if (!probe.exists() || !probe.isFile()) {
102: return false;
103: }
104: }
105: }
106:
107: return true;
108: }
109:
110: public static boolean isJdk(File javaHome) {
111: if (!isJavaHome(javaHome)) {
112: return false;
113: }
114:
115: // there is no such thing as private JRE for macos, thus skipping this
116: // part; in fact every javahome on a mac is a jdk
117: if (!SystemUtils.isMacOS()) {
118: File privateJRE = new File(javaHome, "jre");
119:
120: if (!privateJRE.exists()) {
121: return false;
122: }
123:
124: if (!privateJRE.isDirectory()) {
125: return false;
126: }
127: }
128:
129: return true;
130: }
131:
132: public static void addJavaInfo(final File location,
133: final JavaInfo info) {
134: if (knownJdks.get(location) == null) {
135: knownJdks.put(location, info);
136: }
137: }
138:
139: public static void removeJavaInfo(final File location) {
140: if (knownJdks.get(location) != null) {
141: knownJdks.remove(location);
142: }
143: }
144:
145: public static Version getVersion(File javaHome) {
146: final JavaInfo info = getInfo(javaHome);
147:
148: return (info == null) ? null : info.getVersion();
149: }
150:
151: public static JavaInfo getInfo(final File javaHome) {
152: File location = javaHome;
153: try {
154: location = javaHome.getCanonicalFile();
155: } catch (IOException e) {
156: ErrorManager.notifyDebug(ResourceUtils.getString(
157: JavaUtils.class, ERROR_CANNOT_CANONIZE_KEY,
158: javaHome), e);
159: }
160:
161: if (knownJdks.get(location) != null) {
162: return knownJdks.get(location);
163: }
164:
165: if (!isJavaHome(location)) {
166: return null;
167: }
168:
169: final File executable = getExecutable(location);
170:
171: final File testJdk;
172: try {
173: testJdk = FileProxy.getInstance().getFile(TEST_JDK_URI);
174: } catch (DownloadException e) {
175: ErrorManager.notifyError(ResourceUtils.getString(
176: JavaUtils.class, ERROR_CANNOT_DOWNLOAD_TESTJDK_KEY,
177: TEST_JDK_URI), e);
178: return null;
179: }
180:
181: JavaInfo jdkInfo = null;
182: try {
183: final ExecutionResults results = SystemUtils
184: .executeCommand(executable.getAbsolutePath(),
185: "-classpath", testJdk.getParentFile()
186: .getAbsolutePath(),
187: TEST_JDK_CLASSNAME);
188:
189: jdkInfo = JavaInfo.getInfo(results.getStdOut());
190:
191: if (jdkInfo != null) {
192: LogManager.log("... put jdk info to the Java map");
193: knownJdks.put(location, jdkInfo);
194: } else {
195: LogManager
196: .log("... can`t get jdkInfo from " + location);
197: }
198: } catch (IOException e) {
199: LogManager.log(ResourceUtils.getString(JavaUtils.class,
200: ERROR_VERIFICATION_KEY), e);
201: }
202:
203: if (!testJdk.delete()) {
204: ErrorManager.notifyError(ResourceUtils.getString(
205: JavaUtils.class, ERROR_CANNOT_DELETE_KEY, testJdk
206: .getAbsolutePath()));
207: }
208:
209: return jdkInfo;
210: }
211:
212: public static File getExecutable(File javaHome) {
213: if (SystemUtils.isWindows()) {
214: return new File(javaHome, "bin/java.exe");
215: } else {
216: return new File(javaHome, "bin/java");
217: }
218: }
219:
220: public static File getExecutableW(File javaHome) {
221: if (SystemUtils.isWindows()) {
222: return new File(javaHome, "bin/javaw.exe");
223: } else {
224: return new File(javaHome, "bin/java");
225: }
226: }
227:
228: // windows-only /////////////////////////////////////////////////////////////////
229: public static void createJdkKey(Version version, String javaHome)
230: throws NativeException {
231: if (!SystemUtils.isWindows()) {
232: return;
233: }
234:
235: final WindowsRegistry registry = ((WindowsNativeUtils) SystemUtils
236: .getNativeUtils()).getWindowsRegistry();
237:
238: String key = registry.constructKey(JDK_KEY, version
239: .toJdkStyle());
240:
241: setJdkData(key, version, javaHome);
242: updateJdkKey(registry.constructKey(JDK_KEY, version.toMinor()));
243: updateCurrentVersion();
244: }
245:
246: public static void deleteJdkKey(Version version, String javaHome)
247: throws NativeException {
248: if (!SystemUtils.isWindows()) {
249: return;
250: }
251:
252: final WindowsRegistry registry = ((WindowsNativeUtils) SystemUtils
253: .getNativeUtils()).getWindowsRegistry();
254:
255: String key = registry.constructKey(JDK_KEY, version
256: .toJdkStyle());
257: int section = getJDKRegistrySection(registry);
258: if (registry.keyExists(section, key)
259: && registry.valueExists(section, key, JAVAHOME_VALUE)) {
260: String currentJavaHome = registry.getStringValue(section,
261: key, JAVAHOME_VALUE);
262: if (currentJavaHome.equals(javaHome)) {
263: registry.deleteKey(section, key);
264: updateJdkKey(registry.constructKey(JDK_KEY, version
265: .toMinor()));
266: }
267: }
268:
269: updateCurrentVersion();
270: }
271:
272: public static File findJDKHome(Version jdkVersion) {
273: return findJavaHome(JDK_KEY, jdkVersion);
274: }
275:
276: public static File findJreHome(Version jdkVersion) {
277: return findJavaHome(JRE_KEY, jdkVersion);
278: }
279:
280: private static File findJavaHome(String javaKey, Version jdkVersion) {
281: File result = null;
282: try {
283: if (SystemUtils.isWindows()) {
284: final String version = jdkVersion.toJdkStyle();
285: LogManager.log("... checking if JDK " + version
286: + " is already installed");
287: WindowsRegistry winreg = ((WindowsNativeUtils) SystemUtils
288: .getNativeUtils()).getWindowsRegistry();
289: if (winreg.keyExists(HKLM, javaKey, version)) {
290: final String versKey = javaKey + winreg.SEPARATOR
291: + version;
292: if (winreg.valueExists(HKLM, versKey,
293: JAVAHOME_VALUE)) {
294: final String javaHome = winreg.getStringValue(
295: HKLM, versKey, JAVAHOME_VALUE);
296: if (JavaUtils.getInfo(new File(javaHome)) != null) {
297: result = new File(javaHome);
298: } else {
299: LogManager
300: .log("... no Java at " + javaHome);
301: }
302: } else {
303: LogManager
304: .log("... cannot find JavaHome value for this Java");
305: }
306: } else {
307: LogManager.log("... cannot find key for this Java");
308: }
309: }
310: } catch (NativeException e) {
311: LogManager.log(e);
312: }
313: return result;
314: }
315:
316: // private //////////////////////////////////////////////////////////////////////
317: private static int getJDKRegistrySection(WindowsRegistry registry)
318: throws NativeException {
319: return (registry.canModifyKey(HKLM, JDK_KEY) ? HKLM : HKCU);
320: }
321:
322: private static void setJdkData(String key, Version version,
323: String javaHome) throws NativeException {
324: final WindowsRegistry registry = ((WindowsNativeUtils) SystemUtils
325: .getNativeUtils()).getWindowsRegistry();
326: int section = getJDKRegistrySection(registry);
327: registry.createKey(section, key);
328: registry.setStringValue(section, key, JAVAHOME_VALUE, javaHome);
329: registry.setStringValue(section, key, MICROVERSION_VALUE,
330: version.getMicro());
331: }
332:
333: private static void updateJdkKey(String key) throws NativeException {
334: final WindowsRegistry registry = ((WindowsNativeUtils) SystemUtils
335: .getNativeUtils()).getWindowsRegistry();
336: int section = getJDKRegistrySection(registry);
337: registry.createKey(section, key);
338:
339: String javaHome = null;
340: Version version = null;
341: for (String subkey : registry.getSubKeys(section, JDK_KEY)) {
342: if (subkey.startsWith(key)
343: && !subkey.equals(key)
344: && registry.valueExists(section, subkey,
345: JAVAHOME_VALUE)) {
346: final String tempJavaHome = registry.getStringValue(
347: section, subkey, JAVAHOME_VALUE);
348: final Version tempVersion = JavaUtils
349: .getVersion(new File(tempJavaHome));
350: if ((tempVersion != null)
351: && ((version == null) || version
352: .olderThan(tempVersion))) {
353: javaHome = tempJavaHome;
354: version = tempVersion;
355: }
356: }
357: }
358:
359: if ((version != null) && (javaHome != null)) {
360: setJdkData(key, version, javaHome);
361: } else {
362: registry.deleteKey(section, key);
363: }
364: }
365:
366: private static void updateCurrentVersion() throws NativeException {
367: final WindowsRegistry registry = ((WindowsNativeUtils) SystemUtils
368: .getNativeUtils()).getWindowsRegistry();
369: int section = getJDKRegistrySection(registry);
370: registry.createKey(section, JDK_KEY);
371:
372: String name = null;
373: Version version = null;
374:
375: for (String key : registry.getSubKeys(section, JDK_KEY)) {
376: if (registry.valueExists(section, key, JAVAHOME_VALUE)) {
377: String tempName = registry.getKeyName(key);
378: String tempJavaHome = registry.getStringValue(section,
379: key, JAVAHOME_VALUE);
380: Version tempVersion = JavaUtils.getVersion(new File(
381: tempJavaHome));
382: if ((tempVersion != null)
383: && ((version == null) || version
384: .olderThan(tempVersion))) {
385: name = tempName;
386: version = tempVersion;
387: }
388: }
389: }
390:
391: if ((name != null) && (version != null)) {
392: registry.setStringValue(section, JDK_KEY,
393: CURRENT_VERSION_VALUE, name);
394: } else {
395: registry.deleteKey(section, JDK_KEY);
396: }
397: }
398:
399: /////////////////////////////////////////////////////////////////////////////////
400: // Instance
401: private JavaUtils() {
402: // does nothing
403: }
404:
405: /////////////////////////////////////////////////////////////////////////////////
406: // Inner Classes
407: public static class JavaInfo {
408: /////////////////////////////////////////////////////////////////////////////
409: // Static
410: public static JavaInfo getInfo(final String string) {
411: final String[] lines = StringUtils.splitByLines(string);
412:
413: Version version = null;
414: String vendor = null;
415: String osName = null;
416: String osArch = null;
417:
418: boolean nonFinal = false;
419:
420: if (lines.length == (TEST_JDK_OUTPUT_PARAMETERS + 1)) {
421: final String javaVersion = lines[0]; // java.version
422: final String javaVmVersion = lines[1]; // java.vm.version
423:
424: vendor = lines[2]; // java.vendor
425: osName = lines[3]; // os.name
426: osArch = lines[4]; // os.arch
427: LogManager.log("... java.version = " + javaVersion);
428: LogManager
429: .log("... java.vm.version = " + javaVmVersion);
430: LogManager.log("... java.vendor = " + vendor);
431: LogManager.log("... os.name = " + osName);
432: LogManager.log("... os.arch = " + osArch);
433:
434: String versionString;
435:
436: // if java.vm.version contains java.version, then use it, as it
437: // usually contains more detailed info
438: if (javaVmVersion.indexOf(javaVersion) != -1) {
439: versionString = javaVmVersion
440: .substring(javaVmVersion
441: .indexOf(javaVersion));
442: } else {
443: versionString = javaVersion;
444: }
445:
446: // check whether this particular jvm is non final
447: final Matcher nonFinalMatcher = Pattern.compile(
448: NON_FINAL_JVM_PATTERN).matcher(versionString);
449: if (nonFinalMatcher.find()) {
450: versionString = versionString.replaceAll(
451: NON_FINAL_JVM_PATTERN,
452: StringUtils.EMPTY_STRING);
453:
454: nonFinal = true;
455: }
456:
457: // convert 1.6.0-b105 to 1.6.0.0.105
458: if (versionString
459: .matches("[0-9]+\\.[0-9]+\\.[0-9]+-b[0-9]+")) {
460: versionString = versionString.replace("-b", ".0.");
461: }
462:
463: // convert 1.6.0_01-b105 to 1.6.0_01.105
464: if (versionString
465: .matches("[0-9]+\\.[0-9]+\\.[0-9]+_[0-9]+-b[0-9]+")) {
466: versionString = versionString.replace("-b", ".");
467: }
468:
469: // hack for BEA: 1.6.0-20061129 -> 1.6.0.0.20061129
470: if (vendor.indexOf("BEA") != -1) {
471: versionString = versionString.replaceAll(
472: "([0-9]+\\.[0-9]+\\.[0-9])+-([0-9]+)",
473: "$1.0.$2");
474: }
475: LogManager.log("... version string : " + versionString);
476: // and create the version
477: final Matcher matcher = Pattern.compile(
478: "[0-9][0-9_\\.\\-]+[0-9]").matcher(
479: versionString);
480:
481: if (matcher.find()) {
482: version = Version.getVersion(matcher.group());
483: }
484:
485: // if the version was created successfully, then we can provide a
486: // JavaInfo object
487: if (version != null) {
488: return new JavaInfo(version, vendor, nonFinal);
489: }
490: } else {
491: LogManager.log("... different lines number ["
492: + lines.length + "]");
493: for (int j = 0; j < lines.length; j++) {
494: LogManager.log("... line [" + j + "] = ["
495: + lines[j] + "]");
496: }
497: }
498:
499: return null;
500: }
501:
502: /////////////////////////////////////////////////////////////////////////////
503: // Instance
504: private Version version;
505: private String vendor;
506:
507: private boolean nonFinal;
508:
509: public JavaInfo(Version version, String vendor) {
510: this .version = version;
511: this .vendor = vendor;
512:
513: this .nonFinal = false;
514: }
515:
516: public JavaInfo(Version version, String vendor, boolean nonFinal) {
517: this (version, vendor);
518:
519: this .nonFinal = nonFinal;
520: }
521:
522: public Version getVersion() {
523: return version;
524: }
525:
526: public String getVendor() {
527: return vendor;
528: }
529:
530: public boolean isNonFinal() {
531: return nonFinal;
532: }
533: }
534:
535: /////////////////////////////////////////////////////////////////////////////////
536: // Constants
537: public static final String JDK_KEY = "SOFTWARE\\JavaSoft\\Java Development Kit"; // NOI18N
538: public static final String JRE_KEY = "SOFTWARE\\JavaSoft\\Java Runtime Environment"; // NOI18N
539:
540: public static final String JAVAHOME_VALUE = "JavaHome"; // NOI18N
541:
542: public static final String MICROVERSION_VALUE = "MicroVersion"; // NOI18N
543:
544: public static final String CURRENT_VERSION_VALUE = "CurrentVersion"; // NOI18N
545:
546: public static final String TEST_JDK_RESOURCE = "org/netbeans/installer/utils/applications/TestJDK.class"; // NOI18N
547:
548: public static final String TEST_JDK_URI = FileProxy.RESOURCE_SCHEME_PREFIX
549: + TEST_JDK_RESOURCE;
550:
551: public static final String TEST_JDK_CLASSNAME = "TestJDK"; // NOI18N
552:
553: public static final int TEST_JDK_OUTPUT_PARAMETERS = 5; // java.version, java.vm.version, java.vendor, os.name, os.arch
554:
555: public static final String NON_FINAL_JVM_PATTERN = "-(ea|rc[0-9]*|beta[0-9]*|preview[0-9]*|"
556: + // NOI18N
557: "dp[0-9]*|alpha[0-9]*|fcs)"; // NOI18N
558:
559: public static final String ERROR_VERIFICATION_KEY = "JU.error.verification";//NOI18N
560: public static final String ERROR_CANNOT_DELETE_KEY = "JU.error.cannot.delete";//NOI18N
561: public static final String ERROR_CANNOT_DOWNLOAD_TESTJDK_KEY = "JU.error.cannot.download.testjdk";//NOI18N
562: public static final String ERROR_CANNOT_CANONIZE_KEY = "JU.error.cannot.canonize";//NOI18N
563: }
|