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