001: /*
002:
003: Derby - Class org.apache.derby.iapi.services.info.ProductVersionHolder
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.iapi.services.info;
023:
024: import java.io.InputStream;
025: import java.io.IOException;
026: import java.util.Properties;
027:
028: /**
029: Class to hold a cloudscape Product version.
030:
031: This class includes the following product version features.
032:
033: <OL>
034: <LI>Save the product version information this holds as a String. We call
035: the string a 'product version string'.
036: <LI>Construct a ProductVersionHolder from a valid 'product version string'.
037: <LI>Determine if two product versions are feature compatible. This means
038: products of these versions may interoperate with ***NO*** compatibility
039: problems.
040: <LI>Determine if two product versions are the same. This is a stronger
041: test than the test for feature compatibility.
042: </OL>
043:
044:
045:
046: Cloudscape 5.1 and older versions used the majorVersion, minorVersion, maintVersion versions
047: directly. That is a three part version number, majorVersion.minorVersion.maintVersion, e.g. 5.1.21.
048:
049: For Cloudscape 5.2 onwards a four part name is required.
050: majorVersion.minorVersion.fixPack.bugVersion e.g. 5.2.1.2
051:
052: This follows the IBM standard and allows us to state that a fix pack will be 5.2.3 without worrying
053: about how many maintence fixes there are between fix packs.
054:
055: We implement this using the existing format of ProductVersionHolder to reduce disruption to the
056: code, however we make the maintVersion encode the {fixPack.bugVersion}. Since the maintVersion
057: is represented by a int (2G values) we have plenty of room for encoding. If we assign a given
058: majorVersion.minorVersion.fixPack a 10 year life, then we about the maximum number of individual releases
059: it can have is 10 years * 365 days/year = 3650. Thus with the pre 5.2 scheme we would not expect a
060: 5.1.x to have an x > 3650 (approximately). Usually the rate of point releases has been much less than
061: one per day, 5.1.31 is released about 225 days after GA which makes around a point release every 7 days.
062: But in the encoding we need to be conservative. With fix packs the maximum is about 2 per year and fix
063: packs are only made to the current release, thus with a yearly minor release cycle we would imagine
064: only 2 fixpacks per major.minor. However like other IBM products or release cycle may be extended thus
065: we can expect up to a handful of fix packs.
066:
067: Thus we might imagine releases like
068:
069: 5.2.0.12
070: 5.2.0.234
071: 5.2.1.34
072: 5.2.4.2445
073:
074: but highly unlikey to have
075:
076: 5.2.2.59321
077: 5.2.23.1
078:
079:
080: The encoding number must continue to increase so that the
081:
082: encodedMaintB > encodedMaintA
083:
084: if (fixPackB > fixPackA) || ((fixPackB == fixPackA) && (bugB > bugA))
085:
086:
087: Selected encoding
088:
089: encodedMaint = (fixPack * 1,000,000) + (bugVersion);
090:
091: Handles many many fixpacks and upto one million bug fixes per fix pack and remains somewhat human readable.
092:
093: Special fix packs
094:
095: fixpack == 0 = alpha (version off main codeline)
096: fixpack == 1 = first release of major.minor (may be marked with beta)
097: fixpack == 2 = first fix pack (displayed as 1)
098:
099:
100: The drdaMaintVersion is sent in the Network Server PRDID. It never displays
101: but may be used by the client for version specific behaviour. It should be
102: reset to 0 with each minor release.
103:
104: The product version string has the form:
105: <PRE>
106: productVendorName - ProductName - majorVersion.minorVersion.maintVersion [beta] - (buildNumber)
107:
108: </PRE>
109:
110: */
111: public final class ProductVersionHolder implements
112: java.security.PrivilegedAction {
113:
114: //
115: //Used as an invalid value for numbers. This works because all
116: //the numbers in a product version must be non-negative.
117: private static final int BAD_NUMBER = -1;
118: private static final String ALPHA = "alpha";
119: private static final String BETA = "beta";
120:
121: private final static int MAINT_ENCODING = 1000000;
122:
123: private String productVendorName;
124: private String productName;
125: private String productTechnologyName;
126: private int majorVersion = BAD_NUMBER;
127: private int minorVersion = BAD_NUMBER;
128: private int maintVersion = BAD_NUMBER;
129: private int drdaMaintVersion = BAD_NUMBER;
130: private String buildNumber = "????";
131: private Boolean isBeta;
132:
133: private ProductVersionHolder() {
134: }
135:
136: /**
137: Create a ProductVersionHolder
138:
139: <P>Please see the documentation for the varient of getProductVesionHolder
140: that takes the same parameters as this for a description of the parameters.
141: */
142: private ProductVersionHolder(String productVendorName,
143: String productName, String productTechnologyName,
144: int majorVersion, int minorVersion, int maintVersion,
145: int drdaMaintVersion, String buildNumber, Boolean isBeta) {
146: if (productVendorName != null)
147: this .productVendorName = productVendorName.trim();
148: if (productName != null)
149: this .productName = productName.trim();
150: if (productTechnologyName != null)
151: this .productTechnologyName = productTechnologyName.trim();
152: this .majorVersion = majorVersion;
153: this .minorVersion = minorVersion;
154: this .maintVersion = maintVersion;
155: this .drdaMaintVersion = drdaMaintVersion;
156: this .buildNumber = buildNumber;
157: this .isBeta = isBeta;
158: }
159:
160: /**
161: Create a valid ProductVersionHolder. If any of the
162: parameters provided is invalid, this returns null.
163: @param productName The name of the product. productName.length()
164: must be greater than 0. The syntax for a product name is
165: 'productGenus[:productSpecies]'.
166: @param majorVersion The most significant portion of a 3
167: part product version. Must be non-negative.
168: @param minorVersion The second portion of a 3 part
169: product version. Must be non-negative.
170: @param maintVersion The least significant portion of a 3 part
171: product version. Must be non-negative.
172: @param drdaMaintVersion The protocol modification number for minor release.
173: @param buildNumber The buildNumber for a product.
174: @param isBeta true iff the product is beta.
175: @return A valid ProductVersionHolder of null if any of the parameters
176: provided are not valid.
177: */
178: public static ProductVersionHolder getProductVersionHolder(
179: String productVendorName, String productName,
180: String productTechnologyName, int majorVersion,
181: int minorVersion, int maintVersion, int drdaMaintVersion,
182: String buildNumber, Boolean isBeta) {
183: ProductVersionHolder pvh = new ProductVersionHolder(
184: productVendorName, productName, productTechnologyName,
185: majorVersion, minorVersion, maintVersion,
186: drdaMaintVersion, buildNumber, isBeta);
187: return pvh;
188: }
189:
190: /**
191: Get a ProductVersionHolder for a product of a given genus,
192: that is available in the caller's environment.
193: Even though this uses a priv bock, it may stil fail when
194: the jar the version is being fetched from, is different to the
195: one that loaded this class, AND the jars are in different security contexts.
196:
197: @param productGenus The genus for the product.
198: @return The ProductVersionHolder or null if
199: a product with the given genus is not available in the
200: caller's environment.
201: */
202: public static ProductVersionHolder getProductVersionHolderFromMyEnv(
203: String productGenus) {
204:
205: ProductVersionHolder tempPVH = new ProductVersionHolder();
206:
207: tempPVH.productGenus = productGenus;
208: Properties p = (Properties) java.security.AccessController
209: .doPrivileged(tempPVH);
210:
211: if (p == null)
212: return null;
213:
214: return getProductVersionHolder(p);
215: }
216:
217: /**
218: Load the version info from the already opened properties files.
219: We need to do this because if the jar files (e.g. db2jtools and db2j)
220: are in different security contexts (entries in the policy files) then
221: we cannot load the version information for one of them correctly.
222: This is because the this class will either have been loaded from
223: only one of the jars and hence can only access the resource in its own jar.
224: By making code specific to the jar open the resource we are guaranteed it will work.
225: */
226: public static ProductVersionHolder getProductVersionHolderFromMyEnv(
227: InputStream propertiesStream) {
228:
229: if (propertiesStream == null)
230: return null;
231:
232: Properties p = new Properties();
233: try {
234: p.load(propertiesStream);
235: } catch (IOException ioe) {
236:
237: System.out.println("IOE " + ioe.getMessage());
238: //
239: //This case is a bit ugly. If we get an IOException, we return
240: //null. Though this correctly reflects that the product is not
241: //available for use, it may be confusing to users that we swallow
242: //the IO error here.
243: return null;
244: } finally {
245: try {
246: propertiesStream.close();
247: } catch (IOException ioe2) {
248: }
249: }
250:
251: return getProductVersionHolder(p);
252: }
253:
254: /**
255: Get a ProductVersionHolder based on the information in
256: the Properties object provided.
257:
258: @param p The properties object that holds the productVersion
259: information.
260: @return The ProductVersionHolder or null if
261: a product with the given genus is not available in the
262: caller's environment.
263: */
264: public static ProductVersionHolder getProductVersionHolder(
265: Properties p) {
266: String pvn = p.getProperty(PropertyNames.PRODUCT_VENDOR_NAME);
267: String pn = p.getProperty(PropertyNames.PRODUCT_EXTERNAL_NAME);
268: String ptn = p
269: .getProperty(PropertyNames.PRODUCT_TECHNOLOGY_NAME);
270: int v1 = parseInt(p
271: .getProperty(PropertyNames.PRODUCT_MAJOR_VERSION));
272: int v2 = parseInt(p
273: .getProperty(PropertyNames.PRODUCT_MINOR_VERSION));
274: int v3 = parseInt(p
275: .getProperty(PropertyNames.PRODUCT_MAINT_VERSION));
276: int v4 = parseInt(p
277: .getProperty(PropertyNames.PRODUCT_DRDA_MAINT_VERSION));
278: String bn = p.getProperty(PropertyNames.PRODUCT_BUILD_NUMBER);
279: Boolean isBeta = Boolean.valueOf(p
280: .getProperty(PropertyNames.PRODUCT_BETA_VERSION));
281: return getProductVersionHolder(pvn, pn, ptn, v1, v2, v3, v4,
282: bn, isBeta);
283: }
284:
285: /**
286: Return the product vendor name.
287: */
288: public String getProductVendorName() {
289: return productVendorName;
290: }
291:
292: /**
293: Return the external product name.
294: */
295: public String getProductName() {
296: return productName;
297: }
298:
299: public String getProductTechnologyName() {
300: return productTechnologyName;
301: }
302:
303: /**
304: Return the major version number.
305: */
306: public int getMajorVersion() {
307: return majorVersion;
308: }
309:
310: /**
311: Return the minor version number.
312: */
313: public int getMinorVersion() {
314: return minorVersion;
315: }
316:
317: /**
318: Return the <B>encoded</B> maintainence version number.
319: */
320: public int getMaintVersion() {
321: return maintVersion;
322: }
323:
324: /**
325: Return the drda protocol maintenance version for this minor release.
326: Starts at 0 for each minor release and only incremented
327: when client behaviour changes based on the server version.
328: **/
329: public int getDrdaMaintVersion() {
330: return drdaMaintVersion;
331: }
332:
333: /**
334: Return the fix pack version from the maintence encoding.
335: */
336: public int getFixPackVersion() {
337: return maintVersion / MAINT_ENCODING;
338: }
339:
340: /**
341: Return true if this is a beta product.
342: */
343: public boolean isBeta() {
344: return isBeta.booleanValue();
345: }
346:
347: /**
348: Return true if this is a alpha product.
349: */
350: public boolean isAlpha() {
351: return (majorVersion >= 5) && (minorVersion > 2)
352: && ((maintVersion / MAINT_ENCODING) == 0);
353: }
354:
355: /**
356: Return the build number for this product.
357: */
358: public String getBuildNumber() {
359: return buildNumber;
360: }
361:
362: /**
363: * Return the build number as an integer if possible,
364: * mapping from the SVN number.
365: * nnnnn -> returns nnnnn
366: * nnnnnM -> returns -nnnnn indicates a modified code base
367: * nnnnn:mmmmm -> returns -nnnnn
368: * anything else -> returns -1
369: */
370: public int getBuildNumberAsInt() {
371: if (buildNumber == null)
372: return -1;
373: boolean dubiousCode = false;
374: int offset = buildNumber.indexOf('M');
375: if (offset == -1)
376: offset = buildNumber.indexOf(':');
377: else
378: dubiousCode = true;
379: if (offset == -1)
380: offset = buildNumber.length();
381: else
382: dubiousCode = true;
383:
384: try {
385: int bnai = Integer.parseInt(buildNumber
386: .substring(0, offset));
387: if (dubiousCode)
388: bnai = -bnai;
389: return bnai;
390: } catch (NumberFormatException nfe) {
391: return -1;
392: }
393: }
394:
395: /**
396: Parse a string containing a non-negative integer. Return
397: a negative integer is the String is invalid.
398:
399: @param s A string with a non-negative integer (a sequence
400: of decimal digits.)
401: @return the integer or a negative number if s is invalid.
402: */
403: private static int parseInt(String s) {
404: //System.out.println("Parsing integer: "+s);
405: int result = BAD_NUMBER;
406: try {
407: if (s != null)
408: result = Integer.parseInt(s);
409: } catch (NumberFormatException nfe) {
410: }
411:
412: if (result < 0)
413: result = BAD_NUMBER;
414: return result;
415: }
416:
417: /**
418: Return a string representation of this ProductVersion. The
419: difference between this and createProductVersionString, is
420: that this method retruns a String when this ProductVersionHolder
421: holds invalid version information.
422: */
423: public String toString() {
424: StringBuffer sb = new StringBuffer();
425: sb.append(getProductVendorName());
426: sb.append(" - ");
427: sb.append(getProductName());
428: sb.append(" - ");
429: sb.append(getVersionBuildString(true));
430: return sb.toString();
431: }
432:
433: /**
434: Return the feature version string, ie. major.minor. (e.g. 5.2)
435: */
436: public String getSimpleVersionString() {
437:
438: return ProductVersionHolder.simpleVersionString(majorVersion,
439: minorVersion, isBeta());
440: }
441:
442: /**
443: Convert a major and minor number with beta status into a string.
444: */
445: public static String simpleVersionString(int major, int minor,
446: boolean isBeta) {
447:
448: StringBuffer sb = new StringBuffer();
449:
450: sb.append(major);
451: sb.append('.');
452: sb.append(minor);
453: if (isBeta) {
454: sb.append(' ');
455: sb.append(BETA);
456: }
457:
458: return sb.toString();
459: }
460:
461: public static String fullVersionString(int major, int minor,
462: int maint, boolean isBeta, String build) {
463: StringBuffer sb = new StringBuffer();
464: sb.append(major);
465: sb.append('.');
466: sb.append(minor);
467: sb.append('.');
468:
469: String preRelease = null;
470: if (major == 5 && minor <= 2 && maint < MAINT_ENCODING) {
471: sb.append(maint);
472: if (isBeta)
473: preRelease = BETA;
474: } else {
475: int fixPack = maint / MAINT_ENCODING;
476: int bugVersion = maint % MAINT_ENCODING;
477: sb.append(fixPack);
478: sb.append('.');
479: sb.append(bugVersion);
480:
481: if (fixPack == 0) {
482: preRelease = ALPHA;
483: } else if (isBeta) {
484: preRelease = BETA;
485: }
486: }
487:
488: if (preRelease != null) {
489: sb.append(' ');
490: sb.append(preRelease);
491: }
492: if (build != null) {
493: sb.append(" - (");
494:
495: sb.append(build);
496: sb.append(')');
497: }
498: return sb.toString();
499: }
500:
501: /**
502: Returns a short-hand value for the product version string.
503: Used by Sysinfo.
504: Includes the optional <beta> designation
505: */
506: public String getVersionBuildString(boolean withBuild) {
507: return ProductVersionHolder.fullVersionString(majorVersion,
508: minorVersion, maintVersion, isBeta(),
509: withBuild ? buildNumber : null);
510: }
511:
512: /*
513: ** Security related methods
514: */
515: private String productGenus;
516:
517: public final Object run() {
518:
519: // SECURITY PERMISSION - IP4
520: return loadProperties(this .productGenus);
521: }
522:
523: // SECURITY PERMISSION - IP4
524: private Properties loadProperties(String productGenus) {
525: String resourceName = "/org/apache/derby/info/" + productGenus
526: + ".properties";
527:
528: InputStream is = getClass().getResourceAsStream(resourceName);
529: if (is == null) {
530: return null;
531: }
532:
533: Properties p = new Properties();
534: try {
535: p.load(is);
536: return p;
537: } catch (IOException ioe) {
538: //
539: //This case is a bit ugly. If we get an IOException, we return
540: //null. Though this correctly reflects that the product is not
541: //available for use, it may be confusing to users that we swallow
542: //the IO error here.
543: return null;
544: }
545: }
546: }
|