001    /*
002     * Copyright 2004-2005 Stephen J. McConnell.
003     * Copyright 2004 Niclas Hedhman
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
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.transit.artifact;
020    
021    import java.io.IOException;
022    import java.io.PrintWriter;
023    
024    import java.net.URL;
025    import java.net.URLConnection;
026    import java.net.URLStreamHandler;
027    import java.security.AccessController;
028    import java.security.PrivilegedAction;
029    
030    import net.dpml.transit.Transit;
031    import net.dpml.transit.TransitRuntimeException;
032    import net.dpml.transit.SecuredTransitContext;
033    
034    /**
035     * The artifact URL protocol handler.
036     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
037     * @version 1.1.0
038     */
039    public class Handler extends URLStreamHandler
040    {
041        // ------------------------------------------------------------------------
042        // static
043        // ------------------------------------------------------------------------
044    
045       /**
046        * Default buffer size.
047        */
048        private static final int BUFFER_SIZE = 100;
049    
050        // ------------------------------------------------------------------------
051        // state
052        // ------------------------------------------------------------------------
053    
054       /**
055        * The transit context.
056        */
057        private SecuredTransitContext   m_context;
058    
059        // ------------------------------------------------------------------------
060        // constructor
061        // ------------------------------------------------------------------------
062    
063       /**
064        * Creation of a new transit artifact protocol handler.
065        */
066        public Handler()
067        {
068            try
069            {
070                Transit.getInstance();
071                m_context = SecuredTransitContext.getInstance();
072            }
073            catch( RuntimeException e )
074            {
075                e.printStackTrace();
076                throw e;
077            }
078        }
079    
080        // ------------------------------------------------------------------------
081        // implementation
082        // ------------------------------------------------------------------------
083    
084        /**
085         * Opens a connection to the specified URL.
086         *
087         * @param url A URL to open a connection to.
088         * @return The established connection.
089         * @throws IOException If a connection failure occurs.
090         */
091        protected URLConnection openConnection( final URL url )
092          throws IOException
093        {
094            return new ArtifactURLConnection( url, m_context );
095        }
096    
097       /**
098        * Return the external representation of the supplied url.
099        * @param u the url
100        * @return a string representing of the url as an artifact uri
101        */
102        protected String toExternalForm( URL u )
103        {
104            StringBuffer buf = new StringBuffer( BUFFER_SIZE );
105            buf.append( u.getProtocol() );
106            buf.append( ":" );
107            String path = getRealPath( u );
108            if( path != null )
109            {
110                int lastPos = path.length() - 1;
111                if( path.charAt( lastPos ) == '/' )
112                {
113                    buf.append( path.substring( 0, lastPos ) );
114                }
115                else
116                {
117                    buf.append( path );
118                }
119            }
120    
121            String internal = getInternalPath( u );
122            if( null != internal )
123            {
124                buf.append( internal );
125            }
126            
127            String query = u.getQuery();
128            if( query != null )
129            {
130                buf.append( '?' );
131                buf.append( query );
132            }
133    
134            String version = u.getUserInfo();
135            if( ( version != null ) && !"".equals( version ) )
136            {
137                buf.append( '#' );
138                buf.append( version );
139            }
140            String result = buf.toString();
141            buf.setLength( 0 );
142            return result;
143        }
144    
145       /**
146        * Return the pure artifact path without any internal address.
147        * @param url the url to evaluate
148        * @return the pure path
149        */
150        private String getRealPath( URL url )
151        {
152            String path = url.getPath();
153            if( null == path )
154            {
155                return null;
156            }
157            int index = path.indexOf( "!" );
158            if( index < 0 )
159            {
160                return path;
161            }
162            else
163            {
164                return path.substring( 0, index );
165            }
166        }
167    
168       /**
169        * Return the value of an internal address associated with a path.
170        * @param url the url to evaluate
171        * @return the internal address of null if the url does not contain an internal address
172        */
173        private String getInternalPath( URL url )
174        {
175            String path = url.getPath();
176            if( null == path )
177            {
178                return null;
179            }
180            int index = path.indexOf( "!" );
181            if( index < 0 )
182            {
183                return null;
184            }
185            else
186            {
187                return path.substring( index );
188            }
189        }
190    
191    
192       /**
193        * Parse the supplied specification.
194        * @param dest the destination url to populate
195        * @param spec the supplied spec
196        * @param start the starting position
197        * @param limit the limit
198        */
199        protected void parseURL( final URL dest, String spec, int start, int limit )
200        {
201            try
202            {
203                final String protocol = dest.getProtocol();
204                String specPath = spec.substring( start, limit );
205                String path = dest.getPath();
206                
207                // get any query info from the supplied spec
208                
209                final String query = getQueryFragment( specPath );
210                if( null != query )
211                {
212                    int n = specPath.indexOf( "?" );
213                    if( n > -1 )
214                    {
215                        specPath = specPath.substring( 0, n );
216                    }
217                }
218                
219                // get the version using the url ref semantics
220                
221                final String version = getRefFragment( specPath );
222                if( null != version )
223                {
224                    int n = specPath.indexOf( "#" );
225                    if( n > -1 )
226                    {
227                        specPath = specPath.substring( 0, n );
228                    }
229                }
230                
231                // setup the path 
232                
233                if( path == null )
234                {
235                    path = specPath;
236                    if( !path.endsWith( "/" ) && ( path.indexOf( "!" ) < 0 ) )
237                    {
238                        path = path + "/";
239                    }
240                }
241                else
242                {
243                    int lastPos = path.length() - 1;
244                    if( path.charAt( lastPos ) == '/' )
245                    {
246                        path = path.substring( 0, lastPos );
247                    }
248                    if( specPath.charAt( 0 ) == '/' )
249                    {
250                        path = path + "!" + specPath;
251                    }
252                    else
253                    {
254                        path = path + "!/" + specPath;
255                    }
256                }
257                
258                // establish the artifact type
259                
260                String type = dest.getUserInfo();
261                if( type == null )
262                {
263                    if( limit < spec.length() )
264                    {
265                        type = spec.substring( limit + 1 );
266                    }
267                }
268                
269                // map features to url fields
270                
271                final String user = type; // final String user = version;
272                final String authority = null;
273                final int port = -1;
274                final String host = null;
275                final String ref = version; // final String ref = null;
276                final String finalPath = path;
277                AccessController.doPrivileged( 
278                  new PrivilegedAction()
279                  {
280                    public Object run()
281                    {
282                        setURL( dest, protocol, host, port, authority, user, finalPath, query, ref );
283                        return null;
284                    }
285                  }
286                );
287            }
288            catch( Throwable e )
289            {
290                try
291                {
292                    PrintWriter log = Transit.getInstance().getLogWriter();
293                    String message = "Unable to parse the URL: "
294                        + dest + ", " + spec + ", " + start + ", " + limit;
295                    log.println( message );
296                    log.println( "---------------------------------------------------" );
297                    e.printStackTrace( log );
298                }
299                catch( TransitRuntimeException f )
300                {
301                    f.printStackTrace();
302                    e.printStackTrace();
303                }
304            }
305        }
306        
307        private String getQueryFragment( String path )
308        {
309            if( null == path )
310            {
311                return null;
312            }
313            else
314            {
315                int q = path.indexOf( "?" );
316                if( q > -1 )
317                {
318                    return path.substring( q+1 );
319                }
320                else
321                {
322                    return null;
323                }
324            }
325        }
326        
327        private String getRefFragment( String path )
328        {
329            if( null == path )
330            {
331                return null;
332            }
333            else
334            {
335                int n = path.indexOf( "#" );
336                if( n > -1 )
337                {
338                    return path.substring( n+1 );
339                }
340                else
341                {
342                    return null;
343                }
344            }
345        }
346    }