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.MacroExecutionException;
24  import org.apache.maven.doxia.macro.MacroRequest;
25  import org.apache.maven.doxia.sink.Sink;
26  import org.codehaus.plexus.util.StringUtils;
27  
28  import java.io.IOException;
29  import java.io.File;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.util.HashMap;
33  import java.util.Map;
34  import org.apache.maven.doxia.sink.SinkEventAttributeSet;
35  
36  /**
37   * A macro that prints out the content of a file or a URL.
38   *
39   * @plexus.component role-hint="snippet"
40   * @version $Id: SnippetMacro.java 1090706 2011-04-09 23:15:28Z hboutemy $
41   */
42  public class SnippetMacro
43      extends AbstractMacro
44  {
45      /** Holds the cache. */
46      private static Map<String, String> cache = new HashMap<String, String>();
47  
48      private static final int HOUR = 60;
49  
50      /** One hour default cache. */
51      private long timeout = HOUR * HOUR * 1000;
52  
53      /** Holds the time cache. */
54      private static Map<String, Long> timeCached = new HashMap<String, Long>();
55  
56      /** Debug. */
57      private boolean debug = false;
58  
59      /** {@inheritDoc} */
60      public void execute( Sink sink, MacroRequest request )
61          throws MacroExecutionException
62      {
63          String id = (String) request.getParameter( "id" );
64  
65          String urlParam = (String) request.getParameter( "url" );
66  
67          String fileParam = (String) request.getParameter( "file" );
68  
69          boolean verbatim = true;
70  
71          String verbatimParam = (String) request.getParameter( "verbatim" );
72  
73          if ( verbatimParam != null && !"".equals( verbatimParam ) )
74          {
75              verbatim = Boolean.valueOf( verbatimParam ).booleanValue();
76          }
77  
78          URL url;
79  
80          if ( !StringUtils.isEmpty( urlParam ) )
81          {
82              try
83              {
84                  url = new URL( urlParam );
85              }
86              catch ( MalformedURLException e )
87              {
88                  throw new IllegalArgumentException( urlParam + " is a malformed URL" );
89              }
90          }
91          else if ( !StringUtils.isEmpty( fileParam ) )
92          {
93              File f = new File( fileParam );
94  
95              if ( !f.isAbsolute() )
96              {
97                  f = new File( request.getBasedir(), fileParam );
98              }
99  
100             try
101             {
102                 url = f.toURI().toURL();
103             }
104             catch ( MalformedURLException e )
105             {
106                 throw new IllegalArgumentException( fileParam + " is a malformed URL" );
107             }
108         }
109         else
110         {
111             throw new IllegalArgumentException( "Either the 'url' or the 'file' param has to be given." );
112         }
113 
114         StringBuffer snippet;
115 
116         try
117         {
118             snippet = getSnippet( url, id );
119         }
120         catch ( IOException e )
121         {
122             throw new MacroExecutionException( "Error reading snippet", e );
123         }
124 
125         if ( verbatim )
126         {
127             sink.verbatim( SinkEventAttributeSet.BOXED );
128 
129             sink.text( snippet.toString() );
130 
131             sink.verbatim_();
132         }
133         else
134         {
135             sink.rawText( snippet.toString() );
136         }
137     }
138 
139     /**
140      * Return a snippet of the given url.
141      *
142      * @param url The URL to parse.
143      * @param id The id of the snippet.
144      * @return The snippet.
145      * @throws IOException if something goes wrong.
146      */
147     private StringBuffer getSnippet( URL url, String id )
148         throws IOException
149     {
150         StringBuffer result;
151 
152         String cachedSnippet = getCachedSnippet( url, id );
153 
154         if ( cachedSnippet != null )
155         {
156             result = new StringBuffer( cachedSnippet );
157 
158             if ( debug )
159             {
160                 result.append( "(Served from cache)" );
161             }
162         }
163         else
164         {
165             result = new SnippetReader( url ).readSnippet( id );
166 
167             cacheSnippet( url, id, result.toString() );
168 
169             if ( debug )
170             {
171                 result.append( "(Fetched from url, cache content " ).append( cache ).append( ")" );
172             }
173         }
174 
175         return result;
176     }
177 
178     /**
179      * Return a snippet from the cache.
180      *
181      * @param url The URL to parse.
182      * @param id The id of the snippet.
183      * @return The snippet.
184      */
185     private String getCachedSnippet( URL url, String id )
186     {
187         if ( isCacheTimedout( url, id ) )
188         {
189             removeFromCache( url, id );
190         }
191         return cache.get( globalSnippetId( url, id ) );
192     }
193 
194     /**
195      * Return true if the snippet has been cached longer than
196      * the current timeout.
197      *
198      * @param url The URL to parse.
199      * @param id The id of the snippet.
200      * @return True if timeout exceeded.
201      */
202     boolean isCacheTimedout( URL url, String id )
203     {
204         return timeInCache( url, id ) >= timeout;
205     }
206 
207     /**
208      * Return the time the snippet has been cached.
209      *
210      * @param url The URL to parse.
211      * @param id The id of the snippet.
212      * @return The cache time.
213      */
214     long timeInCache( URL url, String id )
215     {
216         return System.currentTimeMillis() - getTimeCached( url, id );
217     }
218 
219     /**
220      * Return the absolute value of when the snippet has been cached.
221      *
222      * @param url The URL to parse.
223      * @param id The id of the snippet.
224      * @return The cache time.
225      */
226     long getTimeCached( URL url, String id )
227     {
228         String globalId = globalSnippetId( url, id );
229 
230         return timeCached.containsKey( globalId ) ? timeCached.get( globalId ).longValue() : 0;
231     }
232 
233     /**
234      * Removes the snippet from the cache.
235      *
236      * @param url The URL to parse.
237      * @param id The id of the snippet.
238      */
239     private void removeFromCache( URL url, String id )
240     {
241         String globalId = globalSnippetId( url, id );
242 
243         timeCached.remove( globalId );
244 
245         cache.remove( globalId );
246     }
247 
248     /**
249      * Return a global identifier for the snippet.
250      *
251      * @param url The URL to parse.
252      * @param id The id of the snippet.
253      * @return An identifier, concatenated url and id,
254      *  or just url.toString() if id is empty or null.
255      */
256     private String globalSnippetId( URL url, String id )
257     {
258         if ( StringUtils.isEmpty( id ) )
259         {
260             return url.toString();
261         }
262 
263         return url + " " + id;
264     }
265 
266     /**
267      * Puts the given snippet into the cache.
268      *
269      * @param url The URL to parse.
270      * @param id The id of the snippet.
271      * @param content The content of the snippet.
272      */
273     public void cacheSnippet( URL url, String id, String content )
274     {
275         cache.put( globalSnippetId( url, id ), content );
276 
277         timeCached.put( globalSnippetId( url, id ), new Long( System.currentTimeMillis() ) );
278     }
279 
280     /**
281      * Set the cache timeout.
282      *
283      * @param time The timeout to set.
284      */
285     public void setCacheTimeout( int time )
286     {
287         this.timeout = time;
288     }
289 }