All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.browsermob.proxy.jetty.http.HttpContext Maven / Gradle / Ivy

There is a newer version: 2.0-beta-7
Show newest version
// ========================================================================
// $Id: HttpContext.java,v 1.136 2006/02/21 09:47:43 gregwilkins Exp $
// Copyright 2000-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at 
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================

package org.browsermob.proxy.jetty.http;

import org.apache.commons.logging.Log;
import org.browsermob.proxy.jetty.http.ResourceCache.ResourceMetaData;
import org.browsermob.proxy.jetty.http.handler.ErrorPageHandler;
import org.browsermob.proxy.jetty.log.LogFactory;
import org.browsermob.proxy.jetty.util.*;
import org.browsermob.proxy.jetty.util.URI;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.*;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.util.*;


/* ------------------------------------------------------------ */
/** Context for a collection of HttpHandlers.
 * HTTP Context provides an ordered container for HttpHandlers
 * that share the same path prefix, filebase, resourcebase and/or
 * classpath.
 * 

* A HttpContext is analagous to a ServletContext in the * Servlet API, except that it may contain other types of handler * other than servlets. *

* A ClassLoader is created for the context and it uses * Thread.currentThread().getContextClassLoader(); as it's parent loader. * The class loader is initialized during start(), when a derived * context calls initClassLoader() or on the first call to loadClass() *

* * Note. that order is important when configuring a HttpContext. * For example, if resource serving is enabled before servlets, then resources * take priority. * * @see HttpServer * @see HttpHandler * @see org.browsermob.proxy.jetty.jetty.servlet.ServletHttpContext * @version $Id: HttpContext.java,v 1.136 2006/02/21 09:47:43 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ public class HttpContext extends Container implements LifeCycle, HttpHandler, EventProvider, Serializable { private static Log log = LogFactory.getLog(HttpContext.class); /* ------------------------------------------------------------ */ /** File class path attribute. * If this name is set as a context init parameter, then the attribute * name given will be used to set the file classpath for the context as a * context attribute. */ public final static String __fileClassPathAttr= "org.browsermob.proxy.jetty.http.HttpContext.FileClassPathAttribute"; public final static String __ErrorHandler= "org.browsermob.proxy.jetty.http.ErrorHandler"; /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ // These attributes are serialized by WebApplicationContext, which needs // to be updated if you add to these private String _contextPath; private List _vhosts=new ArrayList(2); private List _hosts=new ArrayList(2); private List _handlers=new ArrayList(3); private Map _attributes = new HashMap(3); private boolean _redirectNullPath=true; private boolean _statsOn=false; private PermissionCollection _permissions; private boolean _classLoaderJava2Compliant=true; private ResourceCache _resources; private String[] _systemClasses=new String [] {"java.","javax.servlet.","javax.xml.","org.browsermob.proxy.jetty.","org.xml.","org.w3c.","org.apache.commons.logging."}; private String[] _serverClasses = new String[] {"-org.browsermob.proxy.jetty.http.PathMap","-org.browsermob.proxy.jetty.jetty.servlet.Invoker","-org.browsermob.proxy.jetty.jetty.servlet.JSR154Filter","-org.browsermob.proxy.jetty.jetty.servlet.Default","org.browsermob.proxy.jetty.jetty.Server","org.browsermob.proxy.jetty.http.","org.browsermob.proxy.jetty.start.","org.browsermob.proxy.jetty.stop."}; /* ------------------------------------------------------------ */ private String _contextName; private String _classPath; private Map _initParams = new HashMap(11); private UserRealm _userRealm; private String _realmName; private PathMap _constraintMap=new PathMap(); private Authenticator _authenticator; private RequestLog _requestLog; private String[] _welcomes= { "welcome.html", "index.html", "index.htm", "index.jsp" }; /* ------------------------------------------------------------ */ private transient boolean _gracefulStop; private transient ClassLoader _parent; private transient ClassLoader _loader; private transient HttpServer _httpServer; private transient File _tmpDir; private transient HttpHandler[] _handlersArray; private transient String[] _vhostsArray; /* ------------------------------------------------------------ */ transient Object _statsLock=new Object[0]; transient long _statsStartedAt; transient int _requests; transient int _requestsActive; transient int _requestsActiveMax; transient int _responses1xx; // Informal transient int _responses2xx; // Success transient int _responses3xx; // Redirection transient int _responses4xx; // Client Error transient int _responses5xx; // Server Error /* ------------------------------------------------------------ */ /** Constructor. */ public HttpContext() { setAttribute(__ErrorHandler, new ErrorPageHandler()); _resources=new ResourceCache(); addComponent(_resources); } /* ------------------------------------------------------------ */ /** Constructor. * @param httpServer * @param contextPathSpec */ public HttpContext(HttpServer httpServer,String contextPathSpec) { this(); setHttpServer(httpServer); setContextPath(contextPathSpec); } /* ------------------------------------------------------------ */ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); _statsLock=new Object[0]; getHandlers(); for (int i=0;i<_handlersArray.length;i++) _handlersArray[i].initialize(this); } /* ------------------------------------------------------------ */ /** Get the ThreadLocal HttpConnection. * Get the HttpConnection for current thread, if any. This method is * not static in order to control access. * @return HttpConnection for this thread. */ public HttpConnection getHttpConnection() { return HttpConnection.getHttpConnection(); } /* ------------------------------------------------------------ */ void setHttpServer(HttpServer httpServer) { _httpServer=httpServer; _contextName=null; } /* ------------------------------------------------------------ */ public HttpServer getHttpServer() { return _httpServer; } /* ------------------------------------------------------------ */ public void setStopGracefully(boolean graceful) { _gracefulStop=graceful; } /* ------------------------------------------------------------ */ public boolean getStopGracefully() { return _gracefulStop; } /* ------------------------------------------------------------ */ public static String canonicalContextPathSpec(String contextPathSpec) { // check context path if (contextPathSpec==null || contextPathSpec.indexOf(',')>=0 || contextPathSpec.startsWith("*")) throw new IllegalArgumentException ("Illegal context spec:"+contextPathSpec); if(!contextPathSpec.startsWith("/")) contextPathSpec='/'+contextPathSpec; if (contextPathSpec.length()>1) { if (contextPathSpec.endsWith("/")) contextPathSpec+="*"; else if (!contextPathSpec.endsWith("/*")) contextPathSpec+="/*"; } return contextPathSpec; } /* ------------------------------------------------------------ */ public void setContextPath(String contextPathSpec) { if (_httpServer!=null) _httpServer.removeMappings(this); contextPathSpec=canonicalContextPathSpec(contextPathSpec); if (contextPathSpec.length()>1) _contextPath=contextPathSpec.substring(0,contextPathSpec.length()-2); else _contextPath="/"; _contextName=null; if (_httpServer!=null) _httpServer.addMappings(this); } /* ------------------------------------------------------------ */ /** * @return The context prefix */ public String getContextPath() { return _contextPath; } /* ------------------------------------------------------------ */ /** Add a virtual host alias to this context. * @see #setVirtualHosts * @param hostname A hostname. A null host name means any hostname is * acceptable. Host names may String representation of IP addresses. */ public void addVirtualHost(String hostname) { // Note that null hosts are also added. if (!_vhosts.contains(hostname)) { _vhosts.add(hostname); _contextName=null; if (_httpServer!=null) { if (_vhosts.size()==1) _httpServer.removeMapping(null,this); _httpServer.addMapping(hostname,this); } _vhostsArray=null; } } /* ------------------------------------------------------------ */ /** remove a virtual host alias to this context. * @see #setVirtualHosts * @param hostname A hostname. A null host name means any hostname is * acceptable. Host names may String representation of IP addresses. */ public void removeVirtualHost(String hostname) { // Note that null hosts are also added. if (_vhosts.remove(hostname)) { _contextName=null; if (_httpServer!=null) { _httpServer.removeMapping(hostname,this); if (_vhosts.size()==0) _httpServer.addMapping(null,this); } _vhostsArray=null; } } /* ------------------------------------------------------------ */ /** Set the virtual hosts for the context. * Only requests that have a matching host header or fully qualified * URL will be passed to that context with a virtual host name. * A context with no virtual host names or a null virtual host name is * available to all requests that are not served by a context with a * matching virtual host name. * @param hosts Array of virtual hosts that this context responds to. A * null host name or null/empty array means any hostname is acceptable. * Host names may String representation of IP addresses. */ public void setVirtualHosts(String[] hosts) { List old = new ArrayList(_vhosts); if (hosts!=null) { for (int i=0;i 0) { Object constraints= null; // for each path match // Add only constraints that have the correct method // break if the matching pattern changes. This allows only // constraints with matching pattern and method to be combined. loop: for (int m= 0; m < scss.size(); m++) { Map.Entry entry= (Map.Entry)scss.get(m); Object scs= entry.getValue(); String p=(String)entry.getKey(); for (int c=0;c0) { Object o = request.getHttpConnection().getConnection(); if (o instanceof Socket) { Socket s=(Socket)o; if (!_hosts.contains(s.getLocalAddress())) { if(log.isDebugEnabled())log.debug(s.getLocalAddress()+" not in "+_hosts); return; } } } // handle stats if (_statsOn) { synchronized(_statsLock) { _requests++; _requestsActive++; if (_requestsActive>_requestsActiveMax) _requestsActiveMax=_requestsActive; } } String pathInContext = URI.canonicalPath(request.getPath()); if (pathInContext==null) { // Must be a bad request. throw new HttpException(HttpResponse.__400_Bad_Request); } if (_contextPath.length()>1) pathInContext=pathInContext.substring(_contextPath.length()); if (_redirectNullPath && (pathInContext==null || pathInContext.length()==0)) { StringBuffer buf=request.getRequestURL(); buf.append("/"); String q=request.getQuery(); if (q!=null&&q.length()!=0) buf.append("?"+q); response.sendRedirect(buf.toString()); if (log.isDebugEnabled()) log.debug(this+" consumed all of path "+ request.getPath()+ ", redirect to "+buf.toString()); return; } String pathParams=null; int semi = pathInContext.lastIndexOf(';'); if (semi>=0) { int pl = pathInContext.length()-semi; String ep=request.getEncodedPath(); if(';'==ep.charAt(ep.length()-pl)) { pathParams=pathInContext.substring(semi+1); pathInContext=pathInContext.substring(0,semi); } } try { handle(pathInContext,pathParams,request,response); } finally { if (_userRealm!=null && request.hasUserPrincipal()) _userRealm.disassociate(request.getUserPrincipal()); } } /* ------------------------------------------------------------ */ /** Handler request. * Call each HttpHandler until request is handled. * @param pathInContext Path in context * @param pathParams Path parameters such as encoded Session ID * @param request * @param response * @return True if the request has been handled. * @exception HttpException * @exception IOException */ public void handle(String pathInContext, String pathParams, HttpRequest request, HttpResponse response) throws HttpException, IOException { Object old_scope= null; try { old_scope= enterContextScope(request,response); HttpHandler[] handlers= getHandlers(); for (int k= 0; k < handlers.length; k++) { HttpHandler handler= handlers[k]; if (handler == null) { handlers= getHandlers(); k= -1; continue; } if (!handler.isStarted()) { if (log.isDebugEnabled()) log.debug(handler + " not started in " + this); continue; } if (log.isDebugEnabled()) log.debug("Handler " + handler); handler.handle(pathInContext, pathParams, request, response); if (request.isHandled()) { if (log.isDebugEnabled()) log.debug("Handled by " + handler); return; } } return; } finally { leaveContextScope(request, response, old_scope); } } /* ------------------------------------------------------------ */ /** Enter the context scope. * This method is called (by handle or servlet dispatchers) to indicate that * request handling is entering the scope of this context. The opaque scope object * returned, should be passed to the leaveContextScope method. */ public Object enterContextScope(HttpRequest request, HttpResponse response) { // Save the thread context loader Thread thread = Thread.currentThread(); ClassLoader cl=thread.getContextClassLoader(); HttpContext c=response.getHttpContext(); Scope scope=null; if (cl!=HttpContext.class.getClassLoader() || c!=null) { scope=new Scope(); scope._classLoader=cl; scope._httpContext=c; } if (_loader!=null) thread.setContextClassLoader(_loader); response.setHttpContext(this); return scope; } /* ------------------------------------------------------------ */ /** Leave the context scope. * This method is called (by handle or servlet dispatchers) to indicate that * request handling is leaveing the scope of this context. The opaque scope object * returned by enterContextScope should be passed in. */ public void leaveContextScope(HttpRequest request, HttpResponse response,Object oldScope) { if (oldScope==null) { Thread.currentThread() .setContextClassLoader(HttpContext.class.getClassLoader()); response.setHttpContext(null); } else { Scope old = (Scope)oldScope; Thread.currentThread().setContextClassLoader(old._classLoader); response.setHttpContext(old._httpContext); } } /* ------------------------------------------------------------ */ public String getHttpContextName() { if (_contextName==null) _contextName = (_vhosts.size()>1?(_vhosts.toString()+":"):"")+_contextPath; return _contextName; } /* ------------------------------------------------------------ */ public void setHttpContextName(String s) { _contextName=s; } /* ------------------------------------------------------------ */ public String toString() { return "HttpContext["+getContextPath()+","+getHttpContextName()+"]"; } /* ------------------------------------------------------------ */ public String toString(boolean detail) { return "HttpContext["+getContextPath()+","+getHttpContextName()+"]" + (detail?("="+_handlers):""); } /* ------------------------------------------------------------ */ protected synchronized void doStart() throws Exception { if (isStarted()) return; if (_httpServer.getServerClasses()!=null) _serverClasses=_httpServer.getServerClasses(); if (_httpServer.getSystemClasses()!=null) _systemClasses=_httpServer.getSystemClasses(); _resources.start(); statsReset(); if (_httpServer==null) throw new IllegalStateException("No server for "+this); // start the context itself _resources.getMimeMap(); _resources.getEncodingMap(); // Setup realm if (_userRealm==null && _authenticator!=null) { _userRealm=_httpServer.getRealm(_realmName); if (_userRealm==null) log.warn("No Realm: "+_realmName); } // setup the context loader initClassLoader(false); // Set attribute if needed String attr = getInitParameter(__fileClassPathAttr); if (attr!=null && attr.length()>0) setAttribute(attr,getFileClassPath()); // Start the handlers Thread thread = Thread.currentThread(); ClassLoader lastContextLoader=thread.getContextClassLoader(); try { if (_loader!=null) thread.setContextClassLoader(_loader); if (_requestLog!=null) _requestLog.start(); startHandlers(); } finally { thread.setContextClassLoader(lastContextLoader); getHandlers(); } } /* ------------------------------------------------------------ */ /** Start the handlers. * This is called by start after the classloader has been * initialized and set as the thread context loader. * It may be specialized to provide custom handling * before any handlers are started. * @exception Exception */ protected void startHandlers() throws Exception { // Prepare a multi exception MultiException mx = new MultiException(); Iterator handlers = _handlers.iterator(); while(handlers.hasNext()) { HttpHandler handler=(HttpHandler)handlers.next(); if (!handler.isStarted()) try{handler.start();}catch(Exception e){mx.add(e);} } mx.ifExceptionThrow(); } /* ------------------------------------------------------------ */ /** Stop the context. * @param graceful If true and statistics are on, then this method will wait * for requestsActive to go to zero before calling stop() */ public void stop(boolean graceful) throws InterruptedException { boolean gs=_gracefulStop; try { _gracefulStop=true; // wait for all requests to complete. while (graceful && _statsOn && _requestsActive>0 && _httpServer!=null) try {Thread.sleep(100);} catch (InterruptedException e){throw e;} catch (Exception e){LogSupport.ignore(log,e);} stop(); } finally { _gracefulStop=gs; } } /* ------------------------------------------------------------ */ /** Stop the context. */ protected void doStop() throws Exception { if (_httpServer==null) throw new InterruptedException("Destroy called"); synchronized(this) { // Notify the container for the stop Thread thread = Thread.currentThread(); ClassLoader lastContextLoader=thread.getContextClassLoader(); try { if (_loader!=null) thread.setContextClassLoader(_loader); Iterator handlers = _handlers.iterator(); while(handlers.hasNext()) { HttpHandler handler=(HttpHandler)handlers.next(); if (handler.isStarted()) { try{handler.stop();} catch(Exception e){log.warn(LogSupport.EXCEPTION,e);} } } if (_requestLog!=null) _requestLog.stop(); } finally { thread.setContextClassLoader(lastContextLoader); } // TODO this is a poor test if (_loader instanceof ContextLoader) { ((ContextLoader)_loader).destroy(); LogFactory.release(_loader); } _loader=null; } _resources.flushCache(); _resources.stop(); } /* ------------------------------------------------------------ */ /** Destroy a context. * Destroy a context and remove it from the HttpServer. The * HttpContext must be stopped before it can be destroyed. */ public void destroy() { if (isStarted()) throw new IllegalStateException("Started"); if (_httpServer!=null) _httpServer.removeContext(this); _httpServer=null; if (_handlers!=null) _handlers.clear(); _handlers=null; _parent=null; _loader=null; if (_attributes!=null) _attributes.clear(); _attributes=null; if (_initParams!=null) _initParams.clear(); _initParams=null; if (_vhosts!=null) _vhosts.clear(); _vhosts=null; _hosts=null; _tmpDir=null; _permissions=null; removeComponent(_resources); if (_resources!=null) { _resources.flushCache(); if (_resources.isStarted()) try{_resources.stop();}catch(Exception e){LogSupport.ignore(log,e);} _resources.destroy(); } _resources=null; super.destroy(); } /* ------------------------------------------------------------ */ /** Set the request log. * @param log RequestLog to use. */ public void setRequestLog(RequestLog log) { _requestLog=log; } /* ------------------------------------------------------------ */ public RequestLog getRequestLog() { return _requestLog; } /* ------------------------------------------------------------ */ /** Send an error response. * This method may be specialized to provide alternative error handling for * errors generated by the container. The default implemenation calls HttpResponse.sendError * @param response the response to send * @param code The error code * @param msg The message for the error or null for the default * @throws IOException Problem sending response. */ public void sendError(HttpResponse response,int code,String msg) throws IOException { response.sendError(code,msg); } /* ------------------------------------------------------------ */ /** Send an error response. * This method obtains the responses context and call sendError for context specific * error handling. * @param response the response to send * @param code The error code * @param msg The message for the error or null for the default * @throws IOException Problem sending response. */ public static void sendContextError(HttpResponse response,int code,String msg) throws IOException { HttpContext context = response.getHttpContext(); if (context!=null) context.sendError(response,code,msg); else response.sendError(code,msg); } /* ------------------------------------------------------------ */ /** True set statistics recording on for this context. * @param on If true, statistics will be recorded for this context. */ public void setStatsOn(boolean on) { log.info("setStatsOn "+on+" for "+this); _statsOn=on; statsReset(); } /* ------------------------------------------------------------ */ public boolean getStatsOn() {return _statsOn;} /* ------------------------------------------------------------ */ public long getStatsOnMs() {return _statsOn?(System.currentTimeMillis()-_statsStartedAt):0;} /* ------------------------------------------------------------ */ public void statsReset() { synchronized(_statsLock) { if (_statsOn) _statsStartedAt=System.currentTimeMillis(); _requests=0; _requestsActiveMax=_requestsActive; _responses1xx=0; _responses2xx=0; _responses3xx=0; _responses4xx=0; _responses5xx=0; } } /* ------------------------------------------------------------ */ /** * @return Get the number of requests handled by this context * since last call of statsReset(). If setStatsOn(false) then this * is undefined. */ public int getRequests() {return _requests;} /* ------------------------------------------------------------ */ /** * @return Number of requests currently active. * Undefined if setStatsOn(false). */ public int getRequestsActive() {return _requestsActive;} /* ------------------------------------------------------------ */ /** * @return Maximum number of active requests * since statsReset() called. Undefined if setStatsOn(false). */ public int getRequestsActiveMax() {return _requestsActiveMax;} /* ------------------------------------------------------------ */ /** * @return Get the number of responses with a 2xx status returned * by this context since last call of statsReset(). Undefined if * if setStatsOn(false). */ public int getResponses1xx() {return _responses1xx;} /* ------------------------------------------------------------ */ /** * @return Get the number of responses with a 100 status returned * by this context since last call of statsReset(). Undefined if * if setStatsOn(false). */ public int getResponses2xx() {return _responses2xx;} /* ------------------------------------------------------------ */ /** * @return Get the number of responses with a 3xx status returned * by this context since last call of statsReset(). Undefined if * if setStatsOn(false). */ public int getResponses3xx() {return _responses3xx;} /* ------------------------------------------------------------ */ /** * @return Get the number of responses with a 4xx status returned * by this context since last call of statsReset(). Undefined if * if setStatsOn(false). */ public int getResponses4xx() {return _responses4xx;} /* ------------------------------------------------------------ */ /** * @return Get the number of responses with a 5xx status returned * by this context since last call of statsReset(). Undefined if * if setStatsOn(false). */ public int getResponses5xx() {return _responses5xx;} /* ------------------------------------------------------------ */ /** Log a request and response. * Statistics are also collected by this method. * @param request * @param response */ public void log(HttpRequest request, HttpResponse response, int length) { if (_statsOn) { synchronized(_statsLock) { if (--_requestsActive<0) _requestsActive=0; if (response!=null) { switch(response.getStatus()/100) { case 1: _responses1xx++;break; case 2: _responses2xx++;break; case 3: _responses3xx++;break; case 4: _responses4xx++;break; case 5: _responses5xx++;break; } } } } if (_requestLog!=null && request!=null && response!=null) _requestLog.log(request,response,length); else if (_httpServer!=null) _httpServer.log(request,response,length); } /* ------------------------------------------------------------ */ /* Class to save scope of nested context calls */ private static class Scope { ClassLoader _classLoader; HttpContext _httpContext; } /* * @see org.browsermob.proxy.jetty.http.HttpHandler#getName() */ public String getName() { return this.getContextPath(); } /* * @see org.browsermob.proxy.jetty.http.HttpHandler#getHttpContext() */ public HttpContext getHttpContext() { return this; } /* * @see org.browsermob.proxy.jetty.http.HttpHandler#initialize(org.browsermob.proxy.jetty.http.HttpContext) */ public void initialize(HttpContext context) { throw new UnsupportedOperationException(); } /** * @return */ public Resource getBaseResource() { return _resources.getBaseResource(); } /** * @param type * @return */ public String getEncodingByMimeType(String type) { return _resources.getEncodingByMimeType(type); } /** * @return */ public Map getEncodingMap() { return _resources.getEncodingMap(); } /** * @return */ public int getMaxCachedFileSize() { return _resources.getMaxCachedFileSize(); } /** * @return */ public int getMaxCacheSize() { return _resources.getMaxCacheSize(); } /** * @param filename * @return */ public String getMimeByExtension(String filename) { return _resources.getMimeByExtension(filename); } /** * @return */ public Map getMimeMap() { return _resources.getMimeMap(); } /** * @param pathInContext * @return * @throws IOException */ public Resource getResource(String pathInContext) throws IOException { return _resources.getResource(pathInContext); } /** * @return */ public String getResourceBase() { return _resources.getResourceBase(); } /** * @param resource * @return */ public ResourceMetaData getResourceMetaData(Resource resource) { return _resources.getResourceMetaData(resource); } /** * @param base */ public void setBaseResource(Resource base) { _resources.setBaseResource(base); } /** * @param encodingMap */ public void setEncodingMap(Map encodingMap) { _resources.setEncodingMap(encodingMap); } /** * @param maxCachedFileSize */ public void setMaxCachedFileSize(int maxCachedFileSize) { _resources.setMaxCachedFileSize(maxCachedFileSize); } /** * @param maxCacheSize */ public void setMaxCacheSize(int maxCacheSize) { _resources.setMaxCacheSize(maxCacheSize); } /** * @param mimeMap */ public void setMimeMap(Map mimeMap) { _resources.setMimeMap(mimeMap); } /** * @param extension * @param type */ public void setMimeMapping(String extension, String type) { _resources.setMimeMapping(extension, type); } /** * @param resourceBase */ public void setResourceBase(String resourceBase) { _resources.setResourceBase(resourceBase); } /** * @param mimeType * @param encoding */ public void setTypeEncoding(String mimeType, String encoding) { _resources.setTypeEncoding(mimeType, encoding); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy