| package org.apache.maven.dist.tools; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.io.File; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import org.apache.commons.io.FileUtils; |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.artifact.factory.ArtifactFactory; |
| import org.apache.maven.artifact.repository.ArtifactRepository; |
| import org.apache.maven.dist.tools.checkers.HTMLChecker; |
| import org.apache.maven.dist.tools.checkers.HTMLCheckerFactory; |
| import org.apache.maven.doxia.sink.Sink; |
| import org.apache.maven.doxia.sink.SinkEventAttributeSet; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugins.annotations.Component; |
| import org.apache.maven.plugins.annotations.Mojo; |
| import org.apache.maven.plugins.annotations.Parameter; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.project.MavenProjectBuilder; |
| import org.apache.maven.reporting.MavenReportException; |
| import org.jsoup.HttpStatusException; |
| import org.jsoup.Jsoup; |
| import org.jsoup.nodes.Comment; |
| import org.jsoup.nodes.Document; |
| import org.jsoup.nodes.Element; |
| import org.jsoup.nodes.Node; |
| import org.jsoup.select.Elements; |
| import org.openqa.selenium.OutputType; |
| import org.openqa.selenium.TakesScreenshot; |
| import org.openqa.selenium.WebDriver; |
| import org.openqa.selenium.firefox.FirefoxDriver; |
| |
| /** |
| * |
| * @author skygo |
| */ |
| @Mojo( name = "check-site", requiresProject = false ) |
| public class DistCheckSiteMojo |
| extends AbstractDistCheckMojo |
| { |
| static final String FAILURES_FILENAME = "check-site.log"; |
| |
| /** |
| * Ignore site failure for <code>artifactId</code> or <code>artifactId:version</code> |
| */ |
| @Parameter |
| protected List<String> ignoreSiteFailures; |
| |
| /** |
| * Artifact factory. |
| */ |
| @Component |
| protected ArtifactFactory artifactFactory; |
| |
| /** |
| * Local repository. |
| */ |
| @Parameter( defaultValue = "${localRepository}", required = true, readonly = true ) |
| protected ArtifactRepository localRepository; |
| |
| /** |
| * Maven project builder. |
| */ |
| @Component |
| protected MavenProjectBuilder mavenProjectBuilder; |
| |
| /** |
| * Take screenshot with web browser |
| */ |
| @Parameter( property = "screenshot", defaultValue = "false" ) |
| protected boolean screenShot; |
| |
| private static final String MAVEN_SITE = "http://maven.apache.org"; |
| |
| /** |
| * Http status ok code. |
| */ |
| protected static final int HTTP_OK = 200; |
| @Override |
| boolean useDetailed() |
| { |
| return false; |
| } |
| protected String getFailuresFilename() |
| { |
| return FAILURES_FILENAME; |
| } |
| |
| @Override |
| public String getOutputName() |
| { |
| return "dist-tool-checksite"; |
| } |
| |
| @Override |
| public String getName( Locale locale ) |
| { |
| return "Disttool> Sites"; |
| } |
| |
| @Override |
| public String getDescription( Locale locale ) |
| { |
| return "Verification of various maven web sites"; |
| } |
| |
| class DistCheckSiteResult extends AbstractCheckResult |
| { |
| |
| private String url; |
| private Map<HTMLChecker, Boolean> checkMap = new HashMap<>(); |
| private int statusCode = HTTP_OK; |
| private Document document; |
| private String screenshotName; |
| |
| public DistCheckSiteResult( ConfigurationLineInfo r, String version ) |
| { |
| super( r, version ); |
| } |
| |
| void setUrl( String url ) |
| { |
| this.url = url; |
| } |
| |
| /** |
| * @return the url |
| */ |
| public String getUrl() |
| { |
| return url; |
| } |
| |
| /** |
| * @return the checkMap |
| */ |
| public Map<HTMLChecker, Boolean> getCheckMap() |
| { |
| return checkMap; |
| } |
| |
| private void setHTTPErrorUrl( int status ) |
| { |
| this.statusCode = status; |
| } |
| |
| /** |
| * @return the statusCode |
| */ |
| public int getStatusCode() |
| { |
| return statusCode; |
| } |
| |
| private void getSkins( Sink sink ) |
| { |
| if ( statusCode != HTTP_OK ) |
| { |
| sink.text( "None" ); |
| } |
| else |
| { |
| String text = ""; |
| Elements htmlTag = document.select( "html " ); |
| for ( Element htmlTa : htmlTag ) |
| { |
| Node n = htmlTa.previousSibling(); |
| if ( n instanceof Comment ) |
| { |
| text += ( ( Comment ) n ).getData(); |
| } |
| else |
| { |
| text += " "; |
| } |
| } |
| |
| sink.text( "skin: " ); |
| if ( isSkin( "Fluido" ) ) |
| { |
| sink.text( "Fluido" ); |
| } |
| else if ( isSkin( "Stylus" ) ) |
| { |
| sink.text( "Stylus" ); |
| } |
| else |
| { |
| sink.text( "Not determined" ); |
| } |
| sink.verbatim( null ); |
| sink.text( text.trim().replace( " |", "|" ).replace( "| ", "" ) ); |
| sink.verbatim_(); |
| } |
| } |
| |
| private void getOverall( Sink sink ) |
| { |
| if ( statusCode != HTTP_OK ) |
| { |
| iconError( sink ); |
| } |
| else |
| { |
| boolean found = false; |
| for ( Map.Entry<HTMLChecker, Boolean> e : checkMap.entrySet() ) |
| { |
| if ( e.getValue() ) |
| { |
| iconSuccess( sink ); |
| sink.text( ": " + e.getKey().getName() ); |
| found = true; |
| } |
| } |
| if ( !found ) |
| { |
| iconWarning( sink ); |
| sink.text( ": artifact version not found" ); |
| } |
| } |
| } |
| |
| private boolean isSkin( String skinName ) |
| { |
| boolean tmp = false; |
| for ( Map.Entry<HTMLChecker, Boolean> e : checkMap.entrySet() ) |
| { |
| if ( e.getKey().getSkin().equals( skinName ) ) |
| { |
| tmp = tmp || e.getValue(); |
| } |
| } |
| return tmp; |
| } |
| |
| private void setDocument( Document doc ) |
| { |
| this.document = doc ; |
| } |
| |
| private void setScreenShot( String fileName ) |
| { |
| this.screenshotName = fileName; |
| } |
| private String getScreenShot() |
| { |
| return screenshotName; |
| } |
| } |
| |
| // keep result |
| private List<DistCheckSiteResult> results = new LinkedList<>(); |
| private final List<HTMLChecker> checker = HTMLCheckerFactory.getCheckers(); |
| private WebDriver driver; |
| |
| @Override |
| protected void executeReport( Locale locale ) |
| throws MavenReportException |
| { |
| if ( !outputDirectory.exists() ) |
| { |
| outputDirectory.mkdirs(); |
| } |
| try |
| { |
| this.execute(); |
| } |
| catch ( MojoExecutionException ex ) |
| { |
| throw new MavenReportException( ex.getMessage(), ex ); |
| } |
| Sink sink = getSink(); |
| sink.head(); |
| sink.title(); |
| sink.text( "Check sites" ); |
| sink.title_(); |
| sink.head_(); |
| |
| sink.body(); |
| sink.section1(); |
| sink.rawText( "Checked sites, also do some basic checking in index.html contents." ); |
| sink.rawText( "This is to help maintaining some coherence. How many site are skin fluido, stylus," |
| + " where they have artifact version (right, left)" ); |
| sink.rawText( "All sun icons in one column is kind of objective." ); |
| sink.section1_(); |
| sink.table(); |
| sink.tableRow(); |
| sink.tableHeaderCell(); |
| sink.rawText( "groupId/artifactId" ); |
| sink.tableHeaderCell_(); |
| sink.tableHeaderCell(); |
| sink.rawText( "LATEST" ); |
| sink.tableHeaderCell_(); |
| sink.tableHeaderCell(); |
| sink.rawText( "DATE" ); |
| sink.tableHeaderCell_(); |
| sink.tableHeaderCell(); |
| sink.rawText( "URL" ); |
| sink.lineBreak(); |
| sink.rawText( "Skins" ); |
| sink.lineBreak(); |
| sink.rawText( "Comments on top of html" ); |
| sink.tableHeaderCell_(); |
| if ( screenShot ) |
| { |
| sink.tableHeaderCell(); |
| sink.rawText( "Screen" ); |
| sink.tableHeaderCell_(); |
| } |
| sink.tableHeaderCell(); |
| sink.rawText( "Artifact version displayed" ); |
| sink.tableHeaderCell_(); |
| sink.tableRow_(); |
| |
| String directory = null; |
| for ( DistCheckSiteResult csr : results ) |
| { |
| if ( !csr.getConfigurationLine().getDirectory().equals( directory ) ) |
| { |
| directory = csr.getConfigurationLine().getDirectory(); |
| sink.tableRow(); |
| sink.tableHeaderCell(); |
| // shorten groupid |
| sink.rawText( csr.getConfigurationLine().getGroupId().replaceAll( "org.apache.maven", "o.a.m" ) ); |
| sink.tableHeaderCell_(); |
| for ( int i = 0; i < 5 ; i++ ) |
| { |
| sink.tableHeaderCell(); |
| sink.rawText( " " ); |
| sink.tableHeaderCell_(); |
| } |
| sink.tableRow_(); |
| } |
| |
| sink.tableRow(); |
| sink.tableCell(); |
| sink.rawText( csr.getConfigurationLine().getArtifactId() ); |
| sink.tableCell_(); |
| |
| sink.tableCell(); |
| sink.rawText( csr.getVersion() ); |
| sink.tableCell_(); |
| |
| sink.tableCell(); |
| sink.rawText( csr.getConfigurationLine().getReleaseFromMetadata() ); |
| sink.tableCell_(); |
| sink.tableCell(); |
| if ( csr.getStatusCode() != HTTP_OK ) |
| { |
| iconError( sink ); |
| sink.rawText( "[" + csr.getStatusCode() + "] " ); |
| } |
| sink.link( csr.getUrl() ); |
| sink.rawText( getSimplifiedUrl( csr.getUrl() ) ); |
| sink.link_(); |
| sink.lineBreak(); |
| csr.getSkins( sink ); |
| sink.tableCell_(); |
| if ( screenShot ) |
| { |
| sink.tableCell(); |
| sink.figure( null ); |
| SinkEventAttributeSet atts = new SinkEventAttributeSet(); |
| // no direct attribute, override style only |
| atts.addAttribute( "style", "height:200px;width:200px" ); |
| atts.addAttribute( "alt", getSimplifiedUrl( csr.getUrl() ) ); |
| sink.figureGraphics( csr.getScreenShot(), atts ); |
| sink.figure_(); |
| sink.tableCell_(); |
| } |
| |
| sink.tableCell(); |
| csr.getOverall( sink ); |
| sink.tableCell_(); |
| |
| sink.tableRow_(); |
| } |
| sink.table_(); |
| sink.body_(); |
| sink.flush(); |
| sink.close(); |
| } |
| |
| private String getSimplifiedUrl( String url ) |
| { |
| if ( url.startsWith( MAVEN_SITE ) ) |
| { |
| return url.substring( MAVEN_SITE.length() ); |
| } |
| return url; |
| } |
| |
| private void checkSite( ConfigurationLineInfo cli, String version ) |
| { |
| DistCheckSiteResult result = new DistCheckSiteResult( cli, version ); |
| results.add( result ); |
| try |
| { |
| Artifact artifact = |
| artifactFactory.createProjectArtifact( cli.getGroupId(), cli.getArtifactId(), version ); |
| MavenProject artifactProject = |
| mavenProjectBuilder.buildFromRepository( artifact, artifactRepositories, localRepository, false ); |
| |
| result.setUrl( artifactProject.getUrl() ); |
| Document doc = Jsoup.connect( artifactProject.getUrl() ).get(); |
| if ( screenShot ) |
| { |
| driver.get( artifactProject.getUrl() ); |
| File scrFile = ( ( TakesScreenshot ) driver ).getScreenshotAs( OutputType.FILE ); |
| String fileName = "images" + File.separator |
| + cli.getGroupId() + "_" + cli.getArtifactId() + ".png"; |
| result.setScreenShot( fileName ); |
| FileUtils.copyFile( scrFile, new File( getReportOutputDirectory(), fileName ) ); |
| } |
| for ( HTMLChecker c : checker ) |
| { |
| result.getCheckMap().put( c, c.isOk( doc, version ) ); |
| } |
| result.setDocument( doc ); |
| |
| } |
| catch ( HttpStatusException hes ) |
| { |
| addErrorLine( cli, |
| version, |
| ignoreSiteFailures, |
| "HTTP result code: " + hes.getStatusCode() + " for " + cli.getArtifactId() + " site = " |
| + hes.getUrl() ); |
| result.setHTTPErrorUrl( hes.getStatusCode() ); |
| } |
| catch ( Exception ex ) |
| { |
| //continue for other artifact |
| getLog().error( ex.getMessage() + cli.getArtifactId() ); |
| } |
| |
| } |
| |
| @Override |
| protected void checkArtifact( ConfigurationLineInfo configLine, String latestVersion ) |
| throws MojoExecutionException |
| { |
| checkSite( configLine, latestVersion ); |
| } |
| |
| @Override |
| public void execute() |
| throws MojoExecutionException |
| { |
| try |
| { |
| //resolve only to what we set |
| if ( screenShot ) |
| { |
| // create driver once reduce time to complete mojo |
| driver = new FirefoxDriver(); |
| } |
| super.execute(); |
| } |
| finally |
| { |
| if ( screenShot ) |
| { |
| driver.close(); |
| } |
| } |
| } |
| } |