001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.service; 020 021import java.io.File; 022import java.io.IOException; 023import java.net.URI; 024import java.net.URISyntaxException; 025import java.net.URL; 026import java.net.URLDecoder; 027import java.text.MessageFormat; 028import java.text.ParseException; 029import java.text.SimpleDateFormat; 030import java.util.ArrayList; 031import java.util.Arrays; 032import java.util.Calendar; 033import java.util.Comparator; 034import java.util.Date; 035import java.util.Enumeration; 036import java.util.HashMap; 037import java.util.HashSet; 038import java.util.List; 039import java.util.Map; 040import java.util.Properties; 041import java.util.Set; 042import java.util.TimeZone; 043import java.util.Map.Entry; 044 045import org.apache.commons.lang.StringUtils; 046import org.apache.hadoop.fs.FileStatus; 047import org.apache.hadoop.fs.FileSystem; 048import org.apache.hadoop.fs.Path; 049import org.apache.hadoop.fs.PathFilter; 050import org.apache.hadoop.fs.permission.FsPermission; 051import org.apache.oozie.action.ActionExecutor; 052import org.apache.oozie.action.hadoop.JavaActionExecutor; 053import org.apache.oozie.client.rest.JsonUtils; 054import org.apache.oozie.hadoop.utils.HadoopShims; 055import org.apache.oozie.util.Instrumentable; 056import org.apache.oozie.util.Instrumentation; 057import org.apache.oozie.util.XLog; 058 059import com.google.common.annotations.VisibleForTesting; 060 061import org.apache.oozie.ErrorCode; 062 063public class ShareLibService implements Service, Instrumentable { 064 065 public static final String LAUNCHERJAR_LIB_RETENTION = CONF_PREFIX + "ShareLibService.temp.sharelib.retention.days"; 066 067 public static final String SHARELIB_MAPPING_FILE = CONF_PREFIX + "ShareLibService.mapping.file"; 068 069 public static final String SHIP_LAUNCHER_JAR = "oozie.action.ship.launcher.jar"; 070 071 public static final String PURGE_INTERVAL = CONF_PREFIX + "ShareLibService.purge.interval"; 072 073 public static final String FAIL_FAST_ON_STARTUP = CONF_PREFIX + "ShareLibService.fail.fast.on.startup"; 074 075 private static final String PERMISSION_STRING = "-rwxr-xr-x"; 076 077 public static final String LAUNCHER_PREFIX = "launcher_"; 078 079 public static final String SHARED_LIB_PREFIX = "lib_"; 080 081 public static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); 082 083 private Services services; 084 085 private Map<String, List<Path>> shareLibMap = new HashMap<String, List<Path>>(); 086 087 private Map<String, List<Path>> launcherLibMap = new HashMap<String, List<Path>>(); 088 089 //symlink mapping. Oozie keeps on checking symlink path and if changes, Oozie reloads the sharelib 090 private Map<String, Map<Path, Path>> symlinkMapping = new HashMap<String, Map<Path, Path>>(); 091 092 private static XLog LOG = XLog.getLog(ShareLibService.class); 093 094 private String sharelibMappingFile; 095 096 private boolean isShipLauncherEnabled = false; 097 098 public static String SHARE_LIB_CONF_PREFIX = "oozie"; 099 100 private boolean shareLibLoadAttempted = false; 101 102 private String sharelibMetaFileOldTimeStamp; 103 104 private String sharelibDirOld; 105 106 FileSystem fs; 107 108 final long retentionTime = 1000 * 60 * 60 * 24 * ConfigurationService.getInt(LAUNCHERJAR_LIB_RETENTION); 109 110 @Override 111 public void init(Services services) throws ServiceException { 112 this.services = services; 113 sharelibMappingFile = ConfigurationService.get(services.getConf(), SHARELIB_MAPPING_FILE); 114 isShipLauncherEnabled = ConfigurationService.getBoolean(services.getConf(), SHIP_LAUNCHER_JAR); 115 boolean failOnfailure = ConfigurationService.getBoolean(services.getConf(), FAIL_FAST_ON_STARTUP); 116 Path launcherlibPath = getLauncherlibPath(); 117 HadoopAccessorService has = Services.get().get(HadoopAccessorService.class); 118 URI uri = launcherlibPath.toUri(); 119 try { 120 fs = FileSystem.get(has.createJobConf(uri.getAuthority())); 121 updateLauncherLib(); 122 updateShareLib(); 123 } 124 catch (Throwable e) { 125 if (failOnfailure) { 126 LOG.error("Sharelib initialization fails", e); 127 throw new ServiceException(ErrorCode.E0104, getClass().getName(), "Sharelib initialization fails. ", e); 128 } 129 else { 130 // We don't want to actually fail init by throwing an Exception, so only create the ServiceException and 131 // log it 132 ServiceException se = new ServiceException(ErrorCode.E0104, getClass().getName(), 133 "Not able to cache sharelib. An Admin needs to install the sharelib with oozie-setup.sh and issue the " 134 + "'oozie admin' CLI command to update the sharelib", e); 135 LOG.error(se); 136 } 137 } 138 Runnable purgeLibsRunnable = new Runnable() { 139 @Override 140 public void run() { 141 System.out.flush(); 142 try { 143 // Only one server should purge sharelib 144 if (Services.get().get(JobsConcurrencyService.class).isLeader()) { 145 final Date current = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime(); 146 purgeLibs(fs, LAUNCHER_PREFIX, current); 147 purgeLibs(fs, SHARED_LIB_PREFIX, current); 148 } 149 } 150 catch (IOException e) { 151 LOG.error("There was an issue purging the sharelib", e); 152 } 153 } 154 }; 155 services.get(SchedulerService.class).schedule(purgeLibsRunnable, 10, 156 ConfigurationService.getInt(services.getConf(), PURGE_INTERVAL) * 60 * 60 * 24, 157 SchedulerService.Unit.SEC); 158 } 159 160 /** 161 * Recursively change permissions. 162 * 163 * @throws IOException Signals that an I/O exception has occurred. 164 * @throws ClassNotFoundException the class not found exception 165 */ 166 167 private void updateLauncherLib() throws IOException { 168 if (isShipLauncherEnabled) { 169 if (fs == null) { 170 Path launcherlibPath = getLauncherlibPath(); 171 HadoopAccessorService has = Services.get().get(HadoopAccessorService.class); 172 URI uri = launcherlibPath.toUri(); 173 fs = FileSystem.get(has.createJobConf(uri.getAuthority())); 174 } 175 Path launcherlibPath = getLauncherlibPath(); 176 setupLauncherLibPath(fs, launcherlibPath); 177 recursiveChangePermissions(fs, launcherlibPath, FsPermission.valueOf(PERMISSION_STRING)); 178 } 179 180 } 181 182 /** 183 * Copy launcher jars to Temp directory 184 * 185 * @param fs the FileSystem 186 * @param tmpShareLibPath destination path 187 * @throws IOException Signals that an I/O exception has occurred. 188 * @throws ClassNotFoundException the class not found exception 189 */ 190 private void setupLauncherLibPath(FileSystem fs, Path tmpLauncherLibPath) throws IOException { 191 192 ActionService actionService = Services.get().get(ActionService.class); 193 List<Class> classes = JavaActionExecutor.getCommonLauncherClasses(); 194 Path baseDir = new Path(tmpLauncherLibPath, JavaActionExecutor.OOZIE_COMMON_LIBDIR); 195 copyJarContainingClasses(classes, fs, baseDir, JavaActionExecutor.OOZIE_COMMON_LIBDIR); 196 Set<String> actionTypes = actionService.getActionTypes(); 197 for (String key : actionTypes) { 198 ActionExecutor executor = actionService.getExecutor(key); 199 if (executor instanceof JavaActionExecutor) { 200 JavaActionExecutor jexecutor = (JavaActionExecutor) executor; 201 classes = jexecutor.getLauncherClasses(); 202 if (classes != null) { 203 String type = executor.getType(); 204 Path executorDir = new Path(tmpLauncherLibPath, type); 205 copyJarContainingClasses(classes, fs, executorDir, type); 206 } 207 } 208 } 209 } 210 211 /** 212 * Recursive change permissions. 213 * 214 * @param fs the FileSystem 215 * @param path the Path 216 * @param perm is permission 217 * @throws IOException Signals that an I/O exception has occurred. 218 */ 219 private void recursiveChangePermissions(FileSystem fs, Path path, FsPermission fsPerm) throws IOException { 220 fs.setPermission(path, fsPerm); 221 FileStatus[] filesStatus = fs.listStatus(path); 222 for (int i = 0; i < filesStatus.length; i++) { 223 Path p = filesStatus[i].getPath(); 224 if (filesStatus[i].isDir()) { 225 recursiveChangePermissions(fs, p, fsPerm); 226 } 227 else { 228 fs.setPermission(p, fsPerm); 229 } 230 } 231 } 232 233 /** 234 * Copy jar containing classes. 235 * 236 * @param classes the classes 237 * @param fs the FileSystem 238 * @param executorDir is Path 239 * @param type is sharelib key 240 * @throws IOException Signals that an I/O exception has occurred. 241 */ 242 private void copyJarContainingClasses(List<Class> classes, FileSystem fs, Path executorDir, String type) 243 throws IOException { 244 fs.mkdirs(executorDir); 245 Set<String> localJarSet = new HashSet<String>(); 246 for (Class c : classes) { 247 String localJar = findContainingJar(c); 248 if (localJar != null) { 249 localJarSet.add(localJar); 250 } 251 else { 252 throw new IOException("No jar containing " + c + " found"); 253 } 254 } 255 List<Path> listOfPaths = new ArrayList<Path>(); 256 for (String localJarStr : localJarSet) { 257 File localJar = new File(localJarStr); 258 fs.copyFromLocalFile(new Path(localJar.getPath()), executorDir); 259 Path path = new Path(executorDir, localJar.getName()); 260 listOfPaths.add(path); 261 LOG.info(localJar.getName() + " uploaded to " + executorDir.toString()); 262 } 263 launcherLibMap.put(type, listOfPaths); 264 265 } 266 267 /** 268 * Gets the path recursively. 269 * 270 * @param fs the FileSystem 271 * @param rootDir the root directory 272 * @param listOfPaths the list of paths 273 * @return the path recursively 274 * @throws IOException Signals that an I/O exception has occurred. 275 */ 276 private void getPathRecursively(FileSystem fs, Path rootDir, List<Path> listOfPaths) throws IOException { 277 if (rootDir == null) { 278 return; 279 } 280 281 try { 282 if(fs.isFile(new Path(new URI(rootDir.toString()).getPath()))){ 283 listOfPaths.add(rootDir); 284 return; 285 } 286 } 287 catch (URISyntaxException e) { 288 throw new IOException(e); 289 } 290 FileStatus[] status = fs.listStatus(rootDir); 291 if (status == null) { 292 LOG.info("Shared lib " + rootDir + " doesn't exist, not adding to cache"); 293 return; 294 } 295 296 for (FileStatus file : status) { 297 if (file.isDir()) { 298 getPathRecursively(fs, file.getPath(), listOfPaths); 299 } 300 else { 301 listOfPaths.add(file.getPath()); 302 } 303 } 304 } 305 306 public Map<String, List<Path>> getShareLib() { 307 return shareLibMap; 308 } 309 310 private Map<String, Map<Path, Path>> getSymlinkMapping() { 311 return symlinkMapping; 312 } 313 314 /** 315 * Gets the action sharelib lib jars. 316 * 317 * @param shareLibKey the sharelib key 318 * @return List of paths 319 * @throws IOException 320 */ 321 public List<Path> getShareLibJars(String shareLibKey) throws IOException { 322 // Sharelib map is empty means that on previous or startup attempt of 323 // caching sharelib has failed.Trying to reload 324 if (shareLibMap.isEmpty() && !shareLibLoadAttempted) { 325 synchronized (ShareLibService.class) { 326 if (shareLibMap.isEmpty()) { 327 updateShareLib(); 328 shareLibLoadAttempted = true; 329 } 330 } 331 } 332 checkSymlink(shareLibKey); 333 return shareLibMap.get(shareLibKey); 334 } 335 336 private void checkSymlink(String shareLibKey) throws IOException { 337 if (!HadoopShims.isSymlinkSupported() || symlinkMapping.get(shareLibKey) == null 338 || symlinkMapping.get(shareLibKey).isEmpty()) { 339 return; 340 } 341 342 HadoopShims fileSystem = new HadoopShims(fs); 343 for (Path path : symlinkMapping.get(shareLibKey).keySet()) { 344 if (!symlinkMapping.get(shareLibKey).get(path).equals(fileSystem.getSymLinkTarget(path))) { 345 synchronized (ShareLibService.class) { 346 Map<String, List<Path>> tempShareLibMap = new HashMap<String, List<Path>>(shareLibMap); 347 Map<String, Map<Path, Path>> tmpSymlinkMapping = new HashMap<String, Map<Path, Path>>(symlinkMapping); 348 349 LOG.info(MessageFormat.format("Symlink target for [{0}] has changed, was [{1}], now [{2}]", shareLibKey, 350 path, fileSystem.getSymLinkTarget(path))); 351 loadShareLibMetaFile(tempShareLibMap, tmpSymlinkMapping, sharelibMappingFile, shareLibKey); 352 shareLibMap = tempShareLibMap; 353 symlinkMapping = tmpSymlinkMapping; 354 return; 355 } 356 357 } 358 } 359 360 } 361 362 /** 363 * Gets the launcher jars. 364 * 365 * @param shareLibKey the shareLib key 366 * @return launcher jars paths 367 * @throws ClassNotFoundException 368 * @throws IOException 369 */ 370 public List<Path> getSystemLibJars(String shareLibKey) throws IOException { 371 List<Path> returnList = new ArrayList<Path>(); 372 // Sharelib map is empty means that on previous or startup attempt of 373 // caching launcher jars has failed.Trying to reload 374 if (isShipLauncherEnabled) { 375 if (launcherLibMap.isEmpty()) { 376 synchronized (ShareLibService.class) { 377 if (launcherLibMap.isEmpty()) { 378 updateLauncherLib(); 379 } 380 } 381 } 382 if (launcherLibMap.get(shareLibKey) != null) { 383 returnList.addAll(launcherLibMap.get(shareLibKey)); 384 } 385 } 386 if (shareLibKey.equals(JavaActionExecutor.OOZIE_COMMON_LIBDIR)) { 387 List<Path> sharelibList = getShareLibJars(shareLibKey); 388 if (sharelibList != null) { 389 returnList.addAll(sharelibList); 390 } 391 } 392 return returnList; 393 } 394 395 /** 396 * Find containing jar containing. 397 * 398 * @param clazz the clazz 399 * @return the string 400 */ 401 @VisibleForTesting 402 protected String findContainingJar(Class clazz) { 403 ClassLoader loader = clazz.getClassLoader(); 404 String classFile = clazz.getName().replaceAll("\\.", "/") + ".class"; 405 try { 406 for (Enumeration itr = loader.getResources(classFile); itr.hasMoreElements();) { 407 URL url = (URL) itr.nextElement(); 408 if ("jar".equals(url.getProtocol())) { 409 String toReturn = url.getPath(); 410 if (toReturn.startsWith("file:")) { 411 toReturn = toReturn.substring("file:".length()); 412 // URLDecoder is a misnamed class, since it actually 413 // decodes 414 // x-www-form-urlencoded MIME type rather than actual 415 // URL encoding (which the file path has). Therefore it 416 // would 417 // decode +s to ' 's which is incorrect (spaces are 418 // actually 419 // either unencoded or encoded as "%20"). Replace +s 420 // first, so 421 // that they are kept sacred during the decoding 422 // process. 423 toReturn = toReturn.replaceAll("\\+", "%2B"); 424 toReturn = URLDecoder.decode(toReturn, "UTF-8"); 425 toReturn = toReturn.replaceAll("!.*$", ""); 426 return toReturn; 427 } 428 } 429 } 430 } 431 catch (IOException ioe) { 432 throw new RuntimeException(ioe); 433 } 434 return null; 435 } 436 437 /** 438 * Purge libs. 439 * 440 * @param fs the fs 441 * @param prefix the prefix 442 * @throws IOException Signals that an I/O exception has occurred. 443 */ 444 private void purgeLibs(FileSystem fs, final String prefix, final Date current) throws IOException { 445 Path executorLibBasePath = services.get(WorkflowAppService.class).getSystemLibPath(); 446 PathFilter directoryFilter = new PathFilter() { 447 @Override 448 public boolean accept(Path path) { 449 if (path.getName().startsWith(prefix)) { 450 String name = path.getName(); 451 String time = name.substring(prefix.length()); 452 Date d = null; 453 try { 454 d = dateFormat.parse(time); 455 } 456 catch (ParseException e) { 457 return false; 458 } 459 return (current.getTime() - d.getTime()) > retentionTime; 460 } 461 else { 462 return false; 463 } 464 } 465 }; 466 FileStatus[] dirList = fs.listStatus(executorLibBasePath, directoryFilter); 467 Arrays.sort(dirList, new Comparator<FileStatus>() { 468 // sort in desc order 469 @Override 470 public int compare(FileStatus o1, FileStatus o2) { 471 return o2.getPath().getName().compareTo(o1.getPath().getName()); 472 } 473 }); 474 475 // Logic is to keep all share-lib between current timestamp and 7days old + 1 latest sharelib older than 7 days. 476 // refer OOZIE-1761 477 for (int i = 1; i < dirList.length; i++) { 478 Path dirPath = dirList[i].getPath(); 479 fs.delete(dirPath, true); 480 LOG.info("Deleted old launcher jar lib directory {0}", dirPath.getName()); 481 } 482 } 483 484 @Override 485 public void destroy() { 486 shareLibMap.clear(); 487 launcherLibMap.clear(); 488 489 } 490 491 @Override 492 public Class<? extends Service> getInterface() { 493 return ShareLibService.class; 494 } 495 496 /** 497 * Update share lib cache. 498 * 499 * @return the map 500 * @throws IOException Signals that an I/O exception has occurred. 501 */ 502 public Map<String, String> updateShareLib() throws IOException { 503 Map<String, String> status = new HashMap<String, String>(); 504 505 if (fs == null) { 506 Path launcherlibPath = getLauncherlibPath(); 507 HadoopAccessorService has = Services.get().get(HadoopAccessorService.class); 508 URI uri = launcherlibPath.toUri(); 509 fs = FileSystem.get(has.createJobConf(uri.getAuthority())); 510 } 511 512 Map<String, List<Path>> tempShareLibMap = new HashMap<String, List<Path>>(); 513 Map<String, Map<Path, Path>> tmpSymlinkMapping = new HashMap<String, Map<Path, Path>>(); 514 515 if (!StringUtils.isEmpty(sharelibMappingFile.trim())) { 516 String sharelibMetaFileNewTimeStamp = JsonUtils.formatDateRfc822( 517 new Date(fs.getFileStatus(new Path(sharelibMappingFile)).getModificationTime()), "GMT"); 518 loadShareLibMetaFile(tempShareLibMap, tmpSymlinkMapping, sharelibMappingFile, null); 519 status.put("sharelibMetaFile", sharelibMappingFile); 520 status.put("sharelibMetaFileNewTimeStamp", sharelibMetaFileNewTimeStamp); 521 status.put("sharelibMetaFileOldTimeStamp", sharelibMetaFileOldTimeStamp); 522 sharelibMetaFileOldTimeStamp = sharelibMetaFileNewTimeStamp; 523 } 524 else { 525 Path shareLibpath = getLatestLibPath(services.get(WorkflowAppService.class).getSystemLibPath(), 526 SHARED_LIB_PREFIX); 527 loadShareLibfromDFS(tempShareLibMap, shareLibpath); 528 529 if (shareLibpath != null) { 530 status.put("sharelibDirNew", shareLibpath.toString()); 531 status.put("sharelibDirOld", sharelibDirOld); 532 sharelibDirOld = shareLibpath.toString(); 533 } 534 535 } 536 shareLibMap = tempShareLibMap; 537 symlinkMapping = tmpSymlinkMapping; 538 return status; 539 } 540 541 /** 542 * Update share lib cache. Parse the share lib directory and each sub directory is a action key 543 * 544 * @param shareLibMap the share lib jar map 545 * @param shareLibpath the share libpath 546 * @throws IOException Signals that an I/O exception has occurred. 547 */ 548 private void loadShareLibfromDFS(Map<String, List<Path>> shareLibMap, Path shareLibpath) throws IOException { 549 550 if (shareLibpath == null) { 551 LOG.info("No share lib directory found"); 552 return; 553 554 } 555 556 FileStatus[] dirList = fs.listStatus(shareLibpath); 557 558 if (dirList == null) { 559 return; 560 } 561 562 for (FileStatus dir : dirList) { 563 if (!dir.isDir()) { 564 continue; 565 } 566 List<Path> listOfPaths = new ArrayList<Path>(); 567 getPathRecursively(fs, dir.getPath(), listOfPaths); 568 shareLibMap.put(dir.getPath().getName(), listOfPaths); 569 LOG.info("Share lib for " + dir.getPath().getName() + ":" + listOfPaths); 570 571 } 572 573 } 574 575 /** 576 * Load share lib text file. Sharelib mapping files contains list of key=value. where key is the action key and 577 * value is the DFS location of sharelib files. 578 * 579 * @param shareLibMap the share lib jar map 580 * @param sharelipFileMapping the sharelip file mapping 581 * @param symlinkMapping the symlink mapping 582 * @parm shareLibKey the sharelib key 583 * @throws IOException Signals that an I/O exception has occurred. 584 */ 585 private void loadShareLibMetaFile(Map<String, List<Path>> shareLibMap, 586 Map<String, Map<Path, Path>> symlinkMapping, String sharelibFileMapping, String shareLibKey) 587 throws IOException { 588 589 Path shareFileMappingPath = new Path(sharelibFileMapping); 590 HadoopAccessorService has = Services.get().get(HadoopAccessorService.class); 591 FileSystem filesystem = FileSystem.get(has.createJobConf(shareFileMappingPath.toUri().getAuthority())); 592 Properties prop = new Properties(); 593 prop.load(filesystem.open(new Path(sharelibFileMapping))); 594 595 for (Object keyObject : prop.keySet()) { 596 String key = (String) keyObject; 597 String mapKey = key.substring(SHARE_LIB_CONF_PREFIX.length() + 1); 598 if (key.toLowerCase().startsWith(SHARE_LIB_CONF_PREFIX) 599 && (shareLibKey == null || shareLibKey.equals(mapKey))) { 600 loadSharelib(shareLibMap, symlinkMapping, mapKey, ((String) prop.get(key)).split(",")); 601 } 602 } 603 } 604 605 private void loadSharelib(Map<String, List<Path>> tmpShareLibMap, Map<String, Map<Path, Path>> tmpSymlinkMapping, 606 String shareLibKey, String pathList[]) throws IOException { 607 List<Path> listOfPaths = new ArrayList<Path>(); 608 Map<Path, Path> symlinkMappingforAction = new HashMap<Path, Path>(); 609 HadoopShims fileSystem = new HadoopShims(fs); 610 611 for (String dfsPath : pathList) { 612 Path path = new Path(dfsPath); 613 614 getPathRecursively(fs, path, listOfPaths); 615 if (HadoopShims.isSymlinkSupported() && fileSystem.isSymlink(path)) { 616 symlinkMappingforAction.put(path, fileSystem.getSymLinkTarget(path)); 617 } 618 } 619 if (HadoopShims.isSymlinkSupported()) { 620 LOG.info("symlink for " + shareLibKey + ":" + symlinkMappingforAction); 621 tmpSymlinkMapping.put(shareLibKey, symlinkMappingforAction); 622 } 623 tmpShareLibMap.put(shareLibKey, listOfPaths); 624 LOG.info("Share lib for " + shareLibKey + ":" + listOfPaths); 625 } 626 627 /** 628 * Gets the launcherlib path. 629 * 630 * @return the launcherlib path 631 */ 632 private Path getLauncherlibPath() { 633 String formattedDate = dateFormat.format(Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime()); 634 Path tmpLauncherLibPath = new Path(services.get(WorkflowAppService.class).getSystemLibPath(), LAUNCHER_PREFIX 635 + formattedDate); 636 return tmpLauncherLibPath; 637 } 638 639 /** 640 * Gets the Latest lib path. 641 * 642 * @param rootDir the root dir 643 * @param prefix the prefix 644 * @return latest lib path 645 * @throws IOException Signals that an I/O exception has occurred. 646 */ 647 public Path getLatestLibPath(Path rootDir, final String prefix) throws IOException { 648 Date max = new Date(0L); 649 Path path = null; 650 PathFilter directoryFilter = new PathFilter() { 651 @Override 652 public boolean accept(Path path) { 653 return path.getName().startsWith(prefix); 654 } 655 }; 656 657 FileStatus[] files = fs.listStatus(rootDir, directoryFilter); 658 for (FileStatus file : files) { 659 String name = file.getPath().getName().toString(); 660 String time = name.substring(prefix.length()); 661 Date d = null; 662 try { 663 d = dateFormat.parse(time); 664 } 665 catch (ParseException e) { 666 continue; 667 } 668 if (d.compareTo(max) > 0) { 669 path = file.getPath(); 670 max = d; 671 } 672 } 673 //If there are no timestamped directories, fall back to root directory 674 if (path == null) { 675 path = rootDir; 676 } 677 return path; 678 } 679 680 /** 681 * Instruments the log service. 682 * <p/> 683 * It sets instrumentation variables indicating the location of the sharelib and launcherlib 684 * 685 * @param instr instrumentation to use. 686 */ 687 @Override 688 public void instrument(Instrumentation instr) { 689 instr.addVariable("libs", "sharelib.source", new Instrumentation.Variable<String>() { 690 @Override 691 public String getValue() { 692 if (!StringUtils.isEmpty(sharelibMappingFile.trim())) { 693 return SHARELIB_MAPPING_FILE; 694 } 695 return WorkflowAppService.SYSTEM_LIB_PATH; 696 } 697 }); 698 instr.addVariable("libs", "sharelib.mapping.file", new Instrumentation.Variable<String>() { 699 @Override 700 public String getValue() { 701 if (!StringUtils.isEmpty(sharelibMappingFile.trim())) { 702 return sharelibMappingFile; 703 } 704 return "(none)"; 705 } 706 }); 707 instr.addVariable("libs", "sharelib.system.libpath", new Instrumentation.Variable<String>() { 708 @Override 709 public String getValue() { 710 String sharelibPath = "(unavailable)"; 711 try { 712 Path libPath = getLatestLibPath(services.get(WorkflowAppService.class).getSystemLibPath(), 713 SHARED_LIB_PREFIX); 714 if (libPath != null) { 715 sharelibPath = libPath.toUri().toString(); 716 } 717 } 718 catch (IOException ioe) { 719 // ignore exception because we're just doing instrumentation 720 } 721 return sharelibPath; 722 } 723 }); 724 instr.addVariable("libs", "sharelib.mapping.file.timestamp", new Instrumentation.Variable<String>() { 725 @Override 726 public String getValue() { 727 if (!StringUtils.isEmpty(sharelibMetaFileOldTimeStamp)) { 728 return sharelibMetaFileOldTimeStamp; 729 } 730 return "(none)"; 731 } 732 }); 733 instr.addVariable("libs", "sharelib.keys", new Instrumentation.Variable<String>() { 734 @Override 735 public String getValue() { 736 Map<String, List<Path>> shareLib = getShareLib(); 737 if (shareLib != null && !shareLib.isEmpty()) { 738 Set<String> keySet = shareLib.keySet(); 739 return keySet.toString(); 740 } 741 return "(unavailable)"; 742 } 743 }); 744 instr.addVariable("libs", "launcherlib.system.libpath", new Instrumentation.Variable<String>() { 745 @Override 746 public String getValue() { 747 return getLauncherlibPath().toUri().toString(); 748 } 749 }); 750 instr.addVariable("libs", "sharelib.symlink.mapping", new Instrumentation.Variable<String>() { 751 @Override 752 public String getValue() { 753 Map<String, Map<Path, Path>> shareLibSymlinkMapping = getSymlinkMapping(); 754 if (shareLibSymlinkMapping != null && !shareLibSymlinkMapping.isEmpty() 755 && shareLibSymlinkMapping.values() != null && !shareLibSymlinkMapping.values().isEmpty()) { 756 StringBuffer bf = new StringBuffer(); 757 for (Entry<String, Map<Path, Path>> key : shareLibSymlinkMapping.entrySet()) 758 if (key.getKey() != null && !key.getValue().isEmpty()) { 759 for (Path path : key.getValue().keySet()) { 760 bf.append(path).append("(").append(key).append(")").append("=>") 761 .append(shareLibSymlinkMapping.get(key).get(path)).append(","); 762 } 763 764 } 765 return bf.toString(); 766 } 767 return "(none)"; 768 } 769 }); 770 771 } 772 773 /** 774 * Returns file system for shared libraries. 775 * <p/> 776 * If WorkflowAppService#getSystemLibPath doesn't have authority then a default one assumed 777 * 778 * @return file system for shared libraries 779 */ 780 public FileSystem getFileSystem() { 781 return fs; 782 } 783}