001    /*
002     * Copyright 1999,2004 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.apache.commons.feedparser.network;
018    
019    import java.net.URL;
020    import java.util.ArrayList;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    
024    import org.apache.log4j.Logger;
025    
026    /**
027     * Get a ResourceRequest for a given URL.  The request is handled based on the
028     * URL.
029     *
030     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
031     * @version $Id: ResourceRequestFactory.java 373622 2006-01-30 22:53:00Z mvdb $
032     */
033    public class ResourceRequestFactory {
034    
035        private static Logger log = Logger.getLogger( ResourceRequestFactory.class.getName() );
036    
037        /**
038         * Specified in java.security to indicate the caching policy for successful
039         * name lookups from the name service.. The value is specified as as integer
040         * to indicate the number of seconds to cache the successful lookup.
041         * 
042         *
043         * sun.net.inetaddr.ttl:
044         * 
045         * This is a sun private system property which corresponds to
046         * networkaddress.cache.ttl. It takes the same value and has the same meaning,
047         * but can be set as a command-line option. However, the preferred way is to
048         * use the security property mentioned above.
049         * 
050         * A value of -1 indicates "cache forever".
051         */
052        public static int NETWORKADDRESS_CACHE_TTL = 5 * 60;
053        
054        /**
055         * These properties specify the default connect and read timeout (resp.) for
056         * the protocol handler used by java.net.URLConnection.
057         * 
058         * sun.net.client.defaultConnectTimeout specifies the timeout (in
059         * milliseconds) to establish the connection to the host. For example for
060         * http connections it is the timeout when establishing the connection to
061         * the http server. For ftp connection it is the timeout when establishing
062         * the connection to ftp servers.
063         * 
064         * sun.net.client.defaultReadTimeout specifies the timeout (in milliseconds)
065         * when reading from input stream when a connection is established to a
066         * resource.
067         */
068        public static int DEFAULT_CONNECT_TIMEOUT = 1 * 60 * 1000;
069    
070        public static int DEFAULT_READ_TIMEOUT = DEFAULT_CONNECT_TIMEOUT;
071    
072        /**
073         * Specify the maximum number of redirects to use.
074         */
075        public static int DEFAULT_MAX_REDIRECTS = 5;
076    
077        //FIXME: (should this be a linked list?)
078        private static ArrayList listeners = new ArrayList( 30 );
079    
080        private static HashMap schemeMap = null;
081    
082        private static boolean transparentHTCacheEnabled = false;
083    
084        /**
085         * When offline we either throw an exception or return content from the
086         * cache directly.  This can be used to run code that does not depend on the
087         * network.
088         */
089        private static boolean offline = false;
090    
091        public static ResourceRequest getResourceRequest( String resource,
092                                                          long ifModifiedSince ) throws NetworkException {
093    
094            return getResourceRequest( resource, ifModifiedSince, null );
095            
096        }
097    
098        /**
099         * Get a ResourceRequest for the protocol represented in the resource URL.
100         * It is important that we use a ResourceRequest implementation that supports
101         * fetching the URL.
102         *
103         * 
104         */
105        public static ResourceRequest getResourceRequest( String resource,
106                                                          long ifModifiedSince,
107                                                          String etag ) throws NetworkException {
108    
109            //log.debug( resource );
110            
111            //make sure we are initialized correctly.
112            ResourceRequestFactory.init();
113    
114            //make sure we have an index..
115    
116            int schemeIndex = resource.indexOf( ":" );
117    
118            if ( schemeIndex == -1 )
119                throw new NetworkException( "Unknown scheme: '" + resource + "'" );
120    
121            String scheme = resource.substring( 0, schemeIndex );
122    
123            if ( scheme == null || scheme.equals( "" ) )
124                throw new MalformedResourceException( "Not supported: " + resource );
125    
126            Class clazz = (Class)schemeMap.get( scheme );
127    
128            if ( clazz == null ) {
129                throw new MalformedResourceException( "Scheme not supported: " + scheme );
130            } 
131    
132            try { 
133                
134                ResourceRequest request = (ResourceRequest)clazz.newInstance();
135    
136                request.setResource( resource );
137    
138                //setup resource request options.
139                request.setIfModifiedSince( ifModifiedSince );
140    
141                //set the etag... when its null nothing will happen
142                request.setEtag( etag );
143                
144                request.init();
145    
146                return request;
147    
148            } catch ( Throwable t ) {
149                throw new NetworkException( t );
150            }
151    
152        }
153    
154        /**
155         * 
156         * @see #getResourceRequest( String )
157         * 
158         */
159        public static ResourceRequest getResourceRequest( String resource ) throws NetworkException {
160            return getResourceRequest( resource, -1 );
161        }
162        
163        /**
164         * 
165         * @see #getResourceRequest( String )
166         * 
167         */
168        public static ResourceRequest getResourceRequest( URL resource ) throws NetworkException {
169            return getResourceRequest( resource.toString() );
170        }
171    
172        /**
173         * Add an event listener to this instance of the factory.  This provides a
174         * mechanism to give default listeners to each new ResourceRequest.
175         *
176         * 
177         */
178        public static void addEventListener( NetworkEventListener listener ) {
179    
180            listeners.add( listener );
181    
182        }
183    
184        /**
185         * Get all event listeners.
186         *
187         * 
188         */
189        public static Iterator getNetworkEventListeners() {
190    
191            return listeners.iterator();
192            
193        }
194        
195        /**
196         * Make sure the factory is initialized.  Called once per JVM instance.
197         *
198         * 
199         */
200        private synchronized static void init() {
201    
202            //set the authenticator to use
203    
204            //FIXME: remove this until we figure out how to do proxy authentication.
205            //java.net.Authenticator.setDefault ( new Authenticator() );
206    
207            // A full list of properties is available here:
208    
209            // http://java.sun.com/j2se/1.4.2/docs/guide/net/properties.html
210    
211            System.setProperty( "sun.net.inetaddr.ttl",
212                                Integer.toString( NETWORKADDRESS_CACHE_TTL ) );
213    
214            System.setProperty( "networkaddress.cache.ttl",
215                                Integer.toString( NETWORKADDRESS_CACHE_TTL ) );
216    
217            System.setProperty( "sun.net.client.defaultReadTimeout",
218                                Integer.toString( DEFAULT_READ_TIMEOUT ) );
219    
220            System.setProperty( "sun.net.client.defaultConnectTimeout",
221                                Integer.toString( DEFAULT_CONNECT_TIMEOUT ) );
222    
223            System.setProperty( "http.maxRedirects",
224                                Integer.toString( DEFAULT_MAX_REDIRECTS ) );
225    
226            if ( schemeMap == null ) {
227                
228                schemeMap = new HashMap();
229                
230                schemeMap.put( "file", URLResourceRequest.class );
231                schemeMap.put( "http", URLResourceRequest.class );
232                schemeMap.put( "https", URLResourceRequest.class );
233                schemeMap.put( BlockingResourceRequest.SCHEME, BlockingResourceRequest.class );
234                
235            }
236    
237        }
238    
239        /**
240         * Return true if we support fetching content with the given scheme.
241         * Examples would be "http" and "file"
242         *
243         * 
244         */
245        public static boolean isSupportedScheme( String scheme ) {
246    
247            return schemeMap.get( scheme ) != null;
248            
249        }
250        
251        /**
252         * When the transparent HTCache is enabled we will keep content local
253         * similar to the Mozilla cache and return the cached copy and use
254         * if-Modified-Since when necessary.
255         *
256         * 
257         */
258        public static void setTransparentHTCacheEnabled( boolean enabled ) {
259            transparentHTCacheEnabled = enabled;
260        }
261    
262        /**
263         * Return true if we can enable the htcache.
264         *
265         * 
266         */
267        public static boolean isTransparentHTCacheEnabled() {
268            return transparentHTCacheEnabled;
269        }
270    
271        /**
272         * Enable/disable offline operation.
273         *
274         * 
275         */
276        public static void setOffline( boolean offline ) {
277            ResourceRequestFactory.offline = offline;
278        }
279    
280        /**
281         * 
282         *
283         * 
284         */
285        public static boolean isOffline() {
286            return offline;
287        }
288    
289    }