    001    /*
    002     * Copyright (c) 2007-2014 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.property;
    022
    023    import java.util.Map;
    024    import java.util.Properties;
    025    import java.util.Set;
    026    import java.util.TreeSet;
    027
    028    import cascading.util.Util;
    029    import org.slf4j.Logger;
    030    import org.slf4j.LoggerFactory;
    031
    032    import static cascading.util.Util.join;
    033
    034    /**
    035     * Class AppProps is a fluent helper for setting various application level properties that every
    036     * {@link cascading.flow.Flow} may or may not be required to have set. These properties are typically passed to a Flow
    037     * via a {@link cascading.flow.FlowConnector}.
    038     * <p/>
    039     * New property settings that may be set in Cascading 2 are application name, version, and any tags.
    040     * <p/>
    041     * See {@link #addTag(String)} for examples of using tags to help manage an application.
    042     * <p/>
    043     * In prior releases, the FlowConnector was responsible for setting the "application jar" class or path. Those
    044     * methods have been deprecated and moved to AppProps.
    045     */
    046    public class AppProps extends Props
    047      {
    048      private static final Logger LOG = LoggerFactory.getLogger( AppProps.class );
    049
    050      public static final String APP_ID = "cascading.app.id";
    051      public static final String APP_NAME = "cascading.app.name";
    052      public static final String APP_VERSION = "cascading.app.version";
    053      public static final String APP_TAGS = "cascading.app.tags";
    054      public static final String APP_FRAMEWORKS = "cascading.app.frameworks";
    055      public static final String APP_JAR_CLASS = "cascading.app.appjar.class";
    056      public static final String APP_JAR_PATH = "cascading.app.appjar.path";
    057
    058      static final String DEP_APP_JAR_CLASS = "cascading.flowconnector.appjar.class";
    059      static final String DEP_APP_JAR_PATH = "cascading.flowconnector.appjar.path";
    060
    061      // need a global unique value here
    062      private static String appID;
    063
    064      protected String name;
    065      protected String version;
    066      protected Set<String> tags = new TreeSet<String>();
    067      protected Class jarClass;
    068      protected String jarPath;
    069      protected Set<String> frameworks = new TreeSet<String>();
    070
    071      /**
    072       * Creates a new AppProps instance.
    073       *
    074       * @return AppProps instance
    075       */
    076      public static AppProps appProps()
    077        {
    078        return new AppProps();
    079        }
    080
    081      /**
    082       * Method setApplicationJarClass is used to set the application jar file.
    083       * </p>
    084       * All cluster executed Cascading applications
    085       * need to call setApplicationJarClass(java.util.Map, Class) or
    086       * {@link #setApplicationJarPath(java.util.Map, String)}, otherwise ClassNotFound exceptions are likely.
    087       *
    088       * @param properties of type Map
    089       * @param type       of type Class
    090       */
    091      public static void setApplicationJarClass( Map<Object, Object> properties, Class type )
    092        {
    093        if( type != null )
    094          properties.put( APP_JAR_CLASS, type );
    095        }
    096
    097      /**
    098       * Method getApplicationJarClass returns the Class set by the setApplicationJarClass method.
    099       *
    100       * @param properties of type Map<Object, Object>
    101       * @return Class
    102       */
    103      public static Class getApplicationJarClass( Map<Object, Object> properties )
    104        {
    105        Class property = PropertyUtil.getProperty( properties, DEP_APP_JAR_CLASS, (Class) null );
    106
    107        if( property != null )
    108          {
    109          LOG.warn( "using deprecated property: {}, use instead: {}", DEP_APP_JAR_CLASS, APP_JAR_CLASS );
    110          return property;
    111          }
    112
    113        return PropertyUtil.getProperty( properties, APP_JAR_CLASS, (Class) null );
    114        }
    115
    116      /**
    117       * Method setApplicationJarPath is used to set the application jar file.
    118       * </p>
    119       * All cluster executed Cascading applications
    120       * need to call {@link #setApplicationJarClass(java.util.Map, Class)} or
    121       * setApplicationJarPath(java.util.Map, String), otherwise ClassNotFound exceptions are likely.
    122       *
    123       * @param properties of type Map
    124       * @param path       of type String
    125       */
    126      public static void setApplicationJarPath( Map<Object, Object> properties, String path )
    127        {
    128        if( path != null )
    129          properties.put( APP_JAR_PATH, path );
    130        }
    131
    132      /**
    133       * Method getApplicationJarPath return the path set by the setApplicationJarPath method.
    134       *
    135       * @param properties of type Map<Object, Object>
    136       * @return String
    137       */
    138      public static String getApplicationJarPath( Map<Object, Object> properties )
    139        {
    140        String property = PropertyUtil.getProperty( properties, DEP_APP_JAR_PATH, (String) null );
    141
    142        if( property != null )
    143          {
    144          LOG.warn( "using deprecated property: {}, use instead: {}", DEP_APP_JAR_PATH, APP_JAR_PATH );
    145          return property;
    146          }
    147
    148        return PropertyUtil.getProperty( properties, APP_JAR_PATH, (String) null );
    149        }
    150
    151      public static void setApplicationID( Map<Object, Object> properties )
    152        {
    153        properties.put( APP_ID, getAppID() );
    154        }
    155
    156      public static String getApplicationID( Map<Object, Object> properties )
    157        {
    158        if( properties == null )
    159          return getAppID();
    160
    161        return PropertyUtil.getProperty( properties, APP_ID, getAppID() );
    162        }
    163
    164      private static String getAppID()
    165        {
    166        if( appID == null )
    167          {
    168          appID = Util.createUniqueID();
    169          LOG.info( "using app.id: {}", appID );
    170          }
    171
    172        return appID;
    173        }
    174
    175      /** Sets the static appID value to null. For debugging purposes. */
    176      public static void resetAppID()
    177        {
    178        appID = null;
    179        }
    180
    181      public static void setApplicationName( Map<Object, Object> properties, String name )
    182        {
    183        if( name != null )
    184          properties.put( APP_NAME, name );
    185        }
    186
    187      public static String getApplicationName( Map<Object, Object> properties )
    188        {
    189        return PropertyUtil.getProperty( properties, APP_NAME, (String) null );
    190        }
    191
    192      public static void setApplicationVersion( Map<Object, Object> properties, String version )
    193        {
    194        if( version != null )
    195          properties.put( APP_VERSION, version );
    196        }
    197
    198      public static String getApplicationVersion( Map<Object, Object> properties )
    199        {
    200        return PropertyUtil.getProperty( properties, APP_VERSION, (String) null );
    201        }
    202
    203      public static void addApplicationTag( Map<Object, Object> properties, String tag )
    204        {
    205        if( tag == null )
    206          return;
    207
    208        String tags = PropertyUtil.getProperty( properties, APP_TAGS, (String) null );
    209
    210        if( tags != null )
    211          tags = join( ",", tag.trim(), tags );
    212        else
    213          tags = tag;
    214
    215        properties.put( APP_TAGS, tags );
    216        }
    217
    218      public static String getApplicationTags( Map<Object, Object> properties )
    219        {
    220        return PropertyUtil.getProperty( properties, APP_TAGS, (String) null );
    221        }
    222
    223      /**
    224       * Adds a framework "name:version" string to the property set and to the System properties.
    225       * <p/>
    226       * Properties may be null. Duplicates are removed.
    227       *
    228       * @param properties may be null, additionally adds to System properties
    229       * @param framework  "name:version" String
    230       */
    231      public static void addApplicationFramework( Map<Object, Object> properties, String framework )
    232        {
    233        if( framework == null )
    234          return;
    235
    236        String frameworks = PropertyUtil.getProperty( properties, APP_FRAMEWORKS, System.getProperty( APP_FRAMEWORKS ) );
    237
    238        if( frameworks != null )
    239          frameworks = join( ",", framework.trim(), frameworks );
    240        else
    241          frameworks = framework;
    242
    243        frameworks = Util.unique( frameworks, "," );
    244
    245        if( properties != null )
    246          properties.put( APP_FRAMEWORKS, frameworks );
    247
    248        System.setProperty( APP_FRAMEWORKS, frameworks );
    249        }
    250
    251      public static String getApplicationFrameworks( Map<Object, Object> properties )
    252        {
    253        return PropertyUtil.getProperty( properties, APP_FRAMEWORKS, System.getProperty( APP_FRAMEWORKS ) );
    254        }
    255
    256      public AppProps()
    257        {
    258        }
    259
    260      /**
    261       * Sets the name and version of this application.
    262       *
    263       * @param name    of type String
    264       * @param version of type String
    265       */
    266      public AppProps( String name, String version )
    267        {
    268        this.name = name;
    269        this.version = version;
    270        }
    271
    272      /**
    273       * Method setName sets the application name.
    274       * <p/>
    275       * By default the application name is derived from the jar name (values before the version in most Maven
    276       * compatible jars).
    277       *
    278       * @param name type String
    279       * @return this
    280       */
    281      public AppProps setName( String name )
    282        {
    283        this.name = name;
    284
    285        return this;
    286        }
    287
    288      /**
    289       * Method setVersion sets the application version.
    290       * <p/>
    291       * By default the application version is derived from the jar name (values after the name in most Maven
    292       * compatible jars).
    293       *
    294       * @param version type String
    295       * @return this
    296       */
    297      public AppProps setVersion( String version )
    298        {
    299        this.version = version;
    300
    301        return this;
    302        }
    303
    304      public String getTags()
    305        {
    306        return join( tags, "," );
    307        }
    308
    309      /**
    310       * Method addTag will associate a "tag" with this application. Applications can have an unlimited number of tags.
    311       * <p/>
    312       * Tags allow applications to be searched and organized by management tools.
    313       * <p/>
    314       * Tag values are opaque, but adopting a simple convention of 'category:value' allows for complex use cases.
    315       * <p/>
    316       * Some recommendations for categories are:
    317       * <p/>
    318       * <ul>
    319       * <lli>cluster: - the cluster name the application is or should be run against. A name could be logical, like QA or PROD.</lli>
    320       * <lli>project: - the project name, possibly a JIRA project name this application is managed under.</lli>
    321       * <lli>org: - the group, team or organization that is responsible for the application.</lli>
    322       * <lli>support: - the email address of the user who should be notified of failures or issues.</lli>
    323       * </ul>
    324       *
    325       * @param tag type String
    326       * @return this
    327       */
    328      public AppProps addTag( String tag )
    329        {
    330        if( !Util.isEmpty( tag ) )
    331          tags.add( tag );
    332
    333        return this;
    334        }
    335
    336      /**
    337       * Method addTags will associate the given "tags" with this application. Applications can have an unlimited number of tags.
    338       * <p/>
    339       * Tags allow applications to be searched and organized by management tools.
    340       * <p/>
    341       * Tag values are opaque, but adopting a simple convention of 'category:value' allows for complex use cases.
    342       * <p/>
    343       * Some recommendations for categories are:
    344       * <p/>
    345       * <ul>
    346       * <lli>cluster: - the cluster name the application is or should be run against. A name could be logical, like QA or PROD.</lli>
    347       * <lli>project: - the project name, possibly a JIRA project name this application is managed under.</lli>
    348       * <lli>org: - the group, team or organization that is responsible for the application.</lli>
    349       * <lli>support: - the email address of the user who should be notified of failures or issues.</lli>
    350       * </ul>
    351       *
    352       * @param tags type String
    353       * @return this
    354       */
    355      public AppProps addTags( String... tags )
    356        {
    357        for( String tag : tags )
    358          addTag( tag );
    359
    360        return this;
    361        }
    362
    363      /**
    364       * Method getFrameworks returns a list of frameworks used to build this App.
    365       *
    366       * @return Registered frameworks
    367       */
    368      public String getFrameworks()
    369        {
    370        return join( frameworks, "," );
    371        }
    372
    373      /**
    374       * Method addFramework adds a new framework name to the list of frameworks used.
    375       * <p/>
    376       * Higher level tools should register themselves, and preferably with their version,
    377       * for example {@code foo-flow-builder:1.2.3}.
    378       * <p/>
    379       * See {@link #addFramework(String, String)}.
    380       *
    381       * @param framework A String
    382       * @return this AppProps instance
    383       */
    384      public AppProps addFramework( String framework )
    385        {
    386        if( !Util.isEmpty( framework ) )
    387          frameworks.add( framework );
    388
    389        return this;
    390        }
    391
    392      /**
    393       * Method addFramework adds a new framework name and its version to the list of frameworks used.
    394       * <p/>
    395       * Higher level tools should register themselves, and preferably with their version,
    396       * for example {@code foo-flow-builder:1.2.3}.
    397       *
    398       * @param framework A String
    399       * @return this AppProps instance
    400       */
    401      public AppProps addFramework( String framework, String version )
    402        {
    403        if( !Util.isEmpty( framework ) && !Util.isEmpty( version ) )
    404          frameworks.add( framework + ":" + version );
    405
    406        if( !Util.isEmpty( framework ) )
    407          frameworks.add( framework );
    408
    409        return this;
    410        }
    411
    412      /**
    413       * Method addFrameworks adds new framework names to the list of frameworks used.
    414       * <p/>
    415       * Higher level tools should register themselves, and preferably with their version,
    416       * for example {@code foo-flow-builder:1.2.3}.
    417       *
    418       * @param frameworks Strings
    419       * @return this AppProps instance
    420       */
    421      public AppProps addFrameworks( String... frameworks )
    422        {
    423        for( String framework : frameworks )
    424          addFramework( framework );
    425
    426        return this;
    427        }
    428
    429      /**
    430       * Method setJarClass is used to set the application jar file.
    431       * </p>
    432       * All cluster executed Cascading applications
    433       * need to call setApplicationJarClass(java.util.Map, Class) or
    434       * {@link #setApplicationJarPath(java.util.Map, String)}, otherwise ClassNotFound exceptions are likely.
    435       *
    436       * @param jarClass of type Class
    437       */
    438      public AppProps setJarClass( Class jarClass )
    439        {
    440        this.jarClass = jarClass;
    441
    442        return this;
    443        }
    444
    445      /**
    446       * Method setJarPath is used to set the application jar file.
    447       * </p>
    448       * All cluster executed Cascading applications
    449       * need to call {@link #setJarClass(Class)} or
    450       * setJarPath(java.util.Map, String), otherwise ClassNotFound exceptions are likely.
    451       *
    452       * @param jarPath of type String
    453       */
    454      public AppProps setJarPath( String jarPath )
    455        {
    456        this.jarPath = jarPath;
    457
    458        return this;
    459        }
    460
    461      @Override
    462      protected void addPropertiesTo( Properties properties )
    463        {
    464        setApplicationID( properties );
    465        setApplicationName( properties, name );
    466        setApplicationVersion( properties, version );
    467        addApplicationTag( properties, getTags() );
    468        addApplicationFramework( properties, getFrameworks() );
    469        setApplicationJarClass( properties, jarClass );
    470        setApplicationJarPath( properties, jarPath );
    471        }
    472      }