001 /*
002 * Copyright 2004 Niclas Hedhman
003 * Copyright 2004-2006 Stephen J. McConnell
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.
015 *
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020 package net.dpml.transit.artifact;
021
022 import java.io.InputStream;
023 import java.io.IOException;
024 import java.io.OutputStream;
025 import java.io.File;
026 import java.net.UnknownServiceException;
027 import java.net.URI;
028 import java.net.URL;
029 import java.net.URLConnection;
030 import java.net.URISyntaxException;
031
032 import net.dpml.transit.Artifact;
033 import net.dpml.transit.Transit;
034 import net.dpml.transit.SecuredTransitContext;
035 import net.dpml.transit.CacheHandler;
036
037 import net.dpml.lang.Part;
038
039 import net.dpml.util.MimeTypeHandler;
040
041 /**
042 * The connection handler for URLs based on the "artifact" protocol family.
043 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
044 * @version 1.0.3
045 */
046 public class ArtifactURLConnection extends URLConnection
047 {
048 // ------------------------------------------------------------------------
049 // state
050 // ------------------------------------------------------------------------
051
052 /**
053 * Transit context.
054 */
055 private final SecuredTransitContext m_context;
056
057 /**
058 * Artifact.
059 */
060 private final Artifact m_artifact;
061
062 /**
063 * Reference fragment.
064 */
065 private final String m_reference;
066
067 /**
068 * The connected state.
069 */
070 private boolean m_connected;
071
072 // ------------------------------------------------------------------------
073 // constructor
074 // ------------------------------------------------------------------------
075
076 /**
077 * Creation of a new handler.
078 * @param url the url to establish a connection with
079 * @param context the transit context
080 * @exception NullPointerException if the url argument is null
081 * @exception IOException if the url argument is invalid
082 */
083 ArtifactURLConnection( URL url, SecuredTransitContext context )
084 throws NullPointerException, IOException
085 {
086 super( url );
087
088 Transit.getInstance(); // make sure Transit is initialized
089
090 m_context = context;
091 m_reference = getReference( url );
092
093 String spec = getRealSpec( url, m_reference );
094 try
095 {
096 m_artifact = Artifact.createArtifact( spec );
097 }
098 catch( URISyntaxException e )
099 {
100 throw new IOException( e.toString() );
101 }
102 }
103
104 // ------------------------------------------------------------------------
105 // URLConnection
106 // ------------------------------------------------------------------------
107
108 /**
109 * Establish a connection. The implementation will attempt to
110 * resolve the resource relative to the cache and associated hosts.
111 *
112 * @exception IOException is an error occurs while attempting to establish
113 * the connection.
114 */
115 public void connect()
116 throws IOException
117 {
118 if( m_connected )
119 {
120 return;
121 }
122 else
123 {
124 m_connected = true;
125 InputStream stream = null;
126 try
127 {
128 stream = getInputStream();
129 }
130 finally
131 {
132 if( null != stream )
133 {
134 try
135 {
136 stream.close();
137 }
138 finally
139 {
140 stream = null;
141 }
142 }
143 }
144 }
145 }
146
147 /**
148 * Return an input stream to the resource.
149 * @return the input stream
150 * @exception IOException is an error occurs
151 */
152 public InputStream getInputStream()
153 throws IOException
154 {
155 connect();
156 CacheHandler cache = m_context.getCacheHandler();
157 if( null != m_reference )
158 {
159 return cache.getResource( m_artifact, m_reference );
160 }
161 else
162 {
163 return cache.getResource( m_artifact );
164 }
165 }
166
167 /**
168 * Return an output stream to the resource.
169 * @return the output stream
170 * @exception IOException if any I/O problems occur.
171 */
172 public OutputStream getOutputStream()
173 throws IOException
174 {
175 CacheHandler cache = m_context.getCacheHandler();
176 return cache.createOutputStream( m_artifact );
177 }
178
179 /**
180 * Reutrn the mimetype of the content.
181 * @return the content mimetype
182 */
183 public String getContentType()
184 {
185 String type = m_artifact.getType();
186 return MimeTypeHandler.getMimeType( type );
187 }
188
189 /**
190 * Return the content for this artifact.
191 * @return the content object (possibly null)
192 * @exception IOException is an error occurs
193 */
194 public Object getContent()
195 throws IOException
196 {
197 Object content = getContent( new Class[0] );
198 if( content != null )
199 {
200 return content;
201 }
202 else
203 {
204 return super.getContent();
205 }
206 }
207
208 /**
209 * Return the content for this artifact.
210 * @param classes a sequence of classes against which the
211 * implementation will attempt to establish a known match
212 * @return the content object (possibly null)
213 * @exception IOException is an error occurs
214 */
215 public Object getContent( Class[] classes )
216 throws IOException
217 {
218 //
219 // attempt to resolve this locally as we may be dealing
220 // with Depot references to the artifact File
221 //
222
223 for( int i=0; i < classes.length; i++ )
224 {
225 Class c = classes[i];
226 if( c.equals( File.class ) )
227 {
228 return m_context.getCacheHandler().getLocalFile( m_artifact );
229 }
230 }
231
232 String type = m_artifact.getType();
233
234 //
235 // if the type is a plugin then handle this directly
236 //
237
238 if( "part".equals( type ) )
239 {
240 URI uri = m_artifact.toURI();
241 Part part = Part.load( uri );
242 return part.getContent( classes );
243 }
244
245 //
246 // otherwise fallback on the default jvm content handling
247 //
248
249 try
250 {
251 Object content = super.getContent( classes );
252 if( content != null )
253 {
254 return content;
255 }
256 }
257 catch( UnknownServiceException use )
258 {
259 boolean ignoreThis = true;
260 }
261
262 return null;
263 }
264
265 // ------------------------------------------------------------------------
266 // implementation
267 // ------------------------------------------------------------------------
268
269 /**
270 * Return a fragment referencing content within the resource referenced by
271 * the artifact.
272 * @param url the url
273 * @return the fragment or null if this is not a referential url
274 */
275 private String getReference( URL url )
276 {
277 String path = url.getPath();
278 int i = path.indexOf( '!' );
279 if( i < 0 )
280 {
281 return null;
282 }
283 else
284 {
285 return path.substring( i );
286 }
287 }
288
289 /**
290 * Return the real specification of the supplied url.
291 * @param url the url to evaluate
292 * @param ref a reference fragment
293 * @return the artifact url spec withough the ref fragment
294 */
295 private String getRealSpec( URL url, String ref )
296 {
297 if( null != ref )
298 {
299 String spec = url.toString();
300 int j = spec.indexOf( ref );
301 if( j > 0 )
302 {
303 String s = spec.substring( 0, j );
304 String query = url.getQuery();
305 if( null != query )
306 {
307 s = s + "?" + query;
308 }
309 String version = url.getUserInfo();
310 if( null != version )
311 {
312 s = s + "#" + version;
313 }
314 return s;
315 }
316 }
317 return url.toString();
318 }
319 }