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.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 tag = tag.trim(); 209 210 if( Util.containsWhitespace( tag ) ) 211 LOG.warn( "tags should not contain whitespace characters: '{}'", tag ); 212 213 String tags = PropertyUtil.getProperty( properties, APP_TAGS, (String) null ); 214 215 if( tags != null ) 216 tags = join( ",", tag, tags ); 217 else 218 tags = tag; 219 220 properties.put( APP_TAGS, tags ); 221 } 222 223 public static String getApplicationTags( Map<Object, Object> properties ) 224 { 225 return PropertyUtil.getProperty( properties, APP_TAGS, (String) null ); 226 } 227 228 /** 229 * Adds a framework "name:version" string to the property set and to the System properties. 230 * <p/> 231 * Properties may be null. Duplicates are removed. 232 * 233 * @param properties may be null, additionally adds to System properties 234 * @param framework "name:version" String 235 */ 236 public static void addApplicationFramework( Map<Object, Object> properties, String framework ) 237 { 238 if( framework == null ) 239 return; 240 241 String frameworks = PropertyUtil.getProperty( properties, APP_FRAMEWORKS, System.getProperty( APP_FRAMEWORKS ) ); 242 243 if( frameworks != null ) 244 frameworks = join( ",", framework.trim(), frameworks ); 245 else 246 frameworks = framework; 247 248 frameworks = Util.unique( frameworks, "," ); 249 250 if( properties != null ) 251 properties.put( APP_FRAMEWORKS, frameworks ); 252 253 System.setProperty( APP_FRAMEWORKS, frameworks ); 254 } 255 256 public static String getApplicationFrameworks( Map<Object, Object> properties ) 257 { 258 return PropertyUtil.getProperty( properties, APP_FRAMEWORKS, System.getProperty( APP_FRAMEWORKS ) ); 259 } 260 261 public AppProps() 262 { 263 } 264 265 /** 266 * Sets the name and version of this application. 267 * 268 * @param name of type String 269 * @param version of type String 270 */ 271 public AppProps( String name, String version ) 272 { 273 this.name = name; 274 this.version = version; 275 } 276 277 /** 278 * Method setName sets the application name. 279 * <p/> 280 * By default the application name is derived from the jar name (values before the version in most Maven 281 * compatible jars). 282 * 283 * @param name type String 284 * @return this 285 */ 286 public AppProps setName( String name ) 287 { 288 this.name = name; 289 290 return this; 291 } 292 293 /** 294 * Method setVersion sets the application version. 295 * <p/> 296 * By default the application version is derived from the jar name (values after the name in most Maven 297 * compatible jars). 298 * 299 * @param version type String 300 * @return this 301 */ 302 public AppProps setVersion( String version ) 303 { 304 this.version = version; 305 306 return this; 307 } 308 309 public String getTags() 310 { 311 return join( tags, "," ); 312 } 313 314 /** 315 * Method addTag will associate a "tag" with this application. Applications can have an unlimited number of tags. 316 * <p/> 317 * Tags allow applications to be searched and organized by management tools. 318 * <p/> 319 * Tag values are opaque, but adopting a simple convention of 'category:value' allows for complex use cases. 320 * <p/> 321 * Some recommendations for categories are: 322 * <p/> 323 * <ul> 324 * <lli>cluster: - the cluster name the application is or should be run against. A name could be logical, like QA or PROD.</lli> 325 * <lli>project: - the project name, possibly a JIRA project name this application is managed under.</lli> 326 * <lli>org: - the group, team or organization that is responsible for the application.</lli> 327 * <lli>support: - the email address of the user who should be notified of failures or issues.</lli> 328 * </ul> 329 * <p/> 330 * Note that tags should not contain whitespace characters, even though this is not an error, a warning will be 331 * issues. 332 * 333 * @param tag type String 334 * @return this 335 */ 336 public AppProps addTag( String tag ) 337 { 338 if( !Util.isEmpty( tag ) ) 339 tags.add( tag ); 340 341 return this; 342 } 343 344 /** 345 * Method addTags will associate the given "tags" with this application. Applications can have an unlimited number of tags. 346 * <p/> 347 * Tags allow applications to be searched and organized by management tools. 348 * <p/> 349 * Tag values are opaque, but adopting a simple convention of 'category:value' allows for complex use cases. 350 * <p/> 351 * Some recommendations for categories are: 352 * <p/> 353 * <ul> 354 * <lli>cluster: - the cluster name the application is or should be run against. A name could be logical, like QA or PROD.</lli> 355 * <lli>project: - the project name, possibly a JIRA project name this application is managed under.</lli> 356 * <lli>org: - the group, team or organization that is responsible for the application.</lli> 357 * <lli>support: - the email address of the user who should be notified of failures or issues.</lli> 358 * </ul> 359 * <p/> 360 * Note that tags should not contain whitespace characters, even though this is not an error, a warning will be 361 * issues. 362 * 363 * @param tags type String 364 * @return this 365 */ 366 public AppProps addTags( String... tags ) 367 { 368 for( String tag : tags ) 369 addTag( tag ); 370 371 return this; 372 } 373 374 /** 375 * Method getFrameworks returns a list of frameworks used to build this App. 376 * 377 * @return Registered frameworks 378 */ 379 public String getFrameworks() 380 { 381 return join( frameworks, "," ); 382 } 383 384 /** 385 * Method addFramework adds a new framework name to the list of frameworks used. 386 * <p/> 387 * Higher level tools should register themselves, and preferably with their version, 388 * for example {@code foo-flow-builder:1.2.3}. 389 * <p/> 390 * See {@link #addFramework(String, String)}. 391 * 392 * @param framework A String 393 * @return this AppProps instance 394 */ 395 public AppProps addFramework( String framework ) 396 { 397 if( !Util.isEmpty( framework ) ) 398 frameworks.add( framework ); 399 400 return this; 401 } 402 403 /** 404 * Method addFramework adds a new framework name and its version to the list of frameworks used. 405 * <p/> 406 * Higher level tools should register themselves, and preferably with their version, 407 * for example {@code foo-flow-builder:1.2.3}. 408 * 409 * @param framework A String 410 * @return this AppProps instance 411 */ 412 public AppProps addFramework( String framework, String version ) 413 { 414 if( !Util.isEmpty( framework ) && !Util.isEmpty( version ) ) 415 frameworks.add( framework + ":" + version ); 416 417 if( !Util.isEmpty( framework ) ) 418 frameworks.add( framework ); 419 420 return this; 421 } 422 423 /** 424 * Method addFrameworks adds new framework names to the list of frameworks used. 425 * <p/> 426 * Higher level tools should register themselves, and preferably with their version, 427 * for example {@code foo-flow-builder:1.2.3}. 428 * 429 * @param frameworks Strings 430 * @return this AppProps instance 431 */ 432 public AppProps addFrameworks( String... frameworks ) 433 { 434 for( String framework : frameworks ) 435 addFramework( framework ); 436 437 return this; 438 } 439 440 /** 441 * Method setJarClass is used to set the application jar file. 442 * </p> 443 * All cluster executed Cascading applications 444 * need to call setApplicationJarClass(java.util.Map, Class) or 445 * {@link #setApplicationJarPath(java.util.Map, String)}, otherwise ClassNotFound exceptions are likely. 446 * 447 * @param jarClass of type Class 448 */ 449 public AppProps setJarClass( Class jarClass ) 450 { 451 this.jarClass = jarClass; 452 453 return this; 454 } 455 456 /** 457 * Method setJarPath is used to set the application jar file. 458 * </p> 459 * All cluster executed Cascading applications 460 * need to call {@link #setJarClass(Class)} or 461 * setJarPath(java.util.Map, String), otherwise ClassNotFound exceptions are likely. 462 * 463 * @param jarPath of type String 464 */ 465 public AppProps setJarPath( String jarPath ) 466 { 467 this.jarPath = jarPath; 468 469 return this; 470 } 471 472 @Override 473 protected void addPropertiesTo( Properties properties ) 474 { 475 setApplicationID( properties ); 476 setApplicationName( properties, name ); 477 setApplicationVersion( properties, version ); 478 addApplicationTag( properties, getTags() ); 479 addApplicationFramework( properties, getFrameworks() ); 480 setApplicationJarClass( properties, jarClass ); 481 setApplicationJarPath( properties, jarPath ); 482 } 483 }