blob: ae85ca81ff555c2c72290ff4e779d135c73cce67 [file] [log] [blame]
package org.apache.maven.doxia.macro.snippet;
/*
* 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 org.apache.maven.doxia.macro.AbstractMacro;
import org.apache.maven.doxia.macro.Macro;
import org.apache.maven.doxia.macro.MacroExecutionException;
import org.apache.maven.doxia.macro.MacroRequest;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
/**
* A macro that prints out the content of a file or a URL.
*
* @version $Id$
*/
@Component( role = Macro.class, hint = "snippet" )
public class SnippetMacro
extends AbstractMacro
{
/**
* Holds the cache.
*/
private static Map<String, String> cache = new HashMap<String, String>();
private static final int HOUR = 60;
/**
* One hour default cache.
*/
private long timeout = HOUR * HOUR * 1000;
/**
* Holds the time cache.
*/
private static Map<String, Long> timeCached = new HashMap<String, Long>();
/**
* Debug.
*/
private boolean debug = false;
/**
* in case of Exception during snippet download error will ignored and empty content returned.
*/
private boolean ignoreDownloadError = true;
/**
* {@inheritDoc}
*/
public void execute( Sink sink, MacroRequest request )
throws MacroExecutionException
{
String id = (String) request.getParameter( "id" );
String urlParam = (String) request.getParameter( "url" );
String fileParam = (String) request.getParameter( "file" );
String debugParam = (String) request.getParameter( "debug" );
if ( debugParam != null )
{
this.debug = Boolean.parseBoolean( debugParam );
}
String ignoreDownloadErrorParam = (String) request.getParameter( "ignoreDownloadError" );
if ( ignoreDownloadErrorParam != null )
{
this.ignoreDownloadError = Boolean.parseBoolean( ignoreDownloadErrorParam );
}
boolean verbatim = true;
String verbatimParam = (String) request.getParameter( "verbatim" );
if ( verbatimParam != null && !"".equals( verbatimParam ) )
{
verbatim = Boolean.valueOf( verbatimParam ).booleanValue();
}
String encoding = (String) request.getParameter( "encoding" );
URL url;
if ( !StringUtils.isEmpty( urlParam ) )
{
try
{
url = new URL( urlParam );
}
catch ( MalformedURLException e )
{
throw new IllegalArgumentException( urlParam + " is a malformed URL" );
}
}
else if ( !StringUtils.isEmpty( fileParam ) )
{
File f = new File( fileParam );
if ( !f.isAbsolute() )
{
f = new File( request.getBasedir(), fileParam );
}
try
{
url = f.toURI().toURL();
}
catch ( MalformedURLException e )
{
throw new IllegalArgumentException( fileParam + " is a malformed URL" );
}
}
else
{
throw new IllegalArgumentException( "Either the 'url' or the 'file' param has to be given." );
}
StringBuffer snippet;
try
{
snippet = getSnippet( url, encoding, id );
}
catch ( IOException e )
{
throw new MacroExecutionException( "Error reading snippet", e );
}
if ( verbatim )
{
sink.verbatim( SinkEventAttributeSet.BOXED );
sink.text( snippet.toString() );
sink.verbatim_();
}
else
{
sink.rawText( snippet.toString() );
}
}
/**
* Return a snippet of the given url.
*
* @param url The URL to parse.
* @param encoding The encoding of the URL to parse.
* @param id The id of the snippet.
* @return The snippet.
* @throws IOException if something goes wrong.
*/
private StringBuffer getSnippet( URL url, String encoding, String id )
throws IOException
{
StringBuffer result;
String cachedSnippet = getCachedSnippet( url, id );
if ( cachedSnippet != null )
{
result = new StringBuffer( cachedSnippet );
if ( debug )
{
result.append( "(Served from cache)" );
}
}
else
{
try
{
result = new SnippetReader( url, encoding ).readSnippet( id );
cacheSnippet( url, id, result.toString() );
if ( debug )
{
result.append( "(Fetched from url, cache content " ).append( cache ).append( ")" );
}
}
catch ( IOException e )
{
if ( ignoreDownloadError )
{
getLog().debug( "IOException which reading " + url + ": " + e );
result =
new StringBuffer( "Error during retrieving content skip as ignoreDownloadError activated." );
}
else
{
throw e;
}
}
}
return result;
}
/**
* Return a snippet from the cache.
*
* @param url The URL to parse.
* @param id The id of the snippet.
* @return The snippet.
*/
private String getCachedSnippet( URL url, String id )
{
if ( isCacheTimedout( url, id ) )
{
removeFromCache( url, id );
}
return cache.get( globalSnippetId( url, id ) );
}
/**
* Return true if the snippet has been cached longer than
* the current timeout.
*
* @param url The URL to parse.
* @param id The id of the snippet.
* @return True if timeout exceeded.
*/
boolean isCacheTimedout( URL url, String id )
{
return timeInCache( url, id ) >= timeout;
}
/**
* Return the time the snippet has been cached.
*
* @param url The URL to parse.
* @param id The id of the snippet.
* @return The cache time.
*/
long timeInCache( URL url, String id )
{
return System.currentTimeMillis() - getTimeCached( url, id );
}
/**
* Return the absolute value of when the snippet has been cached.
*
* @param url The URL to parse.
* @param id The id of the snippet.
* @return The cache time.
*/
long getTimeCached( URL url, String id )
{
String globalId = globalSnippetId( url, id );
return timeCached.containsKey( globalId ) ? timeCached.get( globalId ).longValue() : 0;
}
/**
* Removes the snippet from the cache.
*
* @param url The URL to parse.
* @param id The id of the snippet.
*/
private void removeFromCache( URL url, String id )
{
String globalId = globalSnippetId( url, id );
timeCached.remove( globalId );
cache.remove( globalId );
}
/**
* Return a global identifier for the snippet.
*
* @param url The URL to parse.
* @param id The id of the snippet.
* @return An identifier, concatenated url and id,
* or just url.toString() if id is empty or null.
*/
private String globalSnippetId( URL url, String id )
{
if ( StringUtils.isEmpty( id ) )
{
return url.toString();
}
return url + " " + id;
}
/**
* Puts the given snippet into the cache.
*
* @param url The URL to parse.
* @param id The id of the snippet.
* @param content The content of the snippet.
*/
public void cacheSnippet( URL url, String id, String content )
{
cache.put( globalSnippetId( url, id ), content );
timeCached.put( globalSnippetId( url, id ), Long.valueOf( System.currentTimeMillis() ) );
}
/**
* Set the cache timeout.
*
* @param time The timeout to set.
*/
public void setCacheTimeout( int time )
{
this.timeout = time;
}
}