001: /*
002: * $Id: JDKPathPanel.java 2056 2008-02-25 08:29:28Z jponge $
003: * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
004: *
005: * http://izpack.org/
006: * http://izpack.codehaus.org/
007: *
008: * Copyright 2004 Klaus Bartz
009: *
010: * Licensed under the Apache License, Version 2.0 (the "License");
011: * you may not use this file except in compliance with the License.
012: * You may obtain a copy of the License at
013: *
014: * http://www.apache.org/licenses/LICENSE-2.0
015: *
016: * Unless required by applicable law or agreed to in writing, software
017: * distributed under the License is distributed on an "AS IS" BASIS,
018: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019: * See the License for the specific language governing permissions and
020: * limitations under the License.
021: */
022:
023: package com.izforge.izpack.panels;
024:
025: import java.io.File;
026: import java.util.Arrays;
027: import java.util.HashSet;
028: import java.util.Set;
029: import java.util.StringTokenizer;
030:
031: import com.coi.tools.os.win.MSWinConstants;
032: import com.coi.tools.os.win.NativeLibException;
033: import com.izforge.izpack.installer.InstallData;
034: import com.izforge.izpack.installer.InstallerFrame;
035: import com.izforge.izpack.util.AbstractUIHandler;
036: import com.izforge.izpack.util.FileExecutor;
037: import com.izforge.izpack.util.OsVersion;
038: import com.izforge.izpack.util.os.RegistryDefaultHandler;
039: import com.izforge.izpack.util.os.RegistryHandler;
040:
041: /**
042: * Panel which asks for the JDK path.
043: *
044: * @author Klaus Bartz
045: *
046: */
047: public class JDKPathPanel extends PathInputPanel {
048:
049: private static final long serialVersionUID = 3257006553327810104L;
050:
051: private static final String[] testFiles = new String[] { "lib"
052: + File.separator + "tools.jar" };
053:
054: private static final String JDK_ROOT_KEY = "Software\\JavaSoft\\Java Development Kit";
055:
056: private static final String JDK_VALUE_NAME = "JavaHome";
057:
058: private static final String OSX_JDK_HOME = "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home/";
059:
060: private static final int OK = 0;
061: private static final int BAD_VERSION = 1;
062: private static final int BAD_REAL_PATH = 2;
063: private static final int BAD_REG_PATH = 3;
064:
065: private String detectedVersion;
066:
067: private String minVersion = null;
068:
069: private String maxVersion = null;
070:
071: private String variableName;
072:
073: private Set<String> badRegEntries = null;
074:
075: /**
076: * The constructor.
077: *
078: * @param parent The parent window.
079: * @param idata The installation data.
080: */
081: public JDKPathPanel(InstallerFrame parent, InstallData idata) {
082: super (parent, idata);
083: setMustExist(true);
084: if (!OsVersion.IS_OSX)
085: setExistFiles(JDKPathPanel.testFiles);
086: setMinVersion(idata.getVariable("JDKPathPanel.minVersion"));
087: setMaxVersion(idata.getVariable("JDKPathPanel.maxVersion"));
088: setVariableName("JDKPath");
089: }
090:
091: /**
092: * Indicates wether the panel has been validated or not.
093: *
094: * @return Wether the panel has been validated or not.
095: */
096: public boolean isValidated() {
097: boolean retval = false;
098: if (super .isValidated()) {
099: switch (verifyVersionEx()) {
100: case OK:
101: idata.setVariable(getVariableName(), pathSelectionPanel
102: .getPath());
103: retval = true;
104: break;
105: case BAD_REG_PATH:
106: if (askQuestion(
107: parent.langpack.getString("installer.warning"),
108: parent.langpack
109: .getString("JDKPathPanel.nonValidPathInReg"),
110: AbstractUIHandler.CHOICES_YES_NO,
111: AbstractUIHandler.ANSWER_NO) == AbstractUIHandler.ANSWER_YES) {
112: idata.setVariable(getVariableName(),
113: pathSelectionPanel.getPath());
114: retval = true;
115: }
116: break;
117: case BAD_REAL_PATH:
118: break;
119: case BAD_VERSION:
120: String min = getMinVersion();
121: String max = getMaxVersion();
122: StringBuffer message = new StringBuffer();
123: message
124: .append(
125: parent.langpack
126: .getString("JDKPathPanel.badVersion1"))
127: .append(getDetectedVersion())
128: .append(
129: parent.langpack
130: .getString("JDKPathPanel.badVersion2"));
131: if (min != null && max != null)
132: message.append(min).append(" - ").append(max);
133: else if (min != null)
134: message.append(" >= ").append(min);
135: else if (max != null)
136: message.append(" <= ").append(max);
137:
138: message.append(parent.langpack
139: .getString("JDKPathPanel.badVersion3"));
140: if (askQuestion(parent.langpack
141: .getString("installer.warning"), message
142: .toString(), AbstractUIHandler.CHOICES_YES_NO,
143: AbstractUIHandler.ANSWER_NO) == AbstractUIHandler.ANSWER_YES) {
144: idata.setVariable(getVariableName(),
145: pathSelectionPanel.getPath());
146: retval = true;
147: }
148: break;
149: default:
150: throw new RuntimeException(
151: "Internal error: unknown result of version verification.");
152:
153: }
154: }
155: return (retval);
156: }
157:
158: /** Called when the panel becomes active. */
159: public void panelActivate() {
160: // Resolve the default for chosenPath
161: super .panelActivate();
162: String chosenPath;
163: // The variable will be exist if we enter this panel
164: // second time. We would maintain the previos
165: // selected path.
166: if (idata.getVariable(getVariableName()) != null)
167: chosenPath = idata.getVariable(getVariableName());
168: else {
169: if (OsVersion.IS_OSX) {
170: chosenPath = OSX_JDK_HOME;
171: } else {
172: // Try the JAVA_HOME as child dir of the jdk path
173: chosenPath = (new File(idata.getVariable("JAVA_HOME")))
174: .getParent();
175: }
176: }
177: // Set the path for method pathIsValid ...
178: pathSelectionPanel.setPath(chosenPath);
179:
180: if (!pathIsValid() || !verifyVersion()) {
181: chosenPath = resolveInRegistry();
182: if (!pathIsValid() || !verifyVersion())
183: chosenPath = "";
184: }
185: // Set the default to the path selection panel.
186: pathSelectionPanel.setPath(chosenPath);
187: String var = idata.getVariable("JDKPathPanel.skipIfValid");
188: // Should we skip this panel?
189: if (chosenPath.length() > 0 && var != null
190: && "yes".equalsIgnoreCase(var)) {
191: idata.setVariable(getVariableName(), chosenPath);
192: parent.skipPanel();
193: }
194:
195: }
196:
197: /**
198: * Returns the path to the needed JDK if found in the registry. If there are more than one JDKs
199: * registered, that one with the highest allowd version will be returned. Works only on windows.
200: * On Unix an empty string returns.
201: *
202: * @return the path to the needed JDK if found in the windows registry
203: */
204: private String resolveInRegistry() {
205: String retval = "";
206: int oldVal = 0;
207: RegistryHandler rh = null;
208: badRegEntries = new HashSet<String>();
209: try {
210: // Get the default registry handler.
211: rh = RegistryDefaultHandler.getInstance();
212: if (rh == null)
213: // We are on a os which has no registry or the
214: // needed dll was not bound to this installation. In
215: // both cases we forget the try to get the JDK path from registry.
216: return (retval);
217: rh.verify(idata);
218: oldVal = rh.getRoot(); // Only for security...
219: rh.setRoot(MSWinConstants.HKEY_LOCAL_MACHINE);
220: String[] keys = rh.getSubkeys(JDK_ROOT_KEY);
221: if (keys == null || keys.length == 0)
222: return (retval);
223: Arrays.sort(keys);
224: int i = keys.length - 1;
225: String min = getMinVersion();
226: String max = getMaxVersion();
227: // We search for the highest allowd version, therefore retrograde
228: while (i > 0) {
229: if (compareVersions(keys[i], max, false, 4, 4,
230: "__NO_NOT_IDENTIFIER_")) { // First allowd version found, now we have to test that the min value
231: // also allows this version.
232: if (compareVersions(keys[i], min, true, 4, 4,
233: "__NO_NOT_IDENTIFIER_")) {
234: String cv = JDK_ROOT_KEY + "\\" + keys[i];
235: String path = rh.getValue(cv, JDK_VALUE_NAME)
236: .getStringData();
237: // Use it only if the path is valid.
238: // Set the path for method pathIsValid ...
239: pathSelectionPanel.setPath(path);
240: if (!pathIsValid()) {
241: badRegEntries.add(keys[i]);
242: } else if ("".equals(retval)) {
243: retval = path;
244: }
245: pathSelectionPanel.setPath(retval);
246: }
247: }
248: i--;
249: }
250: } catch (Exception e) { // Will only be happen if registry handler is good, but an
251: // exception at performing was thrown. This is an error...
252: e.printStackTrace();
253: } finally {
254: if (rh != null && oldVal != 0)
255: try {
256: rh.setRoot(MSWinConstants.HKEY_LOCAL_MACHINE);
257: } catch (NativeLibException e) {
258: e.printStackTrace();
259: }
260: }
261: return (retval);
262: }
263:
264: private int verifyVersionEx() {
265: String min = getMinVersion();
266: String max = getMaxVersion();
267: int retval = OK;
268: // No min and max, version always ok.
269: if (min == null && max == null)
270: return (OK);
271:
272: if (!pathIsValid())
273: return (BAD_REAL_PATH);
274: // No get the version ...
275: // We cannot look to the version of this vm because we should
276: // test the given JDK VM.
277: String[] params = {
278: pathSelectionPanel.getPath() + File.separator + "bin"
279: + File.separator + "java", "-version" };
280: String[] output = new String[2];
281: FileExecutor fe = new FileExecutor();
282: fe.executeCommand(params, output);
283: // "My" VM writes the version on stderr :-(
284: String vs = (output[0].length() > 0) ? output[0] : output[1];
285: if (min != null) {
286: if (!compareVersions(vs, min, true, 4, 4,
287: "__NO_NOT_IDENTIFIER_"))
288: retval = BAD_VERSION;
289: }
290: if (max != null)
291: if (!compareVersions(vs, max, false, 4, 4,
292: "__NO_NOT_IDENTIFIER_"))
293: retval = BAD_VERSION;
294: if (retval == OK && badRegEntries != null
295: && badRegEntries.size() > 0) { // Test for bad registry entry.
296: if (badRegEntries.contains(getDetectedVersion()))
297: retval = BAD_REG_PATH;
298: }
299: return (retval);
300:
301: }
302:
303: private boolean verifyVersion() {
304: return (verifyVersionEx() <= 0);
305: }
306:
307: private boolean compareVersions(String in, String template,
308: boolean isMin, int assumedPlace, int halfRange,
309: String useNotIdentifier) {
310: StringTokenizer st = new StringTokenizer(in, " \t\n\r\f\"");
311: int i;
312: int currentRange = 0;
313: String[] interestedEntries = new String[halfRange + halfRange];
314: for (i = 0; i < assumedPlace - halfRange; ++i)
315: if (st.hasMoreTokens())
316: st.nextToken(); // Forget this entries.
317:
318: for (i = 0; i < halfRange + halfRange; ++i) { // Put the interesting Strings into an intermediaer array.
319: if (st.hasMoreTokens()) {
320: interestedEntries[i] = st.nextToken();
321: currentRange++;
322: }
323: }
324:
325: for (i = 0; i < currentRange; ++i) {
326: if (useNotIdentifier != null
327: && interestedEntries[i].indexOf(useNotIdentifier) > -1)
328: continue;
329: if (Character.getType(interestedEntries[i].charAt(0)) != Character.DECIMAL_DIGIT_NUMBER)
330: continue;
331: break;
332: }
333: if (i == currentRange) {
334: detectedVersion = "<not found>";
335: return (false);
336: }
337: detectedVersion = interestedEntries[i];
338: StringTokenizer current = new StringTokenizer(
339: interestedEntries[i], "._-");
340: StringTokenizer needed = new StringTokenizer(template, "._-");
341: while (needed.hasMoreTokens()) {
342: // Current can have no more tokens if needed has more
343: // and if a privious token was not accepted as good version.
344: // e.g. 1.4.2_02 needed, 1.4.2 current. The false return
345: // will be right here. Only if e.g. needed is 1.4.2_00 the
346: // return value will be false, but zero should not b e used
347: // at the last version part.
348: if (!current.hasMoreTokens())
349: return (false);
350: String cur = current.nextToken();
351: String nee = needed.nextToken();
352: int curVal = 0;
353: int neededVal = 0;
354: try {
355: curVal = Integer.parseInt(cur);
356: neededVal = Integer.parseInt(nee);
357: } catch (NumberFormatException nfe) { // A number format exception will be raised if
358: // there is a non numeric part in the version,
359: // e.g. 1.5.0_beta. The verification runs only into
360: // this deep area of version number (fourth sub place)
361: // if all other are equal to the given limit. Then
362: // it is right to return false because e.g.
363: // the minimal needed version will be 1.5.0.2.
364: return (false);
365: }
366: if (curVal < neededVal) {
367: if (isMin)
368: return (false);
369: return (true);
370: }
371: if (Integer.parseInt(cur) > Integer.parseInt(nee)) {
372: if (isMin)
373: return (true);
374: return (false);
375: }
376: }
377: return (true);
378: }
379:
380: /**
381: * Returns the current detected version.
382: *
383: * @return the current detected version
384: */
385: public String getDetectedVersion() {
386: return detectedVersion;
387: }
388:
389: /**
390: * Returns the current used maximum version.
391: *
392: * @return the current used maximum version
393: */
394: public String getMaxVersion() {
395: return maxVersion;
396: }
397:
398: /**
399: * Returns the current used minimum version.
400: *
401: * @return the current used minimum version
402: */
403: public String getMinVersion() {
404: return minVersion;
405: }
406:
407: /**
408: * Sets the given value as current detected version.
409: *
410: * @param string version string to be used as detected version
411: */
412: protected void setDetectedVersion(String string) {
413: detectedVersion = string;
414: }
415:
416: /**
417: * Sets the given value as maximum for version control.
418: *
419: * @param string version string to be used as maximum
420: */
421: protected void setMaxVersion(String string) {
422: if (string != null && string.length() > 0)
423: maxVersion = string;
424: else
425: maxVersion = "99.0.0";
426: }
427:
428: /**
429: * Sets the given value as minimum for version control.
430: *
431: * @param string version string to be used as minimum
432: */
433: protected void setMinVersion(String string) {
434: if (string != null && string.length() > 0)
435: minVersion = string;
436: else
437: minVersion = "1.0.0";
438: }
439:
440: /**
441: * Returns the name of the variable which should be used for the path.
442: *
443: * @return the name of the variable which should be used for the path
444: */
445: public String getVariableName() {
446: return variableName;
447: }
448:
449: /**
450: * Sets the name for the variable which should be set with the path.
451: *
452: * @param string variable name to be used
453: */
454: public void setVariableName(String string) {
455: variableName = string;
456: }
457:
458: /*
459: * (non-Javadoc)
460: *
461: * @see com.izforge.izpack.installer.IzPanel#getSummaryBody()
462: */
463: public String getSummaryBody() {
464: return (idata.getVariable(getVariableName()));
465: }
466: }
|