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