001    /*
002     * Copyright (c) 2007-2015 Concurrent, Inc. All Rights Reserved.
003     *
004     * Project and contact information: http://www.cascading.org/
005     *
006     * This file is part of the Cascading project.
007     *
008     * Licensed under the Apache License, Version 2.0 (the "License");
009     * you may not use this file except in compliance with the License.
010     * 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    package cascading.util;
022    
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.net.URL;
026    import java.util.Collections;
027    import java.util.HashMap;
028    import java.util.HashSet;
029    import java.util.List;
030    import java.util.Map;
031    import java.util.Properties;
032    import java.util.Set;
033    
034    import org.slf4j.Logger;
035    import org.slf4j.LoggerFactory;
036    
037    /**
038     *
039     */
040    public class Version
041      {
042      private static final Logger LOG = LoggerFactory.getLogger( Version.class );
043    
044      private static boolean printedVersion = false;
045    
046      public static final String CASCADING_RELEASE_MAJOR = "cascading.release.major";
047      public static final String CASCADING_RELEASE_MINOR = "cascading.release.minor";
048      public static final String CASCADING_BUILD_NUMBER = "cascading.build.number";
049      public static final String CASCADING = "Cascading";
050    
051      public static Properties versionProperties;
052    
053      private static synchronized Properties getVersionProperties()
054        {
055        try
056          {
057          if( versionProperties == null )
058            {
059            versionProperties = loadVersionProperties();
060    
061            if( versionProperties.isEmpty() )
062              LOG.warn( "unable to load version information" );
063            }
064          }
065        catch( IOException exception )
066          {
067          LOG.warn( "unable to load version information", exception );
068          versionProperties = new Properties();
069          }
070    
071        return versionProperties;
072        }
073    
074      public static synchronized void printBanner()
075        {
076        // only print once
077        if( printedVersion )
078          return;
079    
080        printedVersion = true;
081    
082        String version = getVersionString();
083    
084        if( version != null )
085          LOG.info( version );
086        }
087    
088      public static String getVersionString()
089        {
090        if( getVersionProperties().isEmpty() )
091          return null;
092    
093        String releaseVersion;
094    
095        if( getReleaseBuild() == null || getReleaseBuild().isEmpty() )
096          releaseVersion = String.format( "Concurrent, Inc - %s %s", CASCADING, getReleaseFull() );
097        else
098          releaseVersion = String.format( "Concurrent, Inc - %s %s-%s", CASCADING, getReleaseFull(), getReleaseBuild() );
099    
100        return releaseVersion;
101        }
102    
103      public static String getRelease()
104        {
105        if( getVersionProperties().isEmpty() )
106          return null;
107    
108        if( getReleaseBuild() == null || getReleaseBuild().isEmpty() )
109          return String.format( "%s", getReleaseFull() );
110        else
111          return String.format( "%s-%s", getReleaseFull(), getReleaseBuild() );
112        }
113    
114      public static String getReleaseFull()
115        {
116        String releaseFull;
117    
118        if( getReleaseMinor() == null || getReleaseMinor().isEmpty() )
119          releaseFull = getReleaseMajor();
120        else
121          releaseFull = String.format( "%s.%s", getReleaseMajor(), getReleaseMinor() );
122    
123        return releaseFull;
124        }
125    
126      public static boolean hasMajorMinorVersionInfo()
127        {
128        return !Util.isEmpty( getReleaseMinor() ) && !Util.isEmpty( getReleaseMajor() );
129        }
130    
131      public static boolean hasAllVersionInfo()
132        {
133        return !Util.isEmpty( getReleaseBuild() ) && hasMajorMinorVersionInfo();
134        }
135    
136      public static String getReleaseBuild()
137        {
138        return getVersionProperties().getProperty( CASCADING_BUILD_NUMBER );
139        }
140    
141      public static String getReleaseMinor()
142        {
143        return getVersionProperties().getProperty( CASCADING_RELEASE_MINOR );
144        }
145    
146      public static String getReleaseMajor()
147        {
148        return getVersionProperties().getProperty( CASCADING_RELEASE_MAJOR );
149        }
150    
151      public static Properties loadVersionProperties() throws IOException
152        {
153        Properties properties = new Properties();
154    
155        List<URL> resources = Collections.list( Version.class.getClassLoader().getResources( "cascading/version.properties" ) );
156    
157        if( resources.isEmpty() )
158          return properties;
159    
160        warnOnDuplicate( resources );
161    
162        InputStream stream = resources.get( 0 ).openStream();
163    
164        if( stream == null )
165          return properties;
166    
167        try
168          {
169          properties.load( stream );
170          }
171        finally
172          {
173          stream.close();
174          }
175    
176        stream = Version.class.getClassLoader().getResourceAsStream( "cascading/build.number.properties" );
177    
178        if( stream != null )
179          {
180          try
181            {
182            properties.load( stream );
183            }
184          finally
185            {
186            stream.close();
187            }
188          }
189    
190        return properties;
191        }
192    
193      /**
194       * A shaded jar will have multiple version.properties, e.g.
195       * <pre>
196       * file:/mnt/var/lib/hadoop/tmp/hadoop-unjar7817209360894770970/cascading/version.properties
197       * jar:file:/mnt/single-load/./load-hadoop2-tez-20150729.jar!/cascading/version.properties
198       * </pre>
199       * <p/>
200       * only warn if there are duplicates within a protocol, not across since we should only be seeing file: and jar:
201       */
202      private static void warnOnDuplicate( List<URL> resources )
203        {
204        if( resources.size() == 1 )
205          return;
206    
207        Map<String, Set<String>> map = new HashMap<String, Set<String>>();
208    
209        for( URL resource : resources )
210          {
211          String protocol = resource.getProtocol();
212    
213          if( !map.containsKey( protocol ) )
214            map.put( protocol, new HashSet<String>() );
215    
216          map.get( protocol ).add( resource.toString() );
217          }
218    
219        for( String key : map.keySet() )
220          {
221          Set<String> values = map.get( key );
222    
223          if( values.size() > 1 )
224            LOG.warn( "found multiple 'cascading/version.properties' files on the CLASSPATH. Please check your dependencies: {}, using first returned", Util.join( values, "," ) );
225          }
226        }
227      }