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