001package runwar; 002 003import java.awt.AWTException; 004import java.awt.Image; 005import java.awt.MenuItem; 006import java.awt.PopupMenu; 007import java.awt.SystemTray; 008import java.awt.Toolkit; 009import java.awt.TrayIcon; 010import java.awt.event.ActionEvent; 011import java.awt.event.ActionListener; 012import java.awt.event.MouseEvent; 013import java.awt.event.MouseListener; 014import java.io.BufferedInputStream; 015import java.io.BufferedReader; 016import java.io.ByteArrayOutputStream; 017import java.io.File; 018import java.io.FileInputStream; 019import java.io.FileNotFoundException; 020import java.io.FileOutputStream; 021import java.io.FilenameFilter; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.InputStreamReader; 025import java.io.OutputStream; 026import java.io.PrintStream; 027import java.lang.management.ManagementFactory; 028import java.lang.management.RuntimeMXBean; 029import java.lang.reflect.Method; 030import java.net.MalformedURLException; 031import java.net.URISyntaxException; 032import java.net.URL; 033import java.net.URLDecoder; 034import java.util.ArrayList; 035import java.util.Arrays; 036import java.util.HashSet; 037import java.util.List; 038import java.util.Set; 039import java.util.Timer; 040import java.util.TimerTask; 041import java.util.jar.JarEntry; 042import java.util.jar.JarInputStream; 043import java.util.jar.JarOutputStream; 044import java.util.jar.Pack200; 045import java.util.zip.GZIPInputStream; 046import java.util.zip.ZipEntry; 047import java.util.zip.ZipFile; 048 049import javax.imageio.ImageIO; 050import javax.swing.JOptionPane; 051 052import net.minidev.json.JSONArray; 053import net.minidev.json.JSONObject; 054import net.minidev.json.JSONValue; 055import runwar.logging.Logger; 056import runwar.options.ServerOptions; 057 058public class LaunchUtil { 059 060 private static TrayIcon trayIcon; 061 private static Logger log = Logger.getLogger("RunwarLogger"); 062 private static boolean relaunching; 063 private static final int KB = 1024; 064 public static final Set<String> replicateProps = new HashSet<String>(Arrays.asList(new String[] { "cfml.cli.home", 065 "cfml.server.config.dir", "cfml.web.config.dir", "cfml.server.trayicon", "cfml.server.dockicon" })); 066 067 public static File getJreExecutable() throws FileNotFoundException { 068 String jreDirectory = System.getProperty("java.home"); 069 if (jreDirectory == null) { 070 throw new IllegalStateException("java.home"); 071 } 072 final String javaPath = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java" 073 + (File.separator.equals("\\") ? ".exe" : ""); 074 File exe = new File(javaPath); 075 if (!exe.isFile()) { 076 throw new FileNotFoundException(exe.toString()); 077 } 078 // if(debug)System.out.println("Java: "+javaPath); 079 return exe; 080 } 081 082 public static File getJarDir(Class<?> aclass) { 083 URL url; 084 String extURL; 085 try { 086 url = aclass.getProtectionDomain().getCodeSource().getLocation(); 087 } catch (SecurityException ex) { 088 url = aclass.getResource(aclass.getSimpleName() + ".class"); 089 } 090 extURL = url.toExternalForm(); 091 if (extURL.endsWith(".jar")) // from getCodeSource 092 extURL = extURL.substring(0, extURL.lastIndexOf("/")); 093 else { // from getResource 094 String suffix = "/"+(aclass.getName()).replace(".", "/")+".class"; 095 extURL = extURL.replace(suffix, ""); 096 if (extURL.startsWith("jar:") && extURL.endsWith(".jar!")) 097 extURL = extURL.substring(4, extURL.lastIndexOf("/")); 098 } 099 try { 100 url = new URL(extURL); 101 } catch (MalformedURLException mux) { 102 } 103 try { 104 return new File(url.toURI()); 105 } catch(URISyntaxException ex) { 106 return new File(url.getPath()); 107 } 108 } 109 110 public static void launch(List<String> cmdarray, int timeout) throws IOException, InterruptedException { 111 // byte[] buffer = new byte[1024]; 112 113 ProcessBuilder processBuilder = new ProcessBuilder(cmdarray); 114 processBuilder.redirectErrorStream(true); 115 Process process = processBuilder.start(); 116 Thread.sleep(500); 117 InputStream is = process.getInputStream(); 118 InputStreamReader isr = new InputStreamReader(is); 119 BufferedReader br = new BufferedReader(isr); 120 log.debug("launching: " + cmdarray.toString()); 121 log.debug("timeout of " + timeout / 1000 + " seconds"); 122 String line; 123 int exit = -1; 124 long start = System.currentTimeMillis(); 125 System.out.print("Starting in background - "); 126 while ((System.currentTimeMillis() - start) < timeout) { 127 if (br.ready() && (line = br.readLine()) != null) { 128 // Outputs your process execution 129 try { 130 exit = process.exitValue(); 131 if (exit == 0) { 132 // Process finished 133 while ((line = br.readLine()) != null) { 134 log.debug(line); 135 } 136 System.exit(0); 137 } else if (exit == 1) { 138 System.out.println(); 139 printExceptionLine(line); 140 while ((line = br.readLine()) != null) { 141 printExceptionLine(line); 142 } 143 System.exit(1); 144 } 145 } catch (IllegalThreadStateException t) { 146 // This exceptions means the process has not yet finished. 147 // decide to continue, exit(0), or exit(1) 148 processOutout(line, process); 149 } 150 } 151 Thread.sleep(100); 152 } 153 if ((System.currentTimeMillis() - start) > timeout) { 154 process.destroy(); 155 System.out.println(); 156 System.err.println("ERROR: Startup exceeded timeout of " + timeout / 1000 + " seconds - aborting!"); 157 System.exit(1); 158 } 159 System.out.println("Server is up - "); 160 System.exit(0); 161 } 162 163 private static boolean processOutout(String line, Process process) { 164 log.info("processoutput: " + line); 165 if (line.indexOf("Server is up - ") != -1) { 166 // start up was successful, quit out 167 System.out.println(line); 168 System.exit(0); 169 } else if (line.indexOf("Exception in thread \"main\" java.lang.RuntimeException") != -1) { 170 return true; 171 } 172 return false; 173 } 174 175 public static void printExceptionLine(String line) { 176 final String msg = "java.lang.RuntimeException: "; 177 log.debug(line); 178 String formatted = line.contains(msg) ? line.substring(line.indexOf(msg) + msg.length()) : line; 179 formatted = formatted.matches("^\\s+at runwar.Start.*") ? "" : formatted.trim(); 180 if (formatted.length() > 0) { 181 System.err.println(formatted); 182 } 183 } 184 185 public static void relaunchAsBackgroundProcess(int timeout, String[] args, String processName) { 186 try { 187 if (relaunching) 188 return; 189 relaunching = true; 190 String path = LaunchUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath(); 191 log.info("Starting background " + processName + " from: " + path + " "); 192 String decodedPath = URLDecoder.decode(path, "UTF-8"); 193 decodedPath = new File(decodedPath).getPath(); 194 List<String> cmdarray = new ArrayList<String>(); 195 cmdarray.add(getJreExecutable().toString()); 196 List<String> currentVMArgs = getCurrentVMArgs(); 197 for (String arg : currentVMArgs) { 198 cmdarray.add(arg); 199 } 200 cmdarray.add("-jar"); 201 for (String propertyName : replicateProps) { 202 String property = System.getProperty(propertyName); 203 if (property != null) { 204 cmdarray.add("-D" + propertyName + "=" + property); 205 } 206 } 207 cmdarray.add(decodedPath); 208 for (String arg : args) { 209 cmdarray.add(arg); 210 } 211 launch(cmdarray, timeout); 212 } catch (Exception e) { 213 throw new RuntimeException(e); 214 } 215 } 216 217 private static void appendToBuffer(List<String> resultBuffer, StringBuffer buf) { 218 if (buf.length() > 0) { 219 resultBuffer.add(buf.toString()); 220 buf.setLength(0); 221 } 222 } 223 224 public static List<String> getCurrentVMArgs() { 225 RuntimeMXBean RuntimemxBean = ManagementFactory.getRuntimeMXBean(); 226 List<String> arguments = RuntimemxBean.getInputArguments(); 227 return arguments; 228 } 229 230 public static String[] tokenizeArgs(String argLine) { 231 List<String> resultBuffer = new java.util.ArrayList<String>(); 232 233 if (argLine != null) { 234 int z = argLine.length(); 235 boolean insideQuotes = false; 236 StringBuffer buf = new StringBuffer(); 237 238 for (int i = 0; i < z; ++i) { 239 char c = argLine.charAt(i); 240 if (c == '"') { 241 appendToBuffer(resultBuffer, buf); 242 insideQuotes = !insideQuotes; 243 } else if (c == '\\') { 244 if ((z > i + 1) && ((argLine.charAt(i + 1) == '"') || (argLine.charAt(i + 1) == '\\'))) { 245 buf.append(argLine.charAt(i + 1)); 246 ++i; 247 } else { 248 buf.append("\\"); 249 } 250 } else { 251 if (insideQuotes) { 252 buf.append(c); 253 } else { 254 if (Character.isWhitespace(c)) { 255 appendToBuffer(resultBuffer, buf); 256 } else { 257 buf.append(c); 258 } 259 } 260 } 261 } 262 appendToBuffer(resultBuffer, buf); 263 264 } 265 266 String[] result = new String[resultBuffer.size()]; 267 return resultBuffer.toArray(result); 268 } 269 270 public static void hookTray(Server server) { 271 ServerOptions serverOptions = Server.getServerOptions(); 272 String iconImage = serverOptions.getIconImage(); 273 String host = serverOptions.getHost(); 274 int portNumber = serverOptions.getPortNumber(); 275 final int stopSocket = serverOptions.getSocketNumber(); 276 String processName = serverOptions.getProcessName(); 277 String PID = server.getPID(); 278 279 if (SystemTray.isSupported()) { 280 Image image = getIconImage(iconImage); 281 MouseListener mouseListener = new MouseListener() { 282 public void mouseClicked(MouseEvent e) { 283 } 284 285 public void mouseEntered(MouseEvent e) { 286 } 287 288 public void mouseExited(MouseEvent e) { 289 } 290 291 public void mousePressed(MouseEvent e) { 292 } 293 294 public void mouseReleased(MouseEvent e) { 295 } 296 }; 297 298 trayIcon = new TrayIcon(image, processName + " server on " + host + ":" + portNumber + " PID:" + PID); 299 300 PopupMenu popup = new PopupMenu(); 301 MenuItem item = null; 302 JSONArray menuItems; 303 304 final String defaultMenu = "[" 305 + "{label:\"Stop Server (${runwar.processName})\", action:\"stopserver\"}" 306 + ",{label:\"Open Browser\", action:\"openbrowser\", url:\"http://${runwar.host}:${runwar.port}/\"}" 307 + "]"; 308 309 if (serverOptions.getTrayConfig() != null) { 310 menuItems = (JSONArray) JSONValue.parse(readFile(serverOptions.getTrayConfig())); 311 } else { 312 menuItems = (JSONArray) JSONValue.parse(getResourceAsString("runwar/taskbar.json")); 313 } 314 if (menuItems == null) { 315 log.error("Could not load taskbar properties"); 316 menuItems = (JSONArray) JSONValue.parse(defaultMenu); 317 } 318 for (Object ob : menuItems) { 319 JSONObject itemInfo = (JSONObject) ob; 320 String label = replaceMenuTokens(itemInfo.get("label").toString(), processName, host, portNumber, 321 stopSocket); 322 String action = itemInfo.get("action").toString(); 323 item = new MenuItem(label); 324 if (action.toLowerCase().equals("stopserver")) { 325 item.addActionListener(new ExitActionListener()); 326 } else if (action.toLowerCase().equals("openbrowser")) { 327 String url = replaceMenuTokens(itemInfo.get("url").toString(), processName, host, portNumber, 328 stopSocket); 329 item.addActionListener(new OpenBrowserActionListener(url)); 330 } else { 331 log.error("Unknown menu item action \"" + action + "\" for \"" + label + "\""); 332 } 333 popup.add(item); 334 } 335 336 // MenuItem item = new MenuItem("Stop Server (" + processName + 337 // ")"); 338 // item.addActionListener(new 339 // ExitActionListener(trayIcon,host,stopSocket)); 340 // popup.add(item); 341 // item = new MenuItem("Open Browser"); 342 // item.addActionListener(new 343 // OpenBrowserActionListener(trayIcon,"http://"+host+":"+portNumber 344 // + "/")); 345 // popup.add(item); 346 // item = new MenuItem("Open Admin"); 347 // item.addActionListener(new 348 // OpenBrowserActionListener(trayIcon,railoAdminURL)); 349 // popup.add(item); 350 351 trayIcon.setPopupMenu(popup); 352 trayIcon.setImageAutoSize(true); 353 trayIcon.addMouseListener(mouseListener); 354 355 try { 356 SystemTray.getSystemTray().add(trayIcon); 357 } catch (AWTException e) { 358 System.err.println("TrayIcon could not be added."); 359 } 360 361 } else { 362 log.warn("System Tray is not supported"); 363 } 364 } 365 366 public static void unhookTray() { 367 if (SystemTray.isSupported() && trayIcon != null) { 368 try { 369 log.debug("Removing tray icon"); 370 SystemTray.getSystemTray().remove(trayIcon); 371 } catch (Exception e) { 372 e.printStackTrace(); 373 } 374 } 375 } 376 377 public static Image getIconImage(String iconImage) { 378 Image image = null; 379 if (iconImage != null && iconImage.length() != 0) { 380 iconImage = iconImage.replaceAll("(^\")|(\"$)", ""); 381 log.debug("trying to load icon: " + iconImage); 382 if (iconImage.contains("!")) { 383 String[] zip = iconImage.split("!"); 384 try { 385 ZipFile zipFile = new ZipFile(zip[0]); 386 ZipEntry zipEntry = zipFile.getEntry(zip[1].replaceFirst("^[\\/]", "")); 387 InputStream entryStream = zipFile.getInputStream(zipEntry); 388 image = ImageIO.read(entryStream); 389 zipFile.close(); 390 log.debug("loaded image from archive: " + zip[0] + zip[1]); 391 } catch (IOException e2) { 392 log.debug("Could not get zip resource: " + iconImage + "(" + e2.getMessage() + ")"); 393 } 394 } else if (new File(iconImage).exists()) { 395 try { 396 image = ImageIO.read(new File(iconImage)); 397 } catch (IOException e1) { 398 log.debug("Could not get file resource: " + iconImage + "(" + e1.getMessage() + ")"); 399 } 400 } else { 401 log.debug("trying parent loader for image: " + iconImage); 402 URL imageURL = LaunchUtil.class.getClassLoader().getParent().getResource(iconImage); 403 if (imageURL == null) { 404 log.debug("trying loader for image: " + iconImage); 405 imageURL = LaunchUtil.class.getClassLoader().getResource(iconImage); 406 } 407 if (imageURL != null) { 408 log.debug("Trying getImage for: " + imageURL); 409 image = Toolkit.getDefaultToolkit().getImage(imageURL); 410 } 411 } 412 } else { 413 image = Toolkit.getDefaultToolkit().getImage(Start.class.getResource("/runwar/icon.png")); 414 } 415 // if bad image, use default 416 if (image == null) { 417 log.debug("Bad image, using default."); 418 image = Toolkit.getDefaultToolkit().getImage(Start.class.getResource("/runwar/icon.png")); 419 } 420 return image; 421 } 422 423 private static String replaceMenuTokens(String label, String processName, String host, int portNumber, 424 int stopSocket) { 425 label = label.replaceAll("\\$\\{runwar.port\\}", Integer.toString(portNumber)) 426 .replaceAll("\\$\\{runwar.processName\\}", processName).replaceAll("\\$\\{runwar.host\\}", host) 427 .replaceAll("\\$\\{runwar.stopsocket\\}", Integer.toString(stopSocket)); 428 return label; 429 } 430 431 private static class OpenBrowserActionListener implements ActionListener { 432 private String url; 433 434 public OpenBrowserActionListener(String url) { 435 this.url = url; 436 } 437 438 @Override 439 public void actionPerformed(ActionEvent e) { 440 trayIcon.displayMessage("Browser", "Opening browser", TrayIcon.MessageType.INFO); 441 openURL(url); 442 } 443 } 444 445 private static class ExitActionListener implements ActionListener { 446 447 public ExitActionListener() { 448 } 449 450 @Override 451 public void actionPerformed(ActionEvent e) { 452 try { 453 System.out.println("Exiting..."); 454 System.exit(0); 455 } catch (Exception e1) { 456 trayIcon.displayMessage("Error", e1.getMessage(), TrayIcon.MessageType.INFO); 457 try { 458 Thread.sleep(5000); 459 } catch (InterruptedException e2) { 460 } 461 System.exit(1); 462 } 463 } 464 } 465 466 public static void openURL(String url) { 467 String osName = System.getProperty("os.name"); 468 if (url == null) { 469 System.out.println("ERROR: No URL specified to open the browser to!"); 470 return; 471 } 472 try { 473 System.out.println(url); 474 if (osName.startsWith("Mac OS")) { 475 Class<?> fileMgr = Class.forName("com.apple.eio.FileManager"); 476 Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[] { String.class }); 477 openURL.invoke(null, new Object[] { url }); 478 } else if (osName.startsWith("Windows")) 479 Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url); 480 else { // assume Unix or Linux 481 String[] browsers = { "firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape" }; 482 String browser = null; 483 for (int count = 0; count < browsers.length && browser == null; count++) 484 if (Runtime.getRuntime().exec(new String[] { "which", browsers[count] }).waitFor() == 0) 485 browser = browsers[count]; 486 if (browser == null) 487 throw new Exception("Could not find web browser"); 488 else 489 Runtime.getRuntime().exec(new String[] { browser, url }); 490 } 491 } catch (Exception e) { 492 e.printStackTrace(); 493 JOptionPane.showMessageDialog(null, e.getMessage() + ":\n" + e.getLocalizedMessage()); 494 } 495 } 496 497 public static String getResourceAsString(String path) { 498 return readStream(LaunchUtil.class.getClassLoader().getResourceAsStream(path)); 499 } 500 501 public static void unzipInteralZip(ClassLoader classLoader, String resourcePath, File libDir, boolean debug) { 502 if (debug) 503 System.out.println("Extracting " + resourcePath); 504 libDir.mkdir(); 505 URL resource = classLoader.getResource(resourcePath); 506 if (resource == null) { 507 System.err.println("Could not find the " + resourcePath + " on classpath!"); 508 System.exit(1); 509 } 510 unzipResource(resource, libDir, debug); 511 } 512 513 public static void unzipResource(URL resource, File libDir, boolean debug) { 514 class PrintDot extends TimerTask { 515 public void run() { 516 System.out.print("."); 517 } 518 } 519 Timer timer = new Timer(); 520 PrintDot task = new PrintDot(); 521 timer.schedule(task, 0, 2000); 522 523 try { 524 BufferedInputStream bis = new BufferedInputStream(resource.openStream()); 525 JarInputStream jis = new JarInputStream(bis); 526 JarEntry je = null; 527 while ((je = jis.getNextJarEntry()) != null) { 528 java.io.File f = new java.io.File(libDir.toString() + java.io.File.separator + je.getName()); 529 if (je.isDirectory()) { 530 f.mkdirs(); 531 continue; 532 } 533 File parentDir = new File(f.getParent()); 534 if (!parentDir.exists()) { 535 parentDir.mkdirs(); 536 } 537 FileOutputStream fileOutStream = new FileOutputStream(f); 538 writeStreamTo(jis, fileOutStream, 8 * KB); 539 if (f.getPath().endsWith("pack.gz")) { 540 unpack(f); 541 fileOutStream.close(); 542 f.delete(); 543 } 544 fileOutStream.close(); 545 } 546 547 } catch (Exception exc) { 548 task.cancel(); 549 exc.printStackTrace(); 550 } 551 task.cancel(); 552 553 } 554 555 public static void cleanUpUnpacked(File libDir) { 556 if (libDir.exists() && libDir.listFiles(new ExtFilter(".gz")).length > 0) { 557 for (File gz : libDir.listFiles(new ExtFilter(".gz"))) { 558 try { 559 gz.delete(); 560 } catch (Exception e) { 561 } 562 } 563 } 564 } 565 566 public static void removePreviousLibs(File libDir) { 567 if (libDir.exists() && libDir.listFiles(new ExtFilter(".jar")).length > 0) { 568 for (File previous : libDir.listFiles(new ExtFilter(".jar"))) { 569 try { 570 previous.delete(); 571 } catch (Exception e) { 572 System.err.println("Could not delete previous lib: " + previous.getAbsolutePath()); 573 } 574 } 575 } 576 } 577 578 public static void unpack(File inFile) { 579 JarOutputStream out = null; 580 InputStream in = null; 581 String inName = inFile.getPath(); 582 String outName; 583 584 if (inName.endsWith(".pack.gz")) { 585 outName = inName.substring(0, inName.length() - 8); 586 } else if (inName.endsWith(".pack")) { 587 outName = inName.substring(0, inName.length() - 5); 588 } else { 589 outName = inName + ".unpacked"; 590 } 591 try { 592 Pack200.Unpacker unpacker = Pack200.newUnpacker(); 593 out = new JarOutputStream(new FileOutputStream(outName)); 594 in = new FileInputStream(inName); 595 if (inName.endsWith(".gz")) 596 in = new GZIPInputStream(in); 597 unpacker.unpack(in, out); 598 } catch (IOException ex) { 599 ex.printStackTrace(); 600 } finally { 601 if (in != null) { 602 try { 603 in.close(); 604 } catch (IOException ex) { 605 System.err.println("Error closing file: " + ex.getMessage()); 606 } 607 } 608 if (out != null) { 609 try { 610 out.flush(); 611 out.close(); 612 } catch (IOException ex) { 613 System.err.println("Error closing file: " + ex.getMessage()); 614 } 615 } 616 } 617 } 618 619 public static void copyInternalFile(ClassLoader classLoader, String resourcePath, File dest) { 620 URL resource = classLoader.getResource(resourcePath); 621 try { 622 copyStream(resource.openStream(), dest); 623 } catch (IOException e) { 624 log.error(e); 625 } 626 627 } 628 629 public static String readStream(InputStream is) { 630 ByteArrayOutputStream out = new ByteArrayOutputStream(); 631 PrintStream outPrint = new PrintStream(out); 632 try { 633 int content; 634 while ((content = is.read()) != -1) { 635 // convert to char and display it 636 outPrint.print((char) content); 637 } 638 } catch (IOException e) { 639 e.printStackTrace(); 640 } finally { 641 try { 642 if (is != null) 643 is.close(); 644 if (outPrint != null) 645 outPrint.close(); 646 } catch (IOException ex) { 647 ex.printStackTrace(); 648 } 649 } 650 return out.toString(); 651 } 652 653 public static String readFile(File source) { 654 try { 655 return readStream(new FileInputStream(source)); 656 } catch (FileNotFoundException e) { 657 log.error(e); 658 } 659 return null; 660 } 661 662 public static void copyFile(File source, File dest) { 663 try { 664 copyStream(new FileInputStream(source), dest); 665 } catch (FileNotFoundException e) { 666 log.error(e); 667 } 668 } 669 670 public static void copyStream(InputStream bis, File dest) { 671 try { 672 FileOutputStream output = new FileOutputStream(dest); 673 writeStreamTo(bis, output, 8 * KB); 674 output.close(); 675 } catch (FileNotFoundException e) { 676 log.error(e); 677 } catch (IOException e) { 678 log.error(e); 679 } 680 681 } 682 683 public static int writeStreamTo(final InputStream input, final OutputStream output, int bufferSize) 684 throws IOException { 685 int available = Math.min(input.available(), 256 * KB); 686 byte[] buffer = new byte[Math.max(bufferSize, available)]; 687 int answer = 0; 688 int count = input.read(buffer); 689 while (count >= 0) { 690 output.write(buffer, 0, count); 691 answer += count; 692 count = input.read(buffer); 693 } 694 return answer; 695 } 696 697 public static void deleteRecursive(File f) throws IOException { 698 if (f.isDirectory()) { 699 for (File c : f.listFiles()) 700 deleteRecursive(c); 701 } 702 if (!f.delete()) 703 System.err.println("Could not delete file: " + f.getAbsolutePath()); 704 } 705 706 public static class ExtFilter implements FilenameFilter { 707 private String ext; 708 709 public ExtFilter(String extension) { 710 ext = extension; 711 } 712 713 public boolean accept(File dir, String name) { 714 return name.toLowerCase().endsWith(ext); 715 } 716 } 717 718 public static class PrefixFilter implements FilenameFilter { 719 private String prefix; 720 721 public PrefixFilter(String prefix) { 722 this.prefix = prefix; 723 } 724 725 public boolean accept(File dir, String name) { 726 return name.toLowerCase().startsWith(prefix); 727 } 728 } 729 730}