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     * In prior releases, the FlowConnector was responsible for setting the "application jar" class or path. Those
040     * methods have been deprecated and moved to AppProps.
041     * <p/>
042     * New property settings that may be set in Cascading 2 are application name, version, and any tags.
043     */
044    public class AppProps extends Props
045      {
046      private static final Logger LOG = LoggerFactory.getLogger( AppProps.class );
047    
048      public static final String APP_ID = "cascading.app.id";
049      public static final String APP_NAME = "cascading.app.name";
050      public static final String APP_VERSION = "cascading.app.version";
051      public static final String APP_TAGS = "cascading.app.tags";
052      public static final String APP_FRAMEWORKS = "cascading.app.frameworks";
053      public static final String APP_JAR_CLASS = "cascading.app.appjar.class";
054      public static final String APP_JAR_PATH = "cascading.app.appjar.path";
055    
056      static final String DEP_APP_JAR_CLASS = "cascading.flowconnector.appjar.class";
057      static final String DEP_APP_JAR_PATH = "cascading.flowconnector.appjar.path";
058    
059      // need a global unique value here
060      private static String appID;
061    
062      protected String name;
063      protected String version;
064      protected Set<String> tags = new TreeSet<String>();
065      protected Class jarClass;
066      protected String jarPath;
067      protected Set<String> frameworks = new TreeSet<String>();
068    
069      public static AppProps appProps()
070        {
071        return new AppProps();
072        }
073    
074      /**
075       * Method setApplicationJarClass is used to set the application jar file.
076       * </p>
077       * All cluster executed Cascading applications
078       * need to call setApplicationJarClass(java.util.Map, Class) or
079       * {@link #setApplicationJarPath(java.util.Map, String)}, otherwise ClassNotFound exceptions are likely.
080       *
081       * @param properties of type Map
082       * @param type       of type Class
083       */
084      public static void setApplicationJarClass( Map<Object, Object> properties, Class type )
085        {
086        if( type != null )
087          properties.put( APP_JAR_CLASS, type );
088        }
089    
090      /**
091       * Method getApplicationJarClass returns the Class set by the setApplicationJarClass method.
092       *
093       * @param properties of type Map<Object, Object>
094       * @return Class
095       */
096      public static Class getApplicationJarClass( Map<Object, Object> properties )
097        {
098        Class property = PropertyUtil.getProperty( properties, DEP_APP_JAR_CLASS, (Class) null );
099    
100        if( property != null )
101          {
102          LOG.warn( "using deprecated property: {}, use instead: {}", DEP_APP_JAR_CLASS, APP_JAR_CLASS );
103          return property;
104          }
105    
106        return PropertyUtil.getProperty( properties, APP_JAR_CLASS, (Class) null );
107        }
108    
109      /**
110       * Method setApplicationJarPath is used to set the application jar file.
111       * </p>
112       * All cluster executed Cascading applications
113       * need to call {@link #setApplicationJarClass(java.util.Map, Class)} or
114       * setApplicationJarPath(java.util.Map, String), otherwise ClassNotFound exceptions are likely.
115       *
116       * @param properties of type Map
117       * @param path       of type String
118       */
119      public static void setApplicationJarPath( Map<Object, Object> properties, String path )
120        {
121        if( path != null )
122          properties.put( APP_JAR_PATH, path );
123        }
124    
125      /**
126       * Method getApplicationJarPath return the path set by the setApplicationJarPath method.
127       *
128       * @param properties of type Map<Object, Object>
129       * @return String
130       */
131      public static String getApplicationJarPath( Map<Object, Object> properties )
132        {
133        String property = PropertyUtil.getProperty( properties, DEP_APP_JAR_PATH, (String) null );
134    
135        if( property != null )
136          {
137          LOG.warn( "using deprecated property: {}, use instead: {}", DEP_APP_JAR_PATH, APP_JAR_PATH );
138          return property;
139          }
140    
141        return PropertyUtil.getProperty( properties, APP_JAR_PATH, (String) null );
142        }
143    
144      public static void setApplicationID( Map<Object, Object> properties )
145        {
146        properties.put( APP_ID, getAppID() );
147        }
148    
149      public static String getApplicationID( Map<Object, Object> properties )
150        {
151        if( properties == null )
152          return getAppID();
153    
154        return PropertyUtil.getProperty( properties, APP_ID, getAppID() );
155        }
156    
157      private static String getAppID()
158        {
159        if( appID == null )
160          {
161          appID = Util.createUniqueID();
162          LOG.info( "using app.id: {}", appID );
163          }
164    
165        return appID;
166        }
167    
168      /** Sets the static appID value to null. For debugging purposes. */
169      public static void resetAppID()
170        {
171        appID = null;
172        }
173    
174      public static void setApplicationName( Map<Object, Object> properties, String name )
175        {
176        if( name != null )
177          properties.put( APP_NAME, name );
178        }
179    
180      public static String getApplicationName( Map<Object, Object> properties )
181        {
182        return PropertyUtil.getProperty( properties, APP_NAME, (String) null );
183        }
184    
185      public static void setApplicationVersion( Map<Object, Object> properties, String version )
186        {
187        if( version != null )
188          properties.put( APP_VERSION, version );
189        }
190    
191      public static String getApplicationVersion( Map<Object, Object> properties )
192        {
193        return PropertyUtil.getProperty( properties, APP_VERSION, (String) null );
194        }
195    
196      public static void addApplicationTag( Map<Object, Object> properties, String tag )
197        {
198        if( tag == null )
199          return;
200    
201        String tags = PropertyUtil.getProperty( properties, APP_TAGS, (String) null );
202    
203        if( tags != null )
204          tags = join( ",", tag.trim(), tags );
205        else
206          tags = tag;
207    
208        properties.put( APP_TAGS, tags );
209        }
210    
211      public static String getApplicationTags( Map<Object, Object> properties )
212        {
213        return PropertyUtil.getProperty( properties, APP_TAGS, (String) null );
214        }
215    
216      /**
217       * Adds a framework "name:version" string to the property set and to the System properties.
218       * <p/>
219       * Properties may be null. Duplicates are removed.
220       *
221       * @param properties may be null, additionally adds to System properties
222       * @param framework  "name:version" String
223       */
224      public static void addApplicationFramework( Map<Object, Object> properties, String framework )
225        {
226        if( framework == null )
227          return;
228    
229        String frameworks = PropertyUtil.getProperty( properties, APP_FRAMEWORKS, System.getProperty( APP_FRAMEWORKS ) );
230    
231        if( frameworks != null )
232          frameworks = join( ",", framework.trim(), frameworks );
233        else
234          frameworks = framework;
235    
236        frameworks = Util.unique( frameworks, "," );
237    
238        if( properties != null )
239          properties.put( APP_FRAMEWORKS, frameworks );
240    
241        System.setProperty( APP_FRAMEWORKS, frameworks );
242        }
243    
244      public static String getApplicationFrameworks( Map<Object, Object> properties )
245        {
246        return PropertyUtil.getProperty( properties, APP_FRAMEWORKS, System.getProperty( APP_FRAMEWORKS ) );
247        }
248    
249      public AppProps()
250        {
251        }
252    
253      /**
254       * Sets the name and version of this application.
255       *
256       * @param name    of type String
257       * @param version of type String
258       */
259      public AppProps( String name, String version )
260        {
261        this.name = name;
262        this.version = version;
263        }
264    
265      public AppProps setName( String name )
266        {
267        this.name = name;
268    
269        return this;
270        }
271    
272      public AppProps setVersion( String version )
273        {
274        this.version = version;
275    
276        return this;
277        }
278    
279      public String getTags()
280        {
281        return join( tags, "," );
282        }
283    
284      public AppProps addTag( String tag )
285        {
286        if( !Util.isEmpty( tag ) )
287          tags.add( tag );
288    
289        return this;
290        }
291    
292      public AppProps addTags( String... tags )
293        {
294        for( String tag : tags )
295          addTag( tag );
296    
297        return this;
298        }
299    
300      /**
301       * Returns a list of frameworks used to build this App.
302       *
303       * @return Registered frameworks
304       */
305      public String getFrameworks()
306        {
307        return join( frameworks, "," );
308        }
309    
310      /**
311       * Adds a new framework name to the list of frameworks used.
312       * <p/>
313       * Higher level tools should register themselves, and preferably with their version,
314       * for example {@code foo-flow-builder:1.2.3}.
315       * <p/>
316       * See {@link #addFramework(String, String)}.
317       *
318       * @param framework A String
319       * @return this AppProps instance
320       */
321      public AppProps addFramework( String framework )
322        {
323        if( !Util.isEmpty( framework ) )
324          frameworks.add( framework );
325    
326        return this;
327        }
328    
329      /**
330       * Adds a new framework name and its version to the list of frameworks used.
331       * <p/>
332       * Higher level tools should register themselves, and preferably with their version,
333       * for example {@code foo-flow-builder:1.2.3}.
334       *
335       * @param framework A String
336       * @return this AppProps instance
337       */
338      public AppProps addFramework( String framework, String version )
339        {
340        if( !Util.isEmpty( framework ) && !Util.isEmpty( version ) )
341          frameworks.add( framework + ":" + version );
342    
343        if( !Util.isEmpty( framework ) )
344          frameworks.add( framework );
345    
346        return this;
347        }
348    
349      /**
350       * Adds new framework names to the list of frameworks used.
351       * <p/>
352       * Higher level tools should register themselves, and preferably with their version,
353       * for example {@code foo-flow-builder:1.2.3}.
354       *
355       * @param frameworks Strings
356       * @return this AppProps instance
357       */
358      public AppProps addFrameworks( String... frameworks )
359        {
360        for( String framework : frameworks )
361          addFramework( framework );
362    
363        return this;
364        }
365    
366      /**
367       * Method setJarClass is used to set the application jar file.
368       * </p>
369       * All cluster executed Cascading applications
370       * need to call setApplicationJarClass(java.util.Map, Class) or
371       * {@link #setApplicationJarPath(java.util.Map, String)}, otherwise ClassNotFound exceptions are likely.
372       *
373       * @param jarClass of type Class
374       */
375      public AppProps setJarClass( Class jarClass )
376        {
377        this.jarClass = jarClass;
378    
379        return this;
380        }
381    
382      /**
383       * Method setJarPath is used to set the application jar file.
384       * </p>
385       * All cluster executed Cascading applications
386       * need to call {@link #setJarClass(Class)} or
387       * setJarPath(java.util.Map, String), otherwise ClassNotFound exceptions are likely.
388       *
389       * @param jarPath of type String
390       */
391      public AppProps setJarPath( String jarPath )
392        {
393        this.jarPath = jarPath;
394    
395        return this;
396        }
397    
398      @Override
399      protected void addPropertiesTo( Properties properties )
400        {
401        setApplicationID( properties );
402        setApplicationName( properties, name );
403        setApplicationVersion( properties, version );
404        addApplicationTag( properties, getTags() );
405        addApplicationFramework( properties, getFrameworks() );
406        setApplicationJarClass( properties, jarClass );
407        setApplicationJarPath( properties, jarPath );
408        }
409      }