blob: 5efcbf6300f4b452703d51d52b1d28badeb63cb8 [file] [log] [blame]
Felix Meschbergerc7ee0152008-03-26 09:24:35 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.mortbay.jetty.servlet;
20
21
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.OutputStream;
Rob Walker8179cc72008-10-13 06:04:01 +000025import java.io.File;
Felix Meschbergerc7ee0152008-03-26 09:24:35 +000026import java.net.URL;
Rob Walker8179cc72008-10-13 06:04:01 +000027import java.net.URLConnection;
Felix Meschbergerc7ee0152008-03-26 09:24:35 +000028import java.security.AccessControlContext;
29import java.security.AccessController;
30import java.security.PrivilegedActionException;
31import java.security.PrivilegedExceptionAction;
32
33import javax.servlet.Servlet;
34import javax.servlet.ServletException;
35import javax.servlet.ServletRequest;
36import javax.servlet.ServletResponse;
37import javax.servlet.http.HttpServletRequest;
38import javax.servlet.http.HttpServletResponse;
39
40import org.apache.felix.http.jetty.Activator;
41import org.apache.felix.http.jetty.ServletContextGroup;
42import org.mortbay.jetty.HttpConnection;
43import org.mortbay.jetty.HttpMethods;
44import org.mortbay.jetty.Request;
45import org.osgi.service.http.HttpContext;
46import org.osgi.service.log.LogService;
47
48
49public class OsgiResourceHolder extends ServletHolder
50{
51 private ServletContextGroup m_servletContextGroup;
52 private HttpContext m_osgiHttpContext;
53 private AccessControlContext m_acc;
Rob Walker48cb2432008-10-13 14:30:43 +000054 private String m_path;
Felix Meschbergerc7ee0152008-03-26 09:24:35 +000055
56
Rob Walker48cb2432008-10-13 14:30:43 +000057 public OsgiResourceHolder( ServletHandler handler, String name, String path, ServletContextGroup servletContextGroup )
Felix Meschbergerc7ee0152008-03-26 09:24:35 +000058 {
59 super();
60
61 setServletHandler( handler );
62 setName( name );
63
64 m_servletContextGroup = servletContextGroup;
65 m_osgiHttpContext = servletContextGroup.getOsgiHttpContext();
Rob Walker48cb2432008-10-13 14:30:43 +000066 m_path = path;
67 if (m_path == null)
68 {
69 m_path = "";
70 }
71
Felix Meschbergerc7ee0152008-03-26 09:24:35 +000072 if ( System.getSecurityManager() != null )
73 {
74 m_acc = AccessController.getContext();
75 }
76 }
77
78
79 public synchronized Servlet getServlet()
80 {
81 return null;
82 }
83
84
85 // override "Holder" method to prevent instantiation
86 public synchronized Object newInstance()
87 {
88 return null;
89 }
90
91
92 public void handle( ServletRequest sRequest, ServletResponse sResponse ) throws ServletException, IOException
93 {
94 HttpServletRequest request = ( HttpServletRequest ) sRequest;
95 HttpServletResponse response = ( HttpServletResponse ) sResponse;
Felix Meschberger5cc7e7d2008-03-26 09:48:07 +000096
97 // get the relative path (assume empty path if there is no path info)
98 // (FELIX-503)
Felix Meschbergerc7ee0152008-03-26 09:24:35 +000099 String target = request.getPathInfo();
Rob Walker48cb2432008-10-13 14:30:43 +0000100 if (target == null)
101 {
Felix Meschberger5cc7e7d2008-03-26 09:48:07 +0000102 target = "";
103 }
Rob Walker48cb2432008-10-13 14:30:43 +0000104
105 if (!target.startsWith("/"))
106 {
107 target += "/" + target;
108 }
Felix Meschbergerc7ee0152008-03-26 09:24:35 +0000109
Rob Walker48cb2432008-10-13 14:30:43 +0000110 Activator.debug( "handle for name:" + m_path + " (path=" + target + ")" );
Felix Meschbergerc7ee0152008-03-26 09:24:35 +0000111
112 if ( !m_osgiHttpContext.handleSecurity( request, response ) )
113 {
114 return;
115 }
116
117 // Create resource based name and see if we can resolve it
Rob Walker48cb2432008-10-13 14:30:43 +0000118 String resName = m_path + target;
Felix Meschbergerc7ee0152008-03-26 09:24:35 +0000119 Activator.debug( "** looking for: " + resName );
120 URL url = m_osgiHttpContext.getResource( resName );
121
122 if ( url == null )
123 {
124 Request base_request = sRequest instanceof Request ? ( Request ) sRequest : HttpConnection
125 .getCurrentConnection().getRequest();
126 base_request.setHandled( false );
127 return;
128 }
129
130 Activator.debug( "serving up:" + resName );
131
132 String method = request.getMethod();
133 if ( method.equals( HttpMethods.GET ) || method.equals( HttpMethods.POST ) || method.equals( HttpMethods.HEAD ) )
134 {
135 handleGet( request, response, url, resName );
136 }
137 else
138 {
139 try
140 {
141 response.sendError( HttpServletResponse.SC_NOT_IMPLEMENTED );
142 }
143 catch ( Exception e )
144 {/*TODO: include error logging*/
145 }
146 }
147 }
148
149
150 public void handleGet( HttpServletRequest request, final HttpServletResponse response, final URL url, String resName )
151 throws IOException
152 {
153 String encoding = m_osgiHttpContext.getMimeType( resName );
154
155 if ( encoding == null )
156 {
157 encoding = m_servletContextGroup.getMimeType( resName );
158 }
159
160 if ( encoding == null )
161 {
162 encoding = m_servletContextGroup.getMimeType( ".default" );
163 }
164
165 //TODO: not sure why this is needed, but sometimes get "IllegalState"
166 // errors if not included
167 response.setContentType( encoding );
168
Rob Walker8179cc72008-10-13 06:04:01 +0000169 long lastModified = getLastModified(url);
170
171 if (lastModified != 0)
Felix Meschbergerc7ee0152008-03-26 09:24:35 +0000172 {
Rob Walker8179cc72008-10-13 06:04:01 +0000173 response.setDateHeader("Last-Modified", lastModified);
174 }
175
176 if (!resourceModified(lastModified, request.getDateHeader("If-Modified-Since")))
177 {
178 response.setStatus(response.SC_NOT_MODIFIED);
Felix Meschbergerc7ee0152008-03-26 09:24:35 +0000179 }
180 else
181 {
Rob Walker8179cc72008-10-13 06:04:01 +0000182 // make sure we access the resource inside the bundle's access control
183 // context if supplied
184 if ( m_acc != null )
185 {
186 try
187 {
188 AccessController.doPrivileged( new PrivilegedExceptionAction()
189 {
190 public Object run() throws Exception
191 {
192 copyResourceBytes( url, response );
193 return null;
194 }
195 }, m_acc );
196 }
197 catch ( PrivilegedActionException ex )
198 {
199 IOException ioe = ( IOException ) ex.getException();
200 throw ioe;
201 }
202 }
203 else
204 {
205 copyResourceBytes( url, response );
206 }
207
208 //TODO: set other http fields e.g. __LastModified, __ContentLength
Felix Meschbergerc7ee0152008-03-26 09:24:35 +0000209 }
Felix Meschbergerc7ee0152008-03-26 09:24:35 +0000210 }
211
212
213 private void copyResourceBytes( URL url, HttpServletResponse response ) throws IOException
214 {
215 OutputStream os = null;
216 InputStream is = null;
217
218 try
219 {
220 os = response.getOutputStream();
221 is = url.openStream();
222
223 int len = 0;
224 byte[] buf = new byte[1024];
225 int n = 0;
226
227 while ( ( n = is.read( buf, 0, buf.length ) ) >= 0 )
228 {
229 os.write( buf, 0, n );
230 len += n;
231 }
232
233 try
234 {
235 response.setContentLength( len );
236 }
237 catch ( IllegalStateException ex )
238 {
239 Activator.log( LogService.LOG_ERROR, "OsgiResourceHandler", ex );
240 }
241 }
242 finally
243 {
244 if ( is != null )
245 {
246 is.close();
247 }
248 if ( os != null )
249 {
250 os.close();
251 }
252 }
253 }
254
255
256 // override "Holder" method to prevent attempt to load
257 // the servlet class.
258 public void doStart() throws Exception
259 {
260 }
261
262
263 // override "Holder" method to prevent destroy, which is only called
264 // when a bundle manually unregisters
265 public void doStop()
266 {
267 }
Rob Walker8179cc72008-10-13 06:04:01 +0000268
269
270 /**
271 * Gets the last modified value for file modification detection.
272 * Aids in "conditional get" and intermediate proxy/node cacheing.
273 *
274 * Approach used follows that used by Sun for JNLP handling to workaround an
275 * apparent issue where file URLs do not correctly return a last modified time.
276 *
277 */
278 protected long getLastModified (URL resUrl)
279 {
280 long lastModified = 0;
281
282 try
283 {
284 // Get last modified time
285 URLConnection conn = resUrl.openConnection();
286 lastModified = conn.getLastModified();
287 }
288 catch (Exception e)
289 {
290 // do nothing
291 }
Felix Meschbergerc7ee0152008-03-26 09:24:35 +0000292
Rob Walker8179cc72008-10-13 06:04:01 +0000293 if (lastModified == 0)
294 {
295 // Arguably a bug in the JRE will not set the lastModified for file URLs, and
296 // always return 0. This is a workaround for that problem.
297 String filepath = resUrl.getPath();
298
299 if (filepath != null)
300 {
301 File f = new File(filepath);
302 if (f.exists())
303 {
304 lastModified = f.lastModified();
305 }
306 }
307 }
308
Rob Walker4b005f62008-10-14 19:46:13 +0000309 Activator.debug( "url: " + resUrl + ", lastModified:" + lastModified);
310
Rob Walker8179cc72008-10-13 06:04:01 +0000311 return lastModified;
312 }
313
314
315 protected boolean resourceModified(long resTimestamp, long modSince)
316 {
317 boolean retval = false;
318
319 // Have to normalise timestamps as HTTP times have last 3 digits as zero
320 modSince /= 1000;
321 resTimestamp /= 1000;
322
Rob Walker72d3ecf2008-10-13 13:23:10 +0000323 // Timestamp check to see if modified - resTimestamp 0 check is for
324 // safety in case we didn't manage to get a timestamp for the resource
325 if (resTimestamp == 0 || modSince == -1 || resTimestamp > modSince)
Rob Walker8179cc72008-10-13 06:04:01 +0000326 {
327 retval = true;
328 }
329
330 return retval;
331 }
332
Felix Meschbergerc7ee0152008-03-26 09:24:35 +0000333}