001/* 002 * Copyright 2011-2012 Stephen Connolly. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.jszip.maven; 018 019import org.apache.maven.ProjectDependenciesResolver; 020import org.apache.maven.artifact.Artifact; 021import org.apache.maven.artifact.ArtifactUtils; 022import org.apache.maven.artifact.DependencyResolutionRequiredException; 023import org.apache.maven.artifact.repository.ArtifactRepository; 024import org.apache.maven.artifact.resolver.ArtifactNotFoundException; 025import org.apache.maven.artifact.resolver.ArtifactResolutionException; 026import org.apache.maven.artifact.versioning.OverConstrainedVersionException; 027import org.apache.maven.execution.MavenSession; 028import org.apache.maven.model.Plugin; 029import org.apache.maven.model.PluginExecution; 030import org.apache.maven.model.building.ModelBuildingRequest; 031import org.apache.maven.plugin.MavenPluginManager; 032import org.apache.maven.plugin.Mojo; 033import org.apache.maven.plugin.MojoExecution; 034import org.apache.maven.plugin.MojoExecutionException; 035import org.apache.maven.plugin.MojoFailureException; 036import org.apache.maven.plugin.PluginConfigurationException; 037import org.apache.maven.plugin.PluginContainerException; 038import org.apache.maven.plugin.descriptor.MojoDescriptor; 039import org.apache.maven.plugin.descriptor.PluginDescriptor; 040import org.apache.maven.plugins.annotations.Component; 041import org.apache.maven.plugins.annotations.LifecyclePhase; 042import org.apache.maven.plugins.annotations.Parameter; 043import org.apache.maven.plugins.annotations.ResolutionScope; 044import org.apache.maven.project.DefaultProjectBuildingRequest; 045import org.apache.maven.project.DuplicateProjectException; 046import org.apache.maven.project.MavenProject; 047import org.apache.maven.project.MavenProjectHelper; 048import org.apache.maven.project.ProjectBuilder; 049import org.apache.maven.project.ProjectBuildingException; 050import org.apache.maven.project.ProjectBuildingRequest; 051import org.apache.maven.project.ProjectSorter; 052import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; 053import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts; 054import org.apache.maven.shared.artifact.filter.collection.ProjectTransitivityFilter; 055import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; 056import org.apache.maven.shared.artifact.filter.collection.TypeFilter; 057import org.apache.maven.shared.filtering.MavenFilteringException; 058import org.apache.maven.shared.filtering.MavenResourcesExecution; 059import org.apache.maven.shared.filtering.MavenResourcesFiltering; 060import org.apache.maven.shared.invoker.DefaultInvocationRequest; 061import org.apache.maven.shared.invoker.DefaultInvoker; 062import org.apache.maven.shared.invoker.InvocationRequest; 063import org.apache.maven.shared.invoker.Invoker; 064import org.apache.maven.shared.invoker.InvokerLogger; 065import org.apache.maven.shared.invoker.MavenInvocationException; 066import org.codehaus.plexus.util.FileUtils; 067import org.codehaus.plexus.util.IOUtil; 068import org.codehaus.plexus.util.StringUtils; 069import org.codehaus.plexus.util.dag.CycleDetectedException; 070import org.eclipse.jetty.server.Connector; 071import org.eclipse.jetty.server.Handler; 072import org.eclipse.jetty.server.Server; 073import org.eclipse.jetty.server.handler.ContextHandlerCollection; 074import org.eclipse.jetty.server.handler.DefaultHandler; 075import org.eclipse.jetty.server.handler.HandlerCollection; 076import org.eclipse.jetty.server.nio.SelectChannelConnector; 077import org.eclipse.jetty.util.resource.Resource; 078import org.eclipse.jetty.util.resource.ResourceCollection; 079import org.eclipse.jetty.webapp.WebAppClassLoader; 080import org.eclipse.jetty.webapp.WebAppContext; 081import org.jszip.css.CssEngine; 082import org.jszip.jetty.CssEngineResource; 083import org.jszip.jetty.JettyWebAppContext; 084import org.jszip.jetty.SystemProperties; 085import org.jszip.jetty.SystemProperty; 086import org.jszip.jetty.VirtualDirectoryResource; 087import org.jszip.less.LessEngine; 088import org.jszip.pseudo.io.PseudoDirectoryScanner; 089import org.jszip.pseudo.io.PseudoFile; 090import org.jszip.pseudo.io.PseudoFileOutputStream; 091import org.jszip.pseudo.io.PseudoFileSystem; 092import org.jszip.sass.SassEngine; 093 094import java.io.File; 095import java.io.IOException; 096import java.net.MalformedURLException; 097import java.util.ArrayList; 098import java.util.Arrays; 099import java.util.Collections; 100import java.util.HashSet; 101import java.util.Iterator; 102import java.util.List; 103import java.util.Set; 104import java.util.Stack; 105import java.util.concurrent.TimeUnit; 106 107/** 108 * Starts a Jetty servlet container with resources resolved from the reactor projects to enable live editing of those 109 * resources and pom and classpath scanning to restart the servlet container when the classpath is modified. Note that 110 * if the poms are modified in such a way that the reactor build plan is modified, we have no choice but to stop the 111 * servlet container and require the maven session to be restarted, but best effort is made to ensure that restart 112 * is only when required. 113 */ 114@org.apache.maven.plugins.annotations.Mojo(name = "run", 115 defaultPhase = LifecyclePhase.TEST_COMPILE, 116 requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) 117public class RunMojo extends AbstractJSZipMojo { 118 /** 119 * The artifact path mappings for unpacking. 120 */ 121 @Parameter(property = "mappings") 122 private Mapping[] mappings; 123 124 /** 125 * If true, the <testOutputDirectory> 126 * and the dependencies of <scope>test<scope> 127 * will be put first on the runtime classpath. 128 */ 129 @Parameter(alias = "useTextClasspath", defaultValue = "false") 130 private boolean useTestScope; 131 132 133 /** 134 * The default location of the web.xml file. Will be used 135 * if <webApp><descriptor> is not set. 136 */ 137 @Parameter(property = "maven.war.webxml", readonly = true) 138 private String webXml; 139 140 141 /** 142 * The directory containing generated classes. 143 */ 144 @Parameter(property = "project.build.outputDirectory", required = true) 145 private File classesDirectory; 146 147 148 /** 149 * The directory containing generated test classes. 150 */ 151 @Parameter(property = "project.build.testOutputDirectory", required = true) 152 private File testClassesDirectory; 153 154 /** 155 * Root directory for all html/jsp etc files 156 */ 157 @Parameter(defaultValue = "${basedir}/src/main/webapp", required = true) 158 private File warSourceDirectory = null; 159 160 161 /** 162 * List of connectors to use. If none are configured 163 * then the default is a single SelectChannelConnector at port 8080. You can 164 * override this default port number by using the system property jetty.port 165 * on the command line, eg: mvn -Djetty.port=9999 jszip:run. Consider using instead 166 * the <jettyXml> element to specify external jetty xml config file. 167 */ 168 @Parameter 169 protected Connector[] connectors; 170 171 /** 172 * The module that the goal should apply to. Specify either groupId:artifactId or just plain artifactId. 173 */ 174 @Parameter(property = "jszip.run.module") 175 private String runModule; 176 177 /** 178 * List of the packaging types will be considered for executing this goal. Normally you do not 179 * need to configure this parameter unless you have a custom war packaging type. Defaults to <code>war</code> 180 */ 181 @Parameter 182 private String[] runPackages; 183 184 /** 185 * System properties to set before execution. 186 * Note that these properties will NOT override System properties 187 * that have been set on the command line or by the JVM. They WILL 188 * override System properties that have been set via systemPropertiesFile. 189 * Optional. 190 */ 191 @Parameter 192 private SystemProperties systemProperties; 193 194 /** 195 * The project builder 196 */ 197 @Component 198 private ProjectBuilder projectBuilder; 199 200 /** 201 * The reactor project 202 */ 203 @Parameter(property = "reactorProjects", required = true, readonly = true) 204 protected List<MavenProject> reactorProjects; 205 206 /** 207 * Location of the local repository. 208 */ 209 @Parameter(property = "localRepository", required = true, readonly = true) 210 private ArtifactRepository localRepository; 211 212 /** 213 * The current build session instance. This is used for plugin manager API calls. 214 */ 215 @Parameter(property = "session", required = true, readonly = true) 216 private MavenSession session; 217 218 /** 219 * The forked project. 220 */ 221 @Parameter(property = "executedProject", required = true, readonly = true) 222 private MavenProject executedProject; 223 224 /** 225 * Directory containing the less processor. 226 */ 227 @Parameter(defaultValue = "src/build/js/less-rhino.js") 228 private File customLessScript; 229 230 /** 231 * Skip compilation. 232 */ 233 @Parameter(property = "jszip.less.skip", defaultValue = "false") 234 private boolean lessSkip; 235 236 /** 237 * Force compilation even if the source LESS file is older than the destination CSS file. 238 */ 239 @Parameter(property = "jszip.less.forceIfOlder", defaultValue = "false") 240 private boolean lessForceIfOlder; 241 242 /** 243 * Compress CSS. 244 */ 245 @Parameter(property = "jszip.less.compress", defaultValue = "true") 246 private boolean lessCompress; 247 248 /** 249 * Indicates whether the build will continue even if there are compilation errors. 250 */ 251 @Parameter(property = "jszip.less.failOnError", defaultValue = "true") 252 private boolean lessFailOnError; 253 254 /** 255 * Indicates whether to show extracts of the code where errors occur. 256 */ 257 @Parameter(property = "jszip.less.showErrorExtracts", defaultValue = "false") 258 private boolean showErrorExtracts; 259 260 /** 261 * A list of <include> elements specifying the less files (by pattern) that should be included in 262 * processing. 263 */ 264 @Parameter 265 private List<String> lessIncludes; 266 267 /** 268 * A list of <exclude> elements specifying the less files (by pattern) that should be excluded from 269 * processing. 270 */ 271 @Parameter 272 private List<String> lessExcludes; 273 274 /** 275 * Skip compilation. 276 */ 277 @Parameter(property = "jszip.sass.skip", defaultValue = "false") 278 private boolean sassSkip; 279 280 /** 281 * Force compilation even if the source Sass file is older than the destination CSS file. 282 */ 283 @Parameter(property = "jszip.sass.forceIfOlder", defaultValue = "false") 284 private boolean sassForceIfOlder; 285 286 /** 287 * Indicates whether the build will continue even if there are compilation errors. 288 */ 289 @Parameter(property = "jszip.sass.failOnError", defaultValue = "true") 290 private boolean sassFailOnError; 291 292 /** 293 * A list of <include> elements specifying the sass files (by pattern) that should be included in 294 * processing. 295 */ 296 @Parameter 297 private List<String> sassIncludes; 298 299 /** 300 * A list of <exclude> elements specifying the sass files (by pattern) that should be excluded from 301 * processing. 302 */ 303 @Parameter 304 private List<String> sassExcludes; 305 306 /** 307 * The character encoding scheme to be applied when reading SASS files. 308 */ 309 @Parameter( defaultValue = "${project.build.sourceEncoding}" ) 310 private String encoding; 311 312 /** 313 * Used to resolve transitive dependencies. 314 */ 315 @Component 316 private ProjectDependenciesResolver projectDependenciesResolver; 317 318 /** 319 * Maven ProjectHelper. 320 */ 321 @Component 322 private MavenProjectHelper projectHelper; 323 324 /** 325 * The Maven plugin Manager 326 */ 327 @Component 328 private MavenPluginManager mavenPluginManager; 329 330 /** 331 * This plugin's descriptor 332 */ 333 @Parameter(property = "plugin") 334 private PluginDescriptor pluginDescriptor; 335 336 /** 337 * Our resource filterer 338 */ 339 @Component(role = org.apache.maven.shared.filtering.MavenResourcesFiltering.class, hint = "default") 340 protected MavenResourcesFiltering mavenResourcesFiltering; 341 342 private final String scope = "test"; 343 private final long classpathCheckInterval = TimeUnit.SECONDS.toMillis(10); 344 345 public void execute() 346 throws MojoExecutionException, MojoFailureException { 347 if (runPackages == null || runPackages.length == 0) { 348 runPackages = new String[]{"war"}; 349 } 350 351 injectMissingArtifacts(project, executedProject); 352 353 if (!Arrays.asList(runPackages).contains(project.getPackaging())) { 354 getLog().info("Skipping JSZip run: module " + ArtifactUtils.versionlessKey(project.getGroupId(), 355 project.getArtifactId()) + " as not specified in runPackages"); 356 return; 357 } 358 if (StringUtils.isNotBlank(runModule) 359 && !project.getArtifactId().equals(runModule) 360 && !ArtifactUtils.versionlessKey(project.getGroupId(), project.getArtifactId()).equals(runModule)) { 361 getLog().info("Skipping JSZip run: module " + ArtifactUtils.versionlessKey(project.getGroupId(), 362 project.getArtifactId()) + " as requested runModule is " + runModule); 363 return; 364 } 365 getLog().info("Starting JSZip run: module " + ArtifactUtils.versionlessKey(project.getGroupId(), 366 project.getArtifactId())); 367 MavenProject project = this.project; 368 long lastResourceChange = System.currentTimeMillis(); 369 long lastClassChange = System.currentTimeMillis(); 370 long lastPomChange = getPomsLastModified(); 371 372 Server server = new Server(); 373 if (connectors == null || connectors.length == 0) { 374 SelectChannelConnector selectChannelConnector = new SelectChannelConnector(); 375 selectChannelConnector.setPort(8080); 376 connectors = new Connector[]{ 377 selectChannelConnector 378 }; 379 } 380 server.setConnectors(connectors); 381 ContextHandlerCollection contexts = new ContextHandlerCollection(); 382 HandlerCollection handlerCollection = new HandlerCollection(true); 383 DefaultHandler defaultHandler = new DefaultHandler(); 384 handlerCollection.setHandlers(new Handler[]{contexts, defaultHandler}); 385 server.setHandler(handlerCollection); 386 try { 387 server.start(); 388 } catch (Exception e) { 389 throw new MojoExecutionException(e.getMessage(), e); 390 } 391 List<MavenProject> reactorProjects = this.reactorProjects; 392 WebAppContext webAppContext; 393 Resource webXml; 394 List<Resource> resources; 395 try { 396 resources = new ArrayList<Resource>(); 397 addCssEngineResources(project, reactorProjects, mappings, resources); 398 for (Artifact a : getOverlayArtifacts(project, scope)) { 399 addOverlayResources(reactorProjects, resources, a); 400 } 401 if (warSourceDirectory == null) { 402 warSourceDirectory = new File(project.getBasedir(), "src/main/webapp"); 403 } 404 if (warSourceDirectory.isDirectory()) { 405 resources.add(Resource.newResource(warSourceDirectory)); 406 } 407 Collections.reverse(resources); 408 getLog().debug("Overlays:"); 409 int index = 0; 410 for (Resource r : resources) { 411 getLog().debug(" [" + index++ + "] = " + r); 412 } 413 final ResourceCollection resourceCollection = 414 new ResourceCollection(resources.toArray(new Resource[resources.size()])); 415 416 webAppContext = new JettyWebAppContext(); 417 webAppContext.setWar(warSourceDirectory.getAbsolutePath()); 418 webAppContext.setBaseResource(resourceCollection); 419 420 WebAppClassLoader classLoader = new WebAppClassLoader(webAppContext); 421 for (String s : getClasspathElements(project, scope)) { 422 classLoader.addClassPath(s); 423 } 424 webAppContext.setClassLoader(classLoader); 425 426 contexts.setHandlers(new Handler[]{webAppContext}); 427 contexts.start(); 428 webAppContext.start(); 429 Resource webInf = webAppContext.getWebInf(); 430 webXml = webInf != null ? webInf.getResource("web.xml") : null; 431 } catch (MojoExecutionException e) { 432 throw e; 433 } catch (MojoFailureException e) { 434 throw e; 435 } catch (ArtifactFilterException e) { 436 throw new MojoExecutionException(e.getMessage(), e); 437 } catch (MalformedURLException e) { 438 throw new MojoExecutionException(e.getMessage(), e); 439 } catch (IOException e) { 440 throw new MojoExecutionException(e.getMessage(), e); 441 } catch (Exception e) { 442 throw new MojoExecutionException(e.getMessage(), e); 443 } 444 445 long webXmlLastModified = webXml == null ? 0L : webXml.lastModified(); 446 try { 447 448 getLog().info("Context started. Will restart if changes to poms detected."); 449 long nextClasspathCheck = System.currentTimeMillis() + classpathCheckInterval; 450 while (true) { 451 long nextCheck = System.currentTimeMillis() + 500; 452 long pomsLastModified = getPomsLastModified(); 453 boolean pomsChanged = lastPomChange < pomsLastModified; 454 boolean overlaysChanged = false; 455 boolean classPathChanged = webXmlLastModified < (webXml == null ? 0L : webXml.lastModified()); 456 if (nextClasspathCheck < System.currentTimeMillis()) { 457 long classChange = classpathLastModified(project); 458 if (classChange > lastClassChange) { 459 classPathChanged = true; 460 lastClassChange = classChange; 461 } 462 nextClasspathCheck = System.currentTimeMillis() + classpathCheckInterval; 463 } 464 if (!classPathChanged && !overlaysChanged && !pomsChanged) { 465 466 try { 467 lastResourceChange = processResourceSourceChanges(reactorProjects, project, lastResourceChange); 468 } catch (ArtifactFilterException e) { 469 getLog().debug("Couldn't process resource changes", e); 470 } 471 try { 472 Thread.sleep(Math.max(100L, nextCheck - System.currentTimeMillis())); 473 } catch (InterruptedException e) { 474 getLog().debug("Interrupted", e); 475 } 476 continue; 477 } 478 if (pomsChanged) { 479 getLog().info("Change in poms detected, re-parsing to evaluate impact..."); 480 // we will now process this change, 481 // so from now on don't re-process 482 // even if we have issues processing 483 lastPomChange = pomsLastModified; 484 List<MavenProject> newReactorProjects; 485 try { 486 newReactorProjects = buildReactorProjects(); 487 } catch (ProjectBuildingException e) { 488 getLog().info("Re-parse aborted due to malformed pom.xml file(s)", e); 489 continue; 490 } catch (CycleDetectedException e) { 491 getLog().info("Re-parse aborted due to dependency cycle in project model", e); 492 continue; 493 } catch (DuplicateProjectException e) { 494 getLog().info("Re-parse aborted due to duplicate projects in project model", e); 495 continue; 496 } catch (Exception e) { 497 getLog().info("Re-parse aborted due a problem that prevented sorting the project model", e); 498 continue; 499 } 500 if (!buildPlanEqual(newReactorProjects, this.reactorProjects)) { 501 throw new BuildPlanModifiedException("A pom.xml change has impacted the build plan."); 502 } 503 MavenProject newProject = findProject(newReactorProjects, this.project); 504 if (newProject == null) { 505 throw new BuildPlanModifiedException( 506 "A pom.xml change appears to have removed " + this.project.getId() 507 + " from the build plan."); 508 } 509 510 newProject.setArtifacts(resolve(newProject, "runtime")); 511 512 getLog().debug("Comparing effective classpath of new and old models"); 513 try { 514 classPathChanged = classPathChanged || classpathsEqual(project, newProject, scope); 515 } catch (DependencyResolutionRequiredException e) { 516 getLog().info("Re-parse aborted due to dependency resolution problems", e); 517 continue; 518 } 519 if (classPathChanged) { 520 getLog().info("Effective classpath of " + project.getId() + " has changed."); 521 } else { 522 getLog().debug("Effective classpath is unchanged."); 523 } 524 525 getLog().debug("Comparing effective overlays of new and old models"); 526 try { 527 overlaysChanged = overlaysEqual(project, newProject); 528 } catch (OverConstrainedVersionException e) { 529 getLog().info("Re-parse aborted due to dependency resolution problems", e); 530 continue; 531 } catch (ArtifactFilterException e) { 532 getLog().info("Re-parse aborted due to overlay resolution problems", e); 533 continue; 534 } 535 if (overlaysChanged) { 536 getLog().info("Overlay modules of " + project.getId() + " have changed."); 537 } else { 538 getLog().debug("Overlay modules are unchanged."); 539 } 540 541 getLog().debug("Comparing overlays paths of new and old models"); 542 try { 543 List<Resource> newResources = new ArrayList<Resource>(); 544 // TODO newMappings 545 addCssEngineResources(newProject, newReactorProjects, mappings, resources); 546 for (Artifact a : getOverlayArtifacts(project, scope)) { 547 addOverlayResources(newReactorProjects, newResources, a); 548 } 549 if (warSourceDirectory.isDirectory()) { 550 newResources.add(Resource.newResource(warSourceDirectory)); 551 } 552 Collections.reverse(newResources); 553 getLog().debug("New overlays:"); 554 int index = 0; 555 for (Resource r : newResources) { 556 getLog().debug(" [" + index++ + "] = " + r); 557 } 558 boolean overlayPathsChanged = !resources.equals(newResources); 559 if (overlayPathsChanged) { 560 getLog().info("Overlay module paths of " + project.getId() + " have changed."); 561 } else { 562 getLog().debug("Overlay module paths are unchanged."); 563 } 564 overlaysChanged = overlaysChanged || overlayPathsChanged; 565 } catch (ArtifactFilterException e) { 566 getLog().info("Re-parse aborted due to overlay evaluation problems", e); 567 continue; 568 } catch (PluginConfigurationException e) { 569 getLog().info("Re-parse aborted due to overlay evaluation problems", e); 570 continue; 571 } catch (PluginContainerException e) { 572 getLog().info("Re-parse aborted due to overlay evaluation problems", e); 573 continue; 574 } catch (IOException e) { 575 getLog().info("Re-parse aborted due to overlay evaluation problems", e); 576 continue; 577 } 578 579 project = newProject; 580 reactorProjects = newReactorProjects; 581 } 582 583 if (!overlaysChanged && !classPathChanged) { 584 continue; 585 } 586 getLog().info("Restarting context to take account of changes..."); 587 try { 588 webAppContext.stop(); 589 } catch (Exception e) { 590 throw new MojoExecutionException(e.getMessage(), e); 591 } 592 593 if (classPathChanged) { 594 getLog().info("Updating classpath..."); 595 try { 596 WebAppClassLoader classLoader = new WebAppClassLoader(webAppContext); 597 for (String s : getClasspathElements(project, scope)) { 598 classLoader.addClassPath(s); 599 } 600 webAppContext.setClassLoader(classLoader); 601 } catch (Exception e) { 602 throw new MojoExecutionException(e.getMessage(), e); 603 } 604 } 605 606 if (overlaysChanged || classPathChanged) { 607 getLog().info("Updating overlays..."); 608 try { 609 resources = new ArrayList<Resource>(); 610 addCssEngineResources(project, reactorProjects, mappings, resources); 611 for (Artifact a : getOverlayArtifacts(project, scope)) { 612 addOverlayResources(reactorProjects, resources, a); 613 } 614 if (warSourceDirectory.isDirectory()) { 615 resources.add(Resource.newResource(warSourceDirectory)); 616 } 617 Collections.reverse(resources); 618 getLog().debug("Overlays:"); 619 int index = 0; 620 for (Resource r : resources) { 621 getLog().debug(" [" + index++ + "] = " + r); 622 } 623 final ResourceCollection resourceCollection = 624 new ResourceCollection(resources.toArray(new Resource[resources.size()])); 625 webAppContext.setBaseResource(resourceCollection); 626 } catch (Exception e) { 627 throw new MojoExecutionException(e.getMessage(), e); 628 } 629 } 630 try { 631 webAppContext.start(); 632 } catch (Exception e) { 633 throw new MojoExecutionException(e.getMessage(), e); 634 } 635 webXmlLastModified = webXml == null ? 0L : webXml.lastModified(); 636 getLog().info("Context restarted."); 637 } 638 639 } finally { 640 try { 641 server.stop(); 642 } catch (Exception e) { 643 throw new MojoExecutionException(e.getMessage(), e); 644 } 645 } 646 } 647 648 private void addOverlayResources(List<MavenProject> reactorProjects, List<Resource> _resources, Artifact a) 649 throws PluginConfigurationException, PluginContainerException, IOException, MojoExecutionException { 650 List<Resource> resources = new ArrayList<Resource>(); 651 MavenProject fromReactor = findProject(reactorProjects, a); 652 if (fromReactor != null) { 653 MavenSession session = this.session.clone(); 654 session.setCurrentProject(fromReactor); 655 Plugin plugin = findThisPluginInProject(fromReactor); 656 657 // we cheat here and use our version of the plugin... but this is less of a cheat than the only 658 // other way which is via reflection. 659 MojoDescriptor jszipDescriptor = findMojoDescriptor(pluginDescriptor, JSZipMojo.class); 660 661 for (PluginExecution pluginExecution : plugin.getExecutions()) { 662 if (!pluginExecution.getGoals().contains(jszipDescriptor.getGoal())) { 663 continue; 664 } 665 MojoExecution mojoExecution = 666 createMojoExecution(plugin, pluginExecution, jszipDescriptor); 667 JSZipMojo mojo = (JSZipMojo) mavenPluginManager 668 .getConfiguredMojo(Mojo.class, session, mojoExecution); 669 try { 670 File contentDirectory = mojo.getContentDirectory(); 671 if (contentDirectory.isDirectory()) { 672 getLog().debug( 673 "Adding resource directory " + contentDirectory); 674 resources.add(Resource.newResource(contentDirectory)); 675 } 676 // TODO filtering support 677 // 678 // The good news: 679 // * resources:resources gets the list of resources from /project/build/resources *only* 680 // The bad news: 681 // * looks like maven-invoker is the only way to safely invoke it again 682 // 683 // probable solution 684 // 685 // 1. get the list of all resource directories, add on the scan for changes 686 // 2. if a change to a non-filtered file, just copy it over 687 // 3. if a change to a filtered file or a change to effective pom, use maven-invoker to run the 688 // lifecycle up to 'compile' or 'process-resources' <-- preferred 689 // 690 File resourcesDirectory = mojo.getResourcesDirectory(); 691 if (resourcesDirectory.isDirectory()) { 692 getLog().debug( 693 "Adding resource directory " + resourcesDirectory); 694 resources.add(Resource.newResource(resourcesDirectory)); 695 } 696 } finally { 697 mavenPluginManager.releaseMojo(mojo, mojoExecution); 698 } 699 } 700 } else { 701 resources.add(Resource.newResource("jar:" + a.getFile().toURI().toURL() + "!/")); 702 } 703 704 // TODO support live reloading of mappings 705 String path = ""; 706 if (mappings != null) { 707 for (Mapping mapping : mappings) { 708 if (mapping.isMatch(a)) { 709 path = StringUtils.clean(mapping.getPath()); 710 break; 711 } 712 } 713 } 714 715 if (StringUtils.isBlank(path)) { 716 _resources.addAll(resources); 717 } else { 718 ResourceCollection child = new ResourceCollection(resources.toArray(new Resource[resources.size()])); 719 _resources.add(new VirtualDirectoryResource(child, path)); 720 } 721 } 722 723 private void addCssEngineResources(MavenProject project, List<MavenProject> reactorProjects, Mapping[] mappings, List<Resource> _resources) 724 throws MojoExecutionException, IOException { 725 List<PseudoFileSystem.Layer> layers = new ArrayList<PseudoFileSystem.Layer>(); 726 layers.add(new PseudoFileSystem.FileLayer("/virtual", warSourceDirectory)); 727 FilterArtifacts filter = new FilterArtifacts(); 728 729 filter.addFilter(new ProjectTransitivityFilter(project.getDependencyArtifacts(), false)); 730 731 filter.addFilter(new ScopeFilter("runtime", "")); 732 733 filter.addFilter(new TypeFilter(JSZIP_TYPE, "")); 734 735 // start with all artifacts. 736 Set<Artifact> artifacts = project.getArtifacts(); 737 738 // perform filtering 739 try { 740 artifacts = filter.filter(artifacts); 741 } catch (ArtifactFilterException e) { 742 throw new MojoExecutionException(e.getMessage(), e); 743 } 744 745 for (Artifact artifact : artifacts) { 746 String path = Mapping.getArtifactPath(mappings, artifact); 747 getLog().info("Adding " + ArtifactUtils.key(artifact) + " to virtual filesystem"); 748 File file = artifact.getFile(); 749 if (file.isDirectory()) { 750 MavenProject fromReactor = findProject(reactorProjects, artifact); 751 if (fromReactor != null) { 752 MavenSession session = this.session.clone(); 753 session.setCurrentProject(fromReactor); 754 Plugin plugin = findThisPluginInProject(fromReactor); 755 try { 756 // we cheat here and use our version of the plugin... but this is less of a cheat than the only 757 // other way which is via reflection. 758 MojoDescriptor jszipDescriptor = findMojoDescriptor(this.pluginDescriptor, JSZipMojo.class); 759 760 for (PluginExecution pluginExecution : plugin.getExecutions()) { 761 if (!pluginExecution.getGoals().contains(jszipDescriptor.getGoal())) { 762 continue; 763 } 764 MojoExecution mojoExecution = 765 createMojoExecution(plugin, pluginExecution, jszipDescriptor); 766 JSZipMojo mojo = (JSZipMojo) mavenPluginManager 767 .getConfiguredMojo(org.apache.maven.plugin.Mojo.class, session, mojoExecution); 768 try { 769 File contentDirectory = mojo.getContentDirectory(); 770 if (contentDirectory.isDirectory()) { 771 getLog().debug("Merging directory " + contentDirectory + " into " + path); 772 layers.add(new PseudoFileSystem.FileLayer(path, contentDirectory)); 773 } 774 File resourcesDirectory = mojo.getResourcesDirectory(); 775 if (resourcesDirectory.isDirectory()) { 776 getLog().debug("Merging directory " + contentDirectory + " into " + path); 777 layers.add(new PseudoFileSystem.FileLayer(path, resourcesDirectory)); 778 } 779 } finally { 780 mavenPluginManager.releaseMojo(mojo, mojoExecution); 781 } 782 } 783 } catch (PluginConfigurationException e) { 784 throw new MojoExecutionException(e.getMessage(), e); 785 } catch (PluginContainerException e) { 786 throw new MojoExecutionException(e.getMessage(), e); 787 } 788 } else { 789 throw new MojoExecutionException("Cannot find jzsip artifact: " + artifact.getId()); 790 } 791 } else { 792 try { 793 getLog().debug("Merging .zip file " + file + " into " + path); 794 layers.add(new PseudoFileSystem.ZipLayer(path, file)); 795 } catch (IOException e) { 796 throw new MojoExecutionException(e.getMessage(), e); 797 } 798 } 799 } 800 801 final PseudoFileSystem fs = new PseudoFileSystem(layers); 802 803 CssEngine engine = new LessEngine(fs, encoding == null ? "utf-8" : encoding, getLog(), lessCompress, customLessScript, showErrorExtracts); 804 805 // look for files to compile 806 807 PseudoDirectoryScanner scanner = new PseudoDirectoryScanner(); 808 809 scanner.setFileSystem(fs); 810 811 scanner.setBasedir(fs.getPseudoFile("/virtual")); 812 813 if (lessIncludes != null && !lessIncludes.isEmpty()) { 814 scanner.setIncludes(processIncludesExcludes(lessIncludes)); 815 } else { 816 scanner.setIncludes(new String[]{"**/*.less"}); 817 } 818 819 if (lessExcludes != null && !lessExcludes.isEmpty()) { 820 scanner.setExcludes(processIncludesExcludes(lessExcludes)); 821 } else { 822 scanner.setExcludes(new String[0]); 823 } 824 825 scanner.scan(); 826 827 for (String fileName : new ArrayList<String>(Arrays.asList(scanner.getIncludedFiles()))) { 828 final CssEngineResource child = new CssEngineResource(fs, engine, "/virtual/" + fileName); 829 final String path = FileUtils.dirname(fileName); 830 if (StringUtils.isBlank(path)) { 831 _resources.add(new VirtualDirectoryResource(new VirtualDirectoryResource(child, child.getName()), "")); 832 } else { 833 _resources.add(new VirtualDirectoryResource(new VirtualDirectoryResource(child, child.getName()), path)); 834 } 835 } 836 837 engine = new SassEngine(fs, encoding == null ? "utf-8" : encoding); 838 839 if (sassIncludes != null && !sassIncludes.isEmpty()) { 840 scanner.setIncludes(processIncludesExcludes(sassIncludes)); 841 } else { 842 scanner.setIncludes(new String[]{"**/*.sass","**/*.scss"}); 843 } 844 845 if (sassExcludes != null && !sassExcludes.isEmpty()) { 846 scanner.setExcludes(processIncludesExcludes(sassExcludes)); 847 } else { 848 scanner.setExcludes(new String[]{"**/_*.sass","**/_*.scss"}); 849 } 850 851 scanner.scan(); 852 853 for (String fileName : new ArrayList<String>(Arrays.asList(scanner.getIncludedFiles()))) { 854 final CssEngineResource child = new CssEngineResource(fs, engine, "/virtual/" + fileName); 855 final String path = FileUtils.dirname(fileName); 856 if (StringUtils.isBlank(path)) { 857 _resources.add(new VirtualDirectoryResource(new VirtualDirectoryResource(child, child.getName()), "")); 858 } else { 859 _resources.add(new VirtualDirectoryResource(new VirtualDirectoryResource(child, child.getName()), path)); 860 } 861 } 862 863 } 864 865 private void injectMissingArtifacts(MavenProject destination, MavenProject source) { 866 if (destination.getArtifact().getFile() == null && source.getArtifact().getFile() != null) { 867 getLog().info("Pushing primary artifact from forked execution into current execution"); 868 destination.getArtifact().setFile(source.getArtifact().getFile()); 869 } 870 for (Artifact executedArtifact : source.getAttachedArtifacts()) { 871 String executedArtifactId = 872 (executedArtifact.getClassifier() == null ? "." : "-" + executedArtifact.getClassifier() + ".") 873 + executedArtifact.getType(); 874 if (StringUtils.equals(executedArtifact.getGroupId(), destination.getGroupId()) 875 && StringUtils.equals(executedArtifact.getArtifactId(), destination.getArtifactId()) 876 && StringUtils.equals(executedArtifact.getVersion(), destination.getVersion())) { 877 boolean found = false; 878 for (Artifact artifact : destination.getAttachedArtifacts()) { 879 if (StringUtils.equals(artifact.getGroupId(), destination.getGroupId()) 880 && StringUtils.equals(artifact.getArtifactId(), destination.getArtifactId()) 881 && StringUtils.equals(artifact.getVersion(), destination.getVersion()) 882 && StringUtils.equals(artifact.getClassifier(), executedArtifact.getClassifier()) 883 && StringUtils.equals(artifact.getType(), executedArtifact.getType())) { 884 if (artifact.getFile() == null) { 885 getLog().info("Pushing " + executedArtifactId 886 + " artifact from forked execution into current execution"); 887 artifact.setFile(executedArtifact.getFile()); 888 } 889 found = true; 890 } 891 } 892 if (!found) { 893 getLog().info("Attaching " + 894 executedArtifactId 895 + " artifact from forked execution into current execution"); 896 projectHelper 897 .attachArtifact(destination, executedArtifact.getType(), executedArtifact.getClassifier(), 898 executedArtifact.getFile()); 899 } 900 } 901 } 902 } 903 904 private long processResourceSourceChanges(List<MavenProject> reactorProjects, MavenProject project, 905 long lastModified) 906 throws ArtifactFilterException { 907 long newLastModified = lastModified; 908 getLog().debug("Last modified for resource sources = " + lastModified); 909 910 Set<File> checked = new HashSet<File>(); 911 for (Artifact a : getOverlayArtifacts(project, scope)) { 912 MavenProject p = findProject(reactorProjects, a); 913 if (p == null || p.getBuild() == null || p.getBuild().getResources() == null) { 914 continue; 915 } 916 boolean changed = false; 917 boolean changedFiltered = false; 918 for (org.apache.maven.model.Resource r : p.getBuild().getResources()) { 919 File dir = new File(r.getDirectory()); 920 getLog().debug("Checking last modified for " + dir); 921 if (checked.contains(dir)) { 922 continue; 923 } 924 checked.add(dir); 925 long dirLastModified = recursiveLastModified(dir); 926 if (lastModified < dirLastModified) { 927 changed = true; 928 if (r.isFiltering()) { 929 changedFiltered = true; 930 } 931 } 932 } 933 if (changedFiltered) { 934 getLog().info("Detected change in resources of " + ArtifactUtils.versionlessKey(a) + "..."); 935 getLog().debug("Resource filtering is used by project, invoking Maven to handle update"); 936 // need to let Maven handle it as its the only (although slower) safe way to do it right with filters 937 InvocationRequest request = new DefaultInvocationRequest(); 938 request.setPomFile(p.getFile()); 939 request.setInteractive(false); 940 request.setRecursive(false); 941 request.setGoals(Collections.singletonList("process-resources")); 942 943 Invoker invoker = new DefaultInvoker(); 944 invoker.setLogger(new MavenProxyLogger()); 945 try { 946 invoker.execute(request); 947 newLastModified = System.currentTimeMillis(); 948 getLog().info("Change in resources of " + ArtifactUtils.versionlessKey(a) + " processed"); 949 } catch (MavenInvocationException e) { 950 getLog().info(e); 951 } 952 } else if (changed) { 953 getLog().info("Detected change in resources of " + ArtifactUtils.versionlessKey(a) + "..."); 954 getLog().debug("Resource filtering is not used by project, handling update ourselves"); 955 // can do it fast ourselves 956 MavenResourcesExecution mavenResourcesExecution = 957 new MavenResourcesExecution(p.getResources(), new File(p.getBuild().getOutputDirectory()), p, 958 p.getProperties().getProperty("project.build.sourceEncoding"), Collections.emptyList(), 959 Collections.<String>emptyList(), session); 960 try { 961 mavenResourcesFiltering.filterResources(mavenResourcesExecution); 962 newLastModified = System.currentTimeMillis(); 963 getLog().info("Change in resources of " + ArtifactUtils.versionlessKey(a) + " processed"); 964 } catch (MavenFilteringException e) { 965 getLog().info(e); 966 } 967 } 968 } 969 return newLastModified; 970 } 971 972 private List<MavenProject> buildReactorProjects() throws Exception { 973 974 List<MavenProject> projects = new ArrayList<MavenProject>(); 975 for (MavenProject p : reactorProjects) { 976 ProjectBuildingRequest request = new DefaultProjectBuildingRequest(); 977 978 request.setProcessPlugins(true); 979 request.setProfiles(request.getProfiles()); 980 request.setActiveProfileIds(session.getRequest().getActiveProfiles()); 981 request.setInactiveProfileIds(session.getRequest().getInactiveProfiles()); 982 request.setRemoteRepositories(session.getRequest().getRemoteRepositories()); 983 request.setSystemProperties(session.getSystemProperties()); 984 request.setUserProperties(session.getUserProperties()); 985 request.setRemoteRepositories(session.getRequest().getRemoteRepositories()); 986 request.setPluginArtifactRepositories(session.getRequest().getPluginArtifactRepositories()); 987 request.setRepositorySession(session.getRepositorySession()); 988 request.setLocalRepository(localRepository); 989 request.setBuildStartTime(session.getRequest().getStartTime()); 990 request.setResolveDependencies(true); 991 request.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_STRICT); 992 projects.add(projectBuilder.build(p.getFile(), request).getProject()); 993 } 994 return new ProjectSorter(projects).getSortedProjects(); 995 } 996 997 private long classpathLastModified(MavenProject project) { 998 long result = Long.MIN_VALUE; 999 try { 1000 for (String element : getClasspathElements(project, scope)) { 1001 File elementFile = new File(element); 1002 result = Math.max(recursiveLastModified(elementFile), result); 1003 } 1004 } catch (DependencyResolutionRequiredException e) { 1005 // ignore 1006 } 1007 return result; 1008 1009 } 1010 1011 private long recursiveLastModified(File fileOrDirectory) { 1012 long result = Long.MIN_VALUE; 1013 if (fileOrDirectory.exists()) { 1014 result = Math.max(fileOrDirectory.lastModified(), result); 1015 if (fileOrDirectory.isDirectory()) { 1016 Stack<Iterator<File>> stack = new Stack<Iterator<File>>(); 1017 stack.push(contentsAsList(fileOrDirectory).iterator()); 1018 while (!stack.empty()) { 1019 Iterator<File> i = stack.pop(); 1020 while (i.hasNext()) { 1021 File file = i.next(); 1022 result = Math.max(file.lastModified(), result); 1023 if (file.isDirectory()) { 1024 stack.push(i); 1025 i = contentsAsList(file).iterator(); 1026 } 1027 } 1028 } 1029 } 1030 } 1031 return result; 1032 } 1033 1034 private static List<File> contentsAsList(File directory) { 1035 File[] files = directory.listFiles(); 1036 return files == null ? Collections.<File>emptyList() : Arrays.asList(files); 1037 } 1038 1039 private boolean classpathsEqual(MavenProject oldProject, MavenProject newProject, String scope) 1040 throws DependencyResolutionRequiredException { 1041 int seq = 0; 1042 List<String> newCP = getClasspathElements(newProject, scope); 1043 List<String> oldCP = getClasspathElements(oldProject, scope); 1044 boolean classPathChanged = newCP.size() != oldCP.size(); 1045 for (Iterator<String> i = newCP.iterator(), j = oldCP.iterator(); i.hasNext() || j.hasNext(); ) { 1046 String left = i.hasNext() ? i.next() : "(empty)"; 1047 String right = j.hasNext() ? j.next() : "(empty)"; 1048 if (!StringUtils.equals(left, right)) { 1049 getLog().debug("classpath[" + seq + "]"); 1050 getLog().debug(" old = " + left); 1051 getLog().debug(" new = " + right); 1052 classPathChanged = true; 1053 } 1054 seq++; 1055 } 1056 return classPathChanged; 1057 } 1058 1059 private MavenProject findProject(List<MavenProject> newReactorProjects, MavenProject oldProject) { 1060 final String targetId = oldProject.getId(); 1061 for (MavenProject newProject : newReactorProjects) { 1062 if (targetId.equals(newProject.getId())) { 1063 return newProject; 1064 } 1065 } 1066 return null; 1067 } 1068 1069 private boolean buildPlanEqual(List<MavenProject> newPlan, List<MavenProject> oldPlan) { 1070 if (newPlan.size() != oldPlan.size()) { 1071 return false; 1072 } 1073 int seq = 0; 1074 for (Iterator<MavenProject> i = newPlan.iterator(), j = oldPlan.iterator(); i.hasNext() && j.hasNext(); ) { 1075 MavenProject left = i.next(); 1076 MavenProject right = j.next(); 1077 getLog().debug( 1078 "[" + (seq++) + "] = " + left.equals(right) + (left == right ? " same" : " diff") + " : " 1079 + left.getName() + "[" + left.getDependencies().size() + "], " + right.getName() 1080 + "[" 1081 + right.getDependencies().size() + "]"); 1082 if (!left.equals(right)) { 1083 return false; 1084 } 1085 if (left.getDependencies().size() != right.getDependencies().size()) { 1086 getLog().info("Dependency tree of " + left.getId() + " has been modified"); 1087 } 1088 } 1089 return true; 1090 } 1091 1092 private boolean overlaysEqual(MavenProject oldProject, MavenProject newProject) 1093 throws ArtifactFilterException, OverConstrainedVersionException { 1094 boolean overlaysChanged; 1095 Set<Artifact> newOA = getOverlayArtifacts(newProject, scope); 1096 Set<Artifact> oldOA = getOverlayArtifacts(oldProject, scope); 1097 overlaysChanged = newOA.size() != oldOA.size(); 1098 for (Artifact n : newOA) { 1099 boolean found = false; 1100 for (Artifact o : oldOA) { 1101 if (StringUtils.equals(n.getArtifactId(), o.getArtifactId()) && StringUtils 1102 .equals(n.getGroupId(), o.getGroupId())) { 1103 if (o.getSelectedVersion().equals(n.getSelectedVersion())) { 1104 found = true; 1105 break; 1106 } 1107 } 1108 } 1109 if (!found) { 1110 getLog().debug("added overlay artifact: " + n); 1111 overlaysChanged = true; 1112 } 1113 } 1114 for (Artifact o : oldOA) { 1115 boolean found = false; 1116 for (Artifact n : newOA) { 1117 if (StringUtils.equals(n.getArtifactId(), o.getArtifactId()) && StringUtils 1118 .equals(n.getGroupId(), o.getGroupId())) { 1119 if (o.getSelectedVersion().equals(n.getSelectedVersion())) { 1120 found = true; 1121 break; 1122 } 1123 } 1124 } 1125 if (!found) { 1126 getLog().debug("removed overlay artifact: " + o); 1127 overlaysChanged = true; 1128 } 1129 } 1130 if (overlaysChanged) { 1131 getLog().info("Effective overlays of " + oldProject.getId() + " have changed."); 1132 } else { 1133 getLog().debug("Effective overlays are unchanged."); 1134 } 1135 return overlaysChanged; 1136 } 1137 1138 @SuppressWarnings("unchecked") 1139 private Set<Artifact> getOverlayArtifacts(MavenProject project, String scope) throws ArtifactFilterException { 1140 FilterArtifacts filter = new FilterArtifacts(); 1141 1142 filter.addFilter(new ProjectTransitivityFilter(project.getDependencyArtifacts(), false)); 1143 1144 filter.addFilter(new ScopeFilter(scope, "")); 1145 1146 filter.addFilter(new TypeFilter(JSZIP_TYPE, "")); 1147 1148 return filter.filter(project.getArtifacts()); 1149 } 1150 1151 private long getPomsLastModified() { 1152 long result = Long.MIN_VALUE; 1153 for (MavenProject p : reactorProjects) { 1154 result = Math.max(p.getFile().lastModified(), result); 1155 } 1156 return result; 1157 } 1158 1159 @SuppressWarnings("unchecked") 1160 private List<String> getClasspathElements(MavenProject project, String scope) 1161 throws DependencyResolutionRequiredException { 1162 if ("test".equals(scope)) { 1163 return project.getTestClasspathElements(); 1164 } 1165 if ("compile".equals(scope)) { 1166 return project.getCompileClasspathElements(); 1167 } 1168 if ("runtime".equals(scope)) { 1169 return project.getRuntimeClasspathElements(); 1170 } 1171 return Collections.emptyList(); 1172 } 1173 1174 @SuppressWarnings("unchecked") 1175 private Set<Artifact> resolve(MavenProject newProject, String scope) 1176 throws MojoExecutionException { 1177 try { 1178 return projectDependenciesResolver.resolve(newProject, Collections.singletonList(scope), session); 1179 } catch (ArtifactNotFoundException e) { 1180 throw new MojoExecutionException(e.getMessage(), e); 1181 } catch (ArtifactResolutionException e) { 1182 throw new MojoExecutionException(e.getMessage(), e); 1183 } 1184 } 1185 1186 public void setSystemProperties(SystemProperties systemProperties) { 1187 if (this.systemProperties == null) { 1188 this.systemProperties = systemProperties; 1189 } else { 1190 Iterator itor = systemProperties.getSystemProperties().iterator(); 1191 while (itor.hasNext()) { 1192 SystemProperty prop = (SystemProperty) itor.next(); 1193 this.systemProperties.setSystemProperty(prop); 1194 } 1195 } 1196 } 1197 1198 public SystemProperties getSystemProperties() { 1199 return this.systemProperties; 1200 } 1201 1202 1203 private class MavenProxyLogger implements InvokerLogger { 1204 1205 public void debug(String content) { 1206 getLog().debug(content); 1207 } 1208 1209 public void info(Throwable error) { 1210 getLog().info(error); 1211 } 1212 1213 public void info(String content, Throwable error) { 1214 getLog().info(content, error); 1215 } 1216 1217 public void info(String content) { 1218 getLog().info(content); 1219 } 1220 1221 public void warn(Throwable error) { 1222 getLog().warn(error); 1223 } 1224 1225 public void error(String content, Throwable error) { 1226 getLog().error(content, error); 1227 } 1228 1229 public void debug(String content, Throwable error) { 1230 getLog().debug(content, error); 1231 } 1232 1233 public void debug(Throwable error) { 1234 getLog().debug(error); 1235 } 1236 1237 public void warn(String content) { 1238 getLog().warn(content); 1239 } 1240 1241 public void error(Throwable error) { 1242 getLog().error(error); 1243 } 1244 1245 public void error(String content) { 1246 getLog().error(content); 1247 } 1248 1249 public void warn(String content, Throwable error) { 1250 getLog().warn(content, error); 1251 } 1252 1253 public void fatalError(String s) { 1254 getLog().error(s); 1255 } 1256 1257 public boolean isDebugEnabled() { 1258 return getLog().isDebugEnabled(); 1259 } 1260 1261 public boolean isInfoEnabled() { 1262 return getLog().isInfoEnabled(); 1263 } 1264 1265 public boolean isWarnEnabled() { 1266 return getLog().isWarnEnabled(); 1267 } 1268 1269 public boolean isErrorEnabled() { 1270 return getLog().isErrorEnabled(); 1271 } 1272 1273 public void fatalError(String s, Throwable throwable) { 1274 getLog().error(s, throwable); 1275 } 1276 1277 public boolean isFatalErrorEnabled() { 1278 return getLog().isErrorEnabled(); 1279 } 1280 1281 public void setThreshold(int i) { 1282 } 1283 1284 public int getThreshold() { 1285 return 0; 1286 } 1287 } 1288}