blob: 9d6fc046531aec14fcde01a14e6b557ea871a0d4 [file] [log] [blame]
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.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import org.apache.commons.io.FileUtils;
import org.apache.maven.artifact.Artifact;
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.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
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" )
public class DistCheckSiteMojo extends AbstractDistCheckMojo
{
/**
* Take screenshot with web browser
*/
@Parameter( property = "screenshot", defaultValue = "true" )
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
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 += "Nothing";
}
}
if ( isSkink( "Fluido" ) )
{
sink.text( "Fluido" );
}
else if ( isSkink( "Stylus" ) )
{
sink.text( "Stylus" );
}
else
{
sink.text( "Not determined" );
}
sink.monospaced();
sink.text( text );
sink.monospaced_();
}
}
private void getOverall( Sink sink )
{
if ( statusCode != HTTP_OK )
{
iconError( sink );
}
else
{
boolean tmp = false;
for ( Map.Entry<HTMLChecker, Boolean> e : checkMap.entrySet() )
{
tmp = tmp || e.getValue();
}
if ( tmp )
{
iconSuccess( sink );
}
else
{
iconWarning( sink );
}
}
}
private boolean isSkink( 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 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" );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.rawText( "artifactId" );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.rawText( "LATEST" );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.rawText( "DATE" );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.rawText( "URL" );
sink.tableHeaderCell_();
if ( screenShot )
{
sink.tableHeaderCell();
sink.rawText( "Screen" );
sink.tableHeaderCell_();
}
sink.tableHeaderCell();
sink.rawText( "Skins and comments on top of html (helping for date but not always)" );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.rawText( "Precise and overkill contents check summary details on your left ==>" );
sink.tableHeaderCell_();
for ( HTMLChecker c : checker )
{
sink.tableHeaderCell();
sink.rawText( c.getName() );
sink.tableHeaderCell_();
}
sink.tableRow_();
for ( DistCheckSiteResult csr : results )
{
sink.tableRow();
sink.tableCell();
sink.rawText( csr.getConfigurationLine().getGroupId() );
sink.tableCell_();
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.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.getSkins( sink );
sink.tableCell_();
sink.tableCell();
csr.getOverall( sink );
sink.tableCell_();
for ( HTMLChecker c : checker )
{
sink.tableCell();
if ( csr.getCheckMap().get( c ) != null )
{
if ( csr.getCheckMap().get( c ) )
{
iconSuccess( sink );
}
else
{
iconWarning( sink );
}
}
else
{
iconError( 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( String repourl, ConfigurationLineInfo configLine, String version )
{
DistCheckSiteResult result = new DistCheckSiteResult( configLine, version );
results.add( result );
StringBuilder message = new StringBuilder();
try
{
Artifact pluginArtifact = artifactFactory.createProjectArtifact(
configLine.getGroupId(),
configLine.getArtifactId(), version );
MavenProject pluginProject = mavenProjectBuilder.buildFromRepository(
pluginArtifact,
artifactRepositories, localRepository, false );
result.setUrl( pluginProject.getUrl() );
Document doc = Jsoup.connect( pluginProject.getUrl() ).get();
if ( screenShot )
{
driver.get( pluginProject.getUrl() );
File scrFile = ( ( TakesScreenshot ) driver ).getScreenshotAs( OutputType.FILE );
String fileName = "images" + File.separator
+ configLine.getGroupId() + "_" + configLine.getArtifactId() + ".png";
result.setScreenShot( fileName );
FileUtils.copyFile( scrFile, new File( getReportOutputDirectory() + File.separator + fileName ) );
}
for ( HTMLChecker c : checker )
{
result.getCheckMap().put( c, c.isOk( doc, version ) );
}
result.setDocument( doc );
}
catch ( HttpStatusException hes )
{
getLog().error( hes.getStatusCode() + " for " + hes.getUrl() );
result.setHTTPErrorUrl( hes.getStatusCode() );
}
catch ( Exception ex )
{
//continue for other artifact
getLog().error( ex.getMessage() );
getLog().error( ex );
}
}
@Override
void checkArtifact( ConfigurationLineInfo configLine, String repoBaseUrl ) throws MojoExecutionException
{
try ( BufferedReader input = new BufferedReader(
new InputStreamReader( new URL( configLine.getMetadataFileURL( repoBaseUrl ) ).openStream() ) ) )
{
JAXBContext context = JAXBContext.newInstance( MavenMetadata.class );
Unmarshaller unmarshaller = context.createUnmarshaller();
MavenMetadata metadata = ( MavenMetadata ) unmarshaller.unmarshal( input );
configLine.addMetadata( metadata );
getLog().debug( "Checking for site for artifact : " + configLine.getGroupId() + ":"
+ configLine.getArtifactId() + ":" + metadata.versioning.latest );
// revert sort versions (not handling alpha and
// complex vesion scheme but more usefull version are displayed left side
Collections.sort( metadata.versioning.versions, Collections.reverseOrder() );
getLog().debug( metadata.versioning.versions + " version(s) detected " + repoBaseUrl );
// central
checkSite( configLine.getVersionnedPomFileURL(
repoBaseUrl, metadata.versioning.latest ), configLine, metadata.versioning.latest );
}
catch ( MalformedURLException ex )
{
throw new MojoExecutionException( ex.getMessage(), ex );
}
catch ( IOException | JAXBException ex )
{
throw new MojoExecutionException( ex.getMessage(), ex );
}
}
@Override
public void execute() throws MojoExecutionException
{
//resolve only to what we set
if ( screenShot )
{
// create driver once reduce time to complete mojo
driver = new FirefoxDriver();
}
super.execute();
if ( screenShot )
{
driver.close();
}
}
}