View Javadoc

1   /*
2    * Copyright 2000-2001,2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.jetspeed.services.urlmanager;
18  
19  // Java classes
20  import java.util.HashMap;
21  import java.util.Hashtable;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Vector;
26  import java.io.BufferedWriter;
27  import java.io.File;
28  import java.io.FileWriter;
29  import java.io.PrintWriter;
30  
31  import javax.servlet.ServletConfig;
32  import javax.servlet.ServletContext;
33  
34  // Turbine classes
35  import org.apache.turbine.services.TurbineBaseService;
36  
37  // Velocity classes
38  import org.apache.velocity.runtime.configuration.Configuration;
39  
40  // Jetspeed classes
41  import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
42  import org.apache.jetspeed.services.logging.JetspeedLogger;
43  import org.apache.jetspeed.services.resources.JetspeedResources;
44  
45  /***
46   * <p>This implementation of the URLManagerService is backed by a simple
47   * map persisted on disk in a properties file</p>
48   * Added: Support for proxies. <br>
49   * Example: (Set in <code>JetspeedResources.properties</code>)<br>
50   * <code>services.URLManager.proxy.http.host=myproxy.mydomain</code><br>
51   * <code>services.URLManager.proxy.http.port=81</code><br>
52   *
53   * @see URLManagerService
54   * @author <a href="mailto:raphael@apache.org">Raphaël Luta</a>
55   * @author <a href="mailto:sgala@hisitech.com">Santiago Gala</a>
56   * @version $Id: JetspeedURLManagerService.java,v 1.16 2004/02/23 03:30:47 jford Exp $
57   */
58  public class JetspeedURLManagerService extends TurbineBaseService implements URLManagerService 
59  {
60      /***
61       * Static initialization of the logger for this class
62       */    
63      private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(JetspeedURLManagerService.class.getName());
64      
65      /***
66      Map used to store all URL Information.
67      */
68      private Map urls = new HashMap();
69  
70      /***
71      Path to the properties file used for persisting the data
72      */
73      private String path = null;
74  
75      /***
76      Hashtable to store proxy configuration in
77      */
78      private Hashtable proxies = new Hashtable();
79  
80      /***
81       * Late init. Don't return control until early init says we're done.
82       */
83      public void init( )
84      {
85          while( !getInit() ) {
86              try {
87                  Thread.sleep(500);
88              } catch (InterruptedException ie ) {
89                  logger.info("URLManager service: Waiting for init()..." );
90              }
91          }
92  
93      }
94  
95  
96  
97      /***
98       * Called during Turbine.init()
99       *
100      * @param config A ServletConfig.
101      */
102     public synchronized void init( ServletConfig config )
103     {
104         //We have already been initialized...
105         if( getInit() ) return;
106 
107         try
108         {
109             logger.info ( "JetspeedURLManagerService early init()....starting!");
110 
111         // Proxy Settings are stored as 'services.URLManager.proxy.<protocol>.port' and as
112         // 'services.URLManager.proxy.<protocol>.port' in JetspeedResource.properties.
113         // Get a list of settings and store them in the hashtable
114             String prefix = "services." + URLManagerService.SERVICE_NAME + ".proxy.";
115             Iterator resourceKeys = JetspeedResources.getKeys( prefix );
116 
117             String key, hashKey;
118             Object hashValue = null;
119             while( resourceKeys.hasNext() )
120             {
121                 key = (String)resourceKeys.next();
122                 hashKey = key.substring(prefix.length()).toLowerCase();
123                 if ( hashKey.endsWith(".host") )
124                 {
125                     hashValue = JetspeedResources.getString(key);
126                     proxies.put( hashKey, hashValue );
127                 }
128                 else if ( hashKey.endsWith(".port") )
129                 {
130                     hashValue = new Integer(JetspeedResources.getInt(key));
131                     proxies.put( hashKey, hashValue );
132                 }
133             }
134 
135             path = JetspeedResources.getString( "services."+URLManagerService.SERVICE_NAME+".url" );
136 
137             if ( path == null) 
138             {
139                 String tempdir = new String("WEB-INF/conf/datasources.properties");
140                 String ps = System.getProperty("file.separator");
141 
142                 try
143                 {
144                     ServletContext sc = config.getServletContext();
145                     tempdir = sc.getAttribute("javax.servlet.context.tempdir").toString()
146                                            + ps + "jetspeed" 
147                                            + ps + "conf"
148                                            + ps + "datasources.properties";
149                     logger.debug("URLMangler: will create file in servlet temp directory " + tempdir);
150                 }
151                 catch (Exception e)
152                 {
153                     logger.debug("URLMangler: problems creating file in servlet temp directory "
154                            + " falling back to WEB-INF/conf : " + e);
155                 }     
156                 path = tempdir;
157             }
158             else
159             {
160                 logger.debug("URLMangler: will create file in user configured " + path);
161                 path = config.getServletContext().getRealPath(path);
162                 // should test for writability here and fallback to servlet tmp directory on failure
163             }
164 
165             load();
166             logger.info ( "JetspeedURLManagerService early init()....finished!");
167         }
168         catch (Throwable t)
169         {
170             logger.error ( "Cannot initialize JetspeedURLManagerService!", t );
171         }
172         setInit(true);
173 
174     }
175 
176     /***
177      * Called during Turbine destroy(). Persist the Manager state
178      * to disk
179      */
180     public void shutdown() {
181         save();
182     }
183 
184     /***
185      * Registers a new URL record. If the url is already registered in 
186      * the system, doesn't modify the current record.
187      * 
188      * @param url the url to register
189      */
190     public void register( String url ) {
191         if ( url != null ) {
192             URLInfo info = getInfo( url );
193             if ( info == null ) {
194                 register( new URLInfo( url, URLManagerService.STATUS_OK ) );
195             }
196         }
197     }
198 
199     /***
200      * Registers a new URL record. If the url is already registered in 
201      * the system, updates the status of this URL info record
202      * 
203      * @param url the url to register
204      * @param status the status of this url
205      */
206     public void register( String url, int status ) {
207         if ( url != null ) {
208             URLInfo info = getInfo( url );
209             if ( info == null ) {
210                 register( new URLInfo( url, status ) );
211             } else {
212                 info.setStatus( status );
213             }
214         }
215     }
216 
217     /***
218      * Registers a new URL record. If the url is already registered in 
219      * the system, updates both the status and the message of this URL 
220      * info record
221      * 
222      * @param url the url to register
223      * @param status the status of this url
224      * @param message a descriptive message of the status
225      */
226     public void register( String url, int status, String message ) {
227         if ( url != null ) {
228             URLInfo info = getInfo( url );
229             if ( info == null ) {
230                 register( new URLInfo( url, status, message ) );
231             } else {
232                 info.setStatus( status );
233                 info.setMessage( message );
234             }
235         }
236     }
237 
238     /***
239      * Register or replace an URL record. All records are keyed to
240      * the imutable URL of URLInfo.
241      * 
242      * @param info the info record to store
243      */
244     public void register( URLInfo info ) {
245         if ( info != null) {
246             synchronized (urls) {
247                 if( getInfo( info.getURL() ) == null ) 
248                     urls.put( info.getURL().intern(), info );
249             }
250         }
251     }
252 
253     /***
254      * Unregister an URL from the repository
255      * 
256      * @param url the url to remove
257      */
258     public void unregister( String url ) {
259         if ( url != null ) {
260             synchronized (urls) {
261                 urls.remove( url.intern() );
262             }
263         }
264     }
265 
266     /***
267      * Get the information record stored in the database about
268      * an URL.
269      * 
270      * @param url the url whose record is sought
271      * @return the description record found in the repository or null.
272      */
273     public URLInfo getInfo( String url ) {
274         URLInfo info = null;
275 
276         if ( url != null ) {
277             synchronized(urls) {
278                 info = (URLInfo)urls.get( url.intern() );
279             }
280         }
281 
282         return info;
283     }
284 
285     /***
286      * Test whether the URL is currently believed to be OK by this 
287      * repository.
288      * 
289      * @param url the url to be tested
290      * @return false is the url is known by this repository and has
291      * a status indicating an error, true otherwise.
292      */
293     public boolean isOK( String url ) {
294         URLInfo info = getInfo( url );
295 
296         // we don't know this URL, play it safe and say it's good
297         if ( info == null ) return true;
298 
299         return ( info.getStatus() == URLManagerService.STATUS_OK );
300     }
301 
302     /***
303      * List of the current known URLs in the repository
304      *
305      * @return a List of URL strings known to this repository
306      */
307     public List list() {
308         synchronized (urls) {
309             return new Vector( urls.keySet() );
310         }
311     }
312 
313     /***
314      * List of the current known URLs in the repository which have 
315      * the given status.
316      *
317      * @param status the status to be retrieved. May be 
318      * {@link URLManagerService#STATUS_ANY} to indicate any status
319      * @return a List of URL strings known to this repository with this status
320      */
321     public List list( int status ) {
322         Vector result = new Vector();
323 
324         synchronized (urls) {
325             Iterator i = urls.entrySet().iterator();
326             while( i.hasNext() ) {
327                 Map.Entry entry = (Map.Entry)i.next();
328                 URLInfo info = (URLInfo)entry.getValue();
329                 if ( ( info.getStatus() & status ) != 0 ) {
330                     result.addElement( entry.getKey() );
331                 }
332             }
333         }
334 
335         return result;
336     }
337 
338     /***
339      * Load the persisted state of the repository from disk
340      */
341     private synchronized void load() {
342 
343         Map store = new HashMap();
344         Configuration config = null;
345 
346         logger.info( "Restoring the URLs from disk: " + path );
347 
348         try {
349             config = new Configuration( path );
350 
351             int count = 1;
352             String url = null;
353 
354             while ( ( url = ( config
355                               .getString("entry."+count+".url") ) ) != null ) {
356                 //Intern the url to ensure we can use "==" to compare
357                 //and synchronize on it
358                 url = url.intern();
359                 int status = config.getInteger("entry."+count+".status", URLManagerService.STATUS_OK );
360                 if( store.get( url ) == null )
361                     store.put( url, new URLInfo( url, status ) );
362                 count++;
363             }
364 
365             logger.info( "URLManager loaded " + count + " urls" );                    
366 
367         } catch ( Exception e ) {
368             logger.error( "Could not restore URLManager state", e );
369             return;
370         } finally {
371             // set the loaded store as the new store
372             this.urls = store;
373         }
374 
375     }
376 
377     /***
378      * Persist the state of the repository on disk in a properties file
379      */
380     private synchronized void save() {
381         PrintWriter pw = null ;
382 
383         try {
384 
385             File propfile = new File(path); // FileWriter doesn't always do this
386             propfile.getParentFile().mkdirs();
387             propfile.createNewFile();
388 
389             pw = new PrintWriter( new BufferedWriter( new FileWriter( propfile ) ) );
390             synchronized (urls) {
391                 Iterator i = urls.values().iterator();
392                 int entryNum = 1;
393                 while( i.hasNext() ) {
394                     URLInfo info = (URLInfo)i.next();
395                     pw.print( "entry." );
396                     pw.print( entryNum );
397                     pw.print( ".url=" );
398                     writeEscaped( pw, info.getURL() );
399                     pw.println( "" );
400                     pw.print( "entry." );
401                     pw.print( entryNum );
402                     pw.print( ".status=" );
403                     pw.print( info.getStatus() );
404                     pw.println( "" );
405                     entryNum++;
406                 }
407             }
408         } 
409         catch ( Throwable t ) 
410         {
411             logger.error( "Impossible to save URLManager state to "+path, t );
412         } 
413         finally 
414         {
415             if( pw != null )
416             {
417                 pw.close();
418             }
419         }
420     }
421 
422     /***
423      * Return the port of a proxy
424      * @param protocol The protocol that the proxy supports, e.g. 'http'
425      * @return The port number (1-65535), or -1 if no port was specified (= use default)
426      */
427     public int getProxyPort( String protocol ) {
428         Integer proxyPort = (Integer)proxies.get( (protocol + ".port").toLowerCase() );
429 
430         if (proxyPort != null)
431             return proxyPort.intValue();
432         else
433             return -1;
434     }
435 
436     /***
437      * Return a proxy's hostname
438      * @param protocol The protocol that the proxy supports, e.g. 'http'
439      * @return The hostname of the proxy, or <code>null</code> if no proxy is specified for this protocol
440      */
441     public String getProxyHost( String protocol ) {
442         String proxyHost = (String)proxies.get( (protocol + ".host").toLowerCase() );
443 
444         return proxyHost;
445     }
446 
447     /***
448      * <p>Escape values when saving.
449      * Appends a String to a StringBuffer, escaping commas.</p>
450      * <p>We assume that commas are unescaped.</p>
451      * @param sink a StringBuffer to write output
452      * @param element a value to be written
453      */
454     protected void writeEscaped( PrintWriter sink, String element ) {
455         int upTo = element.indexOf(",");
456         if( upTo == -1 ) {
457             sink.print( element );
458             return;
459         }
460         sink.print( element.substring( 0, upTo ) );
461         sink.print( "//," );
462         writeEscaped( sink, element.substring( upTo+1, element.length() ) );
463         return;
464     }
465 }