View Javadoc
1   package org.apache.maven.doxia.macro.snippet;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.doxia.macro.AbstractMacro;
23  import org.apache.maven.doxia.macro.Macro;
24  import org.apache.maven.doxia.macro.MacroExecutionException;
25  import org.apache.maven.doxia.macro.MacroRequest;
26  import org.apache.maven.doxia.sink.Sink;
27  import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
28  import org.codehaus.plexus.component.annotations.Component;
29  import org.codehaus.plexus.util.StringUtils;
30  
31  import java.io.File;
32  import java.io.IOException;
33  import java.net.MalformedURLException;
34  import java.net.URL;
35  import java.util.HashMap;
36  import java.util.Map;
37  
38  /**
39   * A macro that prints out the content of a file or a URL.
40   */
41  @Component( role = Macro.class, hint = "snippet" )
42  public class SnippetMacro
43      extends AbstractMacro
44  {
45      /**
46       * Holds the cache.
47       */
48      private static Map<String, String> cache = new HashMap<>();
49  
50      private static final int HOUR = 60;
51  
52      /**
53       * One hour default cache.
54       */
55      private long timeout = HOUR * HOUR * 1000;
56  
57      /**
58       * Holds the time cache.
59       */
60      private static Map<String, Long> timeCached = new HashMap<>();
61  
62      /**
63       * Debug.
64       */
65      private boolean debug = false;
66  
67      /**
68       * in case of Exception during snippet download error will ignored and empty content returned.
69       */
70      private boolean ignoreDownloadError = true;
71  
72      /** {@inheritDoc} */
73      public void execute( Sink sink, MacroRequest request )
74          throws MacroExecutionException
75      {
76          String id = (String) request.getParameter( "id" );
77  
78          String urlParam = (String) request.getParameter( "url" );
79  
80          String fileParam = (String) request.getParameter( "file" );
81  
82          String debugParam = (String) request.getParameter( "debug" );
83  
84          if ( debugParam != null )
85          {
86              this.debug = Boolean.parseBoolean( debugParam );
87          }
88  
89          String ignoreDownloadErrorParam = (String) request.getParameter( "ignoreDownloadError" );
90  
91          if ( ignoreDownloadErrorParam != null )
92          {
93              this.ignoreDownloadError = Boolean.parseBoolean( ignoreDownloadErrorParam );
94          }
95  
96          boolean verbatim = true;
97  
98          String verbatimParam = (String) request.getParameter( "verbatim" );
99  
100         if ( verbatimParam != null && !"".equals( verbatimParam ) )
101         {
102             verbatim = Boolean.valueOf( verbatimParam );
103         }
104 
105         String encoding = (String) request.getParameter( "encoding" );
106 
107         URL url;
108 
109         if ( !StringUtils.isEmpty( urlParam ) )
110         {
111             try
112             {
113                 url = new URL( urlParam );
114             }
115             catch ( MalformedURLException e )
116             {
117                 throw new IllegalArgumentException( urlParam + " is a malformed URL" );
118             }
119         }
120         else if ( !StringUtils.isEmpty( fileParam ) )
121         {
122             File f = new File( fileParam );
123 
124             if ( !f.isAbsolute() )
125             {
126                 f = new File( request.getBasedir(), fileParam );
127             }
128 
129             try
130             {
131                 url = f.toURI().toURL();
132             }
133             catch ( MalformedURLException e )
134             {
135                 throw new IllegalArgumentException( fileParam + " is a malformed URL" );
136             }
137         }
138         else
139         {
140             throw new IllegalArgumentException( "Either the 'url' or the 'file' param has to be given." );
141         }
142 
143         StringBuffer snippet;
144 
145         try
146         {
147             snippet = getSnippet( url, encoding, id );
148         }
149         catch ( IOException e )
150         {
151             throw new MacroExecutionException( "Error reading snippet", e );
152         }
153 
154         if ( verbatim )
155         {
156             sink.verbatim( SinkEventAttributeSet.BOXED );
157 
158             sink.text( snippet.toString() );
159 
160             sink.verbatim_();
161         }
162         else
163         {
164             sink.rawText( snippet.toString() );
165         }
166     }
167 
168     /**
169      * Return a snippet of the given url.
170      *
171      * @param url The URL to parse.
172      * @param encoding The encoding of the URL to parse.
173      * @param id  The id of the snippet.
174      * @return The snippet.
175      * @throws IOException if something goes wrong.
176      */
177     private StringBuffer getSnippet( URL url, String encoding, String id )
178         throws IOException
179     {
180         StringBuffer result;
181 
182         String cachedSnippet = getCachedSnippet( url, id );
183 
184         if ( cachedSnippet != null )
185         {
186             result = new StringBuffer( cachedSnippet );
187 
188             if ( debug )
189             {
190                 result.append( "(Served from cache)" );
191             }
192         }
193         else
194         {
195             try
196             {
197                 result = new SnippetReader( url, encoding ).readSnippet( id );
198                 cacheSnippet( url, id, result.toString() );
199                 if ( debug )
200                 {
201                     result.append( "(Fetched from url, cache content " ).append( cache ).append( ")" );
202                 }
203             }
204             catch ( IOException e )
205             {
206                 if ( ignoreDownloadError )
207                 {
208                     getLog().debug( "IOException which reading " + url + ": " + e );
209                     result =
210                         new StringBuffer( "Error during retrieving content skip as ignoreDownloadError activated." );
211                 }
212                 else
213                 {
214                     throw e;
215                 }
216             }
217         }
218         return result;
219     }
220 
221     /**
222      * Return a snippet from the cache.
223      *
224      * @param url The URL to parse.
225      * @param id  The id of the snippet.
226      * @return The snippet.
227      */
228     private String getCachedSnippet( URL url, String id )
229     {
230         if ( isCacheTimedout( url, id ) )
231         {
232             removeFromCache( url, id );
233         }
234         return cache.get( globalSnippetId( url, id ) );
235     }
236 
237     /**
238      * Return true if the snippet has been cached longer than
239      * the current timeout.
240      *
241      * @param url The URL to parse.
242      * @param id  The id of the snippet.
243      * @return True if timeout exceeded.
244      */
245     boolean isCacheTimedout( URL url, String id )
246     {
247         return timeInCache( url, id ) >= timeout;
248     }
249 
250     /**
251      * Return the time the snippet has been cached.
252      *
253      * @param url The URL to parse.
254      * @param id  The id of the snippet.
255      * @return The cache time.
256      */
257     long timeInCache( URL url, String id )
258     {
259         return System.currentTimeMillis() - getTimeCached( url, id );
260     }
261 
262     /**
263      * Return the absolute value of when the snippet has been cached.
264      *
265      * @param url The URL to parse.
266      * @param id  The id of the snippet.
267      * @return The cache time.
268      */
269     long getTimeCached( URL url, String id )
270     {
271         String globalId = globalSnippetId( url, id );
272 
273         return timeCached.containsKey( globalId ) ? timeCached.get( globalId ) : 0;
274     }
275 
276     /**
277      * Removes the snippet from the cache.
278      *
279      * @param url The URL to parse.
280      * @param id  The id of the snippet.
281      */
282     private void removeFromCache( URL url, String id )
283     {
284         String globalId = globalSnippetId( url, id );
285 
286         timeCached.remove( globalId );
287 
288         cache.remove( globalId );
289     }
290 
291     /**
292      * Return a global identifier for the snippet.
293      *
294      * @param url The URL to parse.
295      * @param id  The id of the snippet.
296      * @return An identifier, concatenated url and id,
297      *         or just url.toString() if id is empty or null.
298      */
299     private String globalSnippetId( URL url, String id )
300     {
301         if ( StringUtils.isEmpty( id ) )
302         {
303             return url.toString();
304         }
305 
306         return url + " " + id;
307     }
308 
309     /**
310      * Puts the given snippet into the cache.
311      *
312      * @param url     The URL to parse.
313      * @param id      The id of the snippet.
314      * @param content The content of the snippet.
315      */
316     public void cacheSnippet( URL url, String id, String content )
317     {
318         cache.put( globalSnippetId( url, id ), content );
319 
320         timeCached.put( globalSnippetId( url, id ), System.currentTimeMillis() );
321     }
322 
323     /**
324      * Set the cache timeout.
325      *
326      * @param time The timeout to set.
327      */
328     public void setCacheTimeout( int time )
329     {
330         this.timeout = time;
331     }
332 }