View Javadoc
1   /**
2    * Copyright 2014 Internet2
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  /*
17   * @author mchyzer
18   * $Id: GrouperVersion.java,v 1.9 2009-11-18 17:03:50 mchyzer Exp $
19   */
20  package edu.internet2.middleware.grouper.misc;
21  
22  import java.io.File;
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.Properties;
26  import java.util.Set;
27  import java.util.TreeMap;
28  import java.util.TreeSet;
29  import java.util.regex.Matcher;
30  import java.util.regex.Pattern;
31  
32  import org.apache.commons.lang.StringUtils;
33  import org.apache.commons.lang.builder.HashCodeBuilder;
34  import org.apache.commons.logging.Log;
35  
36  import edu.internet2.middleware.grouper.util.GrouperUtil;
37  import edu.internet2.middleware.grouperClient.util.ExpirableCache;
38  
39  
40  /**
41   * keep track of which version grouper is.  Update this file (the GrouperVersion.grouperVersion() constant) before each
42   * non-release-candidate release
43   */
44  public class GrouperVersion {
45    
46    /**
47     * return the parsed and tostring version of this version string (consistent), 
48     * or null if nothing passed in
49     * @param versionString
50     * @return the version string
51     */
52    public static String stringValueOrNull(String versionString) {
53      if (StringUtils.isBlank(versionString)) {
54        return null;
55      }
56      return valueOfIgnoreCase(versionString, true).toString();
57    }
58    
59    /**
60     * @see Object#equals(Object)
61     */
62    @Override
63    public boolean equals(Object obj) {
64      if (!(obj instanceof GrouperVersion)) {
65        return false;
66      }
67      GrouperVersion./../../edu/internet2/middleware/grouper/misc/GrouperVersion.html#GrouperVersion">GrouperVersion other = (GrouperVersion)obj;
68      if (this.build != other.build) {
69        return false;
70      }
71      if (this.major != other.major) {
72        return false;
73      }
74      if (this.minor != other.minor) {
75        return false;
76      }
77      if (!GrouperUtil.equals(this.rc, other.rc)) {
78        return false;
79      }
80      return true;
81    }
82  
83    
84    
85    /**
86     * @see java.lang.Object#hashCode()
87     */
88    @Override
89    public int hashCode() {
90      return new HashCodeBuilder().append(this.build).append(this.major).append(this.minor).append(this.rc).hashCode();
91    }
92  
93  
94  
95    /**
96     * 
97     * @see java.lang.Object#toString()
98     */
99    @Override
100   public String toString() {
101     String result = this.major + "." + this.minor + "." + this.build;
102     if (this.rcString != null) {
103       result+= this.rcString;
104     }
105     return result;
106   }
107   
108   private static String grouperVersionString = null;
109   
110   /**
111    * get the version from jar e.g. 2.5.12
112    * @return the version
113    */
114   public static String grouperVersion() {
115     if (grouperVersionString == null) {
116 
117       try {
118         grouperVersionString = GrouperCheckConfig.jarVersion(GrouperVersion.class);
119       } catch (Exception e) {
120         if (LOG.isDebugEnabled()) {
121           LOG.debug("Can't find version of grouper jar, using 2.5.0", e);
122         } else {
123           LOG.warn("Can't find version of grouper jar, using 2.5.0");
124         }
125       }
126       if (grouperVersionString == null) {
127         grouperVersionString = "2.5.0";
128       }
129     }
130     return grouperVersionString;
131   }
132   
133   /**
134    * se we dont have to keep constructing this
135    */
136   private static GrouperVersion currentVersion = null;
137   
138   /**
139    * current grouper version
140    * @return current grouper version
141    */
142   public static GrouperVersion currentVersion() {
143     if (currentVersion == null) {
144       currentVersion = valueOfIgnoreCase(grouperVersion());
145     }
146     return currentVersion;
147   }
148   
149   /** major number */
150   private int major;
151   
152   /** minor number */
153   private int minor;
154 
155   /** build number */
156   private int build;
157   
158   /** rc number (release cnadidate) */
159   private Integer rc;
160 
161   /** rc component full string including "rc" */
162   private String  rcString;
163 
164   /** keep a map of max size 1000 */
165   private static Map<String, GrouperVersion> versionCache = new HashMap<String, GrouperVersion>();
166 
167   /**
168    * convert string to version like an enum would
169    * 
170    * @param string cannot be blank
171    * @return the enum or null or exception if not found
172    */
173   public static GrouperVersion valueOfIgnoreCase(String string) {
174     return valueOfIgnoreCase(string, true);
175   }
176 
177   /**
178    * convert string to version like an enum would
179    * 
180    * @param string
181    * @param exceptionOnNull will not allow null or blank entries
182    * @return the enum or null or exception if not found
183    */
184   public static GrouperVersion valueOfIgnoreCase(String string, boolean exceptionOnNull) {
185 
186     if (StringUtils.isBlank(string)) {
187       if (exceptionOnNull) {
188         throw new RuntimeException("Not expecting a blank string for version");
189       }
190       return null;
191     }
192     
193     //see if in cache
194     GrouperVersion grouperVersion = versionCache.get(string);
195     if (grouperVersion != null) {
196       return grouperVersion;
197     }
198     
199     grouperVersion = new GrouperVersion(string);
200     
201     //dont cause a memory leak
202     if (versionCache.size() < 1000) {
203       versionCache.put(string, grouperVersion);
204     }
205     return grouperVersion;
206   }
207   
208   /**
209    * private constructor
210    * @param versionString
211    */
212   public GrouperVersion(String versionString) {
213     if (versionString != null) {
214       Matcher grouperMatcher = pattern.matcher(versionString);
215       if (!grouperMatcher.matches()) {
216         throw new RuntimeException("Invalid grouper version: " + versionString
217             + ", expecting something like: 1.2.3, 1.2.3rc4, or 1.2.3-SNAPSHOT");
218       }
219 
220       //get the grouper versions
221       this.major = GrouperUtil.intValue(grouperMatcher.group(1));
222       this.minor = GrouperUtil.intValue(grouperMatcher.group(2));
223       this.build = GrouperUtil.intValue(grouperMatcher.group(3));
224 
225       this.rcString = grouperMatcher.group(4);
226       if ("-SNAPSHOT".equals(grouperMatcher.group(4))) {
227         // snapshot will always be less than any rc version
228         this.rc = -1;
229       } else if (grouperMatcher.group(5) != null) {
230         this.rc = GrouperUtil.intValue(grouperMatcher.group(5));
231       }
232     }
233 
234   }
235   
236   /**
237    * see if the grouper version is greater than or equal to a certain version
238    * @param version
239    * @return true if the grouper version is greater than or equal to a certain version
240    */
241   public static boolean grouperVersionGreaterOrEqual(String version) {
242     return _grouperVersionGreaterOrEqualHelper(GrouperVersion.grouperVersion(), version);
243   }
244   
245   /**
246    * see if the grouper version is greater than or equal to a certain version
247    * @param version
248    * @return true if the grouper version is greater than or equal to a certain version
249    */
250   public boolean greaterOrEqualToArg(String version) {
251     return this.thisGreaterThanArgument(new GrouperVersion(version), true);
252   }
253   
254   /**
255    * see if this version is less than the argument one
256    * @param other
257    * @param orEqual 
258    * @return true if less than, false if equal or greater
259    */
260   public boolean lessThanArg(GrouperVersion other, boolean orEqual) {
261     //switch these around.  if a < b, then b > a
262     return other.thisGreaterThanArgument(this, orEqual);
263   }
264 
265   /**
266    * see if this version is less than the argument one, only considering major and minor version
267    * @param other
268    * @param orEqual 
269    * @return true if less than, false if equal or greater
270    */
271   public boolean lessThanMajorMinorArg(GrouperVersion other, boolean orEqual) {
272     
273     GrouperVersion thisMajorMinor = valueOfIgnoreCase(this.major + "." + this.minor + ".0");
274     GrouperVersion otherMajorMinor = valueOfIgnoreCase(other.major + "." + other.minor + ".0");
275     
276     return thisMajorMinor.lessThanArg(otherMajorMinor, orEqual);
277     
278   }
279 
280   /**
281    * see if this version is same argument one, only considering major and minor version
282    * @param other
283    * @param orEqual 
284    * @return true if less than, false if equal or greater
285    */
286   public boolean sameMajorMinorArg(GrouperVersion other) {
287     
288     return this.major == other.major && this.minor == other.minor;
289     
290   }
291 
292   /**
293    * see if this version is less than the argument one
294    * @param other
295    * @return true if less than, false if equal or greater
296    */
297   public boolean lessThanArg(GrouperVersion other) {
298     return this.lessThanArg(other, false);
299   }
300   
301   /**
302    * see if the grouper version is greater than or equal to a certain version
303    * @param version
304    * @return true if the grouper version is greater than or equal to a certain version
305    */
306   public boolean greaterOrEqualToArg(GrouperVersion version) {
307     return this.thisGreaterThanArgument(version, true);
308   }
309 
310   /**
311    * <pre>
312    * start of string, optional v or V, first digit, period or underscore, second digit, period or underscore, third digit, end of string
313    * parens are for capturing
314    * ^(\\d+)\\.(\\d+)\\.(\\d+)$
315    * </pre>
316    */
317   private static Pattern pattern = Pattern.compile("^[vV]?(\\d+)[\\._](\\d+)[\\._](\\d+)(-?rc(\\d+)|-SNAPSHOT)?$");
318 
319   /**
320    * helper method for unit testing
321    * @param grouperVersion
322    * @param anotherVersion
323    * @return true if grouper is greater
324    */
325   public static boolean _grouperVersionGreaterOrEqualHelper(String grouperVersion, String anotherVersion) {
326     GrouperVersionisc/GrouperVersion.html#GrouperVersion">GrouperVersion grouper = new GrouperVersion(grouperVersion);
327     GrouperVersionisc/GrouperVersion.html#GrouperVersion">GrouperVersion another = new GrouperVersion(anotherVersion);
328     //for grouper version to be greater or equal
329     return grouper.thisGreaterThanArgument(another, true);
330   }
331 
332   /**
333    * see if this version is greater than or equal the argument
334    * @param anotherVersion
335    * @param orEqual if true then count equals as a true
336    * @return true if this is less
337    */
338   private boolean thisGreaterThanArgument(GrouperVersion anotherVersion, boolean orEqual) {
339     //compare, first with major, minor, then build
340     if (this.major > anotherVersion.major) {
341       return true;
342     } else if (this.major < anotherVersion.major) {
343       return false;
344     }
345     if (this.minor > anotherVersion.minor) {
346       return true;
347     } else if (this.minor < anotherVersion.minor) {
348       return false;
349     }
350     //note its greater or equal
351     if (this.build > anotherVersion.build) {
352       return true;
353     } else if (this.build < anotherVersion.build) {
354       return false;
355     }
356     if (GrouperUtil.equals(this.rc, anotherVersion.rc)) {
357       return orEqual;
358     }
359     //not having a release candidate version is higher than having one
360     if (this.rc == null) {
361       return true;
362     }
363     if (anotherVersion.rc == null) {
364       return false;
365     }
366     return this.rc > anotherVersion.rc;
367   }
368   
369   /**
370    * cache this so we dont have to lookup patches all the time
371    */
372   private static ExpirableCache<Boolean, Map<String, Set<Integer>>> patchesInstalledCache = new ExpirableCache<Boolean, Map<String, Set<Integer>>>(10);
373   
374   /**
375    * logger 
376    */
377   private static final Log LOG = GrouperUtil.getLog(GrouperVersion.class);
378 
379 
380 }