/*
* Copyright 2005 John R. Fallows
*
* 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 net.java.dev.weblets.impl;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.Format;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import net.java.dev.weblets.Weblet;
import net.java.dev.weblets.WebletConfig;
import net.java.dev.weblets.WebletContainer;
import net.java.dev.weblets.WebletException;
import net.java.dev.weblets.WebletRequest;
import net.java.dev.weblets.WebletResponse;
import net.java.dev.weblets.impl.misc.SandboxGuard;
import net.java.dev.weblets.impl.parse.DisconnectedEntityResolver;
import net.java.dev.weblets.impl.util.ConfigurationUtils;
import net.java.dev.weblets.util.StringUtils;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.ObjectCreationFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
public class WebletContainerImpl extends WebletContainer {
public WebletContainerImpl(
ServletContext servletContext,
String webletContextPath,
Format webletURLFormat,
Pattern webletURLPattern,
boolean multipleConfigs) throws WebletException {
_servletContext = servletContext;
_webletURLFormat = webletURLFormat;
_webletURLPattern = webletURLPattern;
_webletContextPath = webletContextPath;
checkWebletsContextPath();
try {
Set configs = new HashSet();
// Enumeration e = getConfigEnumeration("weblets-config.xml"); /*lets find the root configs first*/
Set urls = new HashSet(); // urls.add(element);
if (multipleConfigs) {
ConfigurationUtils.getValidConfigFiles("META-INF/", "weblets-config.xml", configs);
try {
ConfigurationUtils.getValidConfigFiles("META-INF/", "MANIFEST.MF", configs);
} catch (NullPointerException ex) {
Log log = LogFactory.getLog(this.getClass());
log.info("MANIFEST.MF search failed for configurations, if you use Websphere, then you can safely ignore this please use a weblets-config.xml as entry point for your weblets configuration, please !");
}
Iterator configNameIterator = configs.iterator();
// Defensive: Glassfish.v2.b25 produces duplicates in Enumeration
// returned by loader.getResources()
while (configNameIterator.hasNext()) {
Enumeration theUrlEnum = ConfigurationUtils.getConfigEnumeration("META-INF/", (String) configNameIterator.next());
while (theUrlEnum.hasMoreElements()) {
URL resource = (URL) theUrlEnum.nextElement();
urls.add(resource);
}
}
} else {
Enumeration theUrlEnum = ConfigurationUtils.getConfigEnumeration("META-INF/", "weblets-config.xml");
while (theUrlEnum.hasMoreElements()) {
URL resource = (URL) theUrlEnum.nextElement();
urls.add(resource);
}
}
Iterator urlIterator = urls.iterator();
while (urlIterator.hasNext()) {
URL resource = (URL) urlIterator.next();
registerConfig(resource);
}
WebletContainer.setInstance(this);
}
catch (IOException e) {
throw new WebletException(e);
}
}
private void checkWebletsContextPath() {
if (_webletContextPath == null || _webletContextPath.trim().equals("")) {
//the setup context path defaults to empty lets determine another one if possible
Log log = LogFactory.getLog(WebletContainerImpl.class);
log.warn("No net.java.dev.weblets.contextpath context-param has been set" +
" this might cause problems in non jsf environments! ");
} else {
if (_webletContextPath.endsWith("/"))
_webletContextPath = _webletContextPath.substring(0, _webletContextPath.length() - 1);
}
}
public ServletContext getServletContext() {
return _servletContext;
}
public void destroy() {
if(_weblets != null && _weblets.values() != null) {
Iterator i = _weblets.values().iterator();
while (i != null && i.hasNext()) {
Weblet weblet = (Weblet) i.next();
weblet.destroy();
}
}
_weblets = null;
_webletConfigs = null;
_webletMappings = null;
_webletContextPath = null;
}
public Pattern getPattern() {
return _webletURLPattern;
}
public String[] parseWebletRequest(
String contextPath,
String requestURI,
long ifModifiedSince) {
Iterator i = _webletMappings.entrySet().iterator();
while (i.hasNext()) {
Map.Entry entry = (Map.Entry) i.next();
Pattern pattern = (Pattern) entry.getValue();
Matcher matcher = pattern.matcher(requestURI);
if (matcher.matches()) {
String webletPath = matcher.group(1);
String pathInfo = matcher.group(2);
String webletName = (String) entry.getKey();
return new String[]{webletName, webletPath, pathInfo};
}
}
return null;
}
/*setter and getter to access the context path*/
public String getWebletContextPath() {
return _webletContextPath;
}
public void setWebletContextPath(String contextPath) {
_webletContextPath = contextPath;
}
public void service(
WebletRequest request,
WebletResponse response) throws IOException, WebletException {
Weblet weblet = getWeblet(request);
String pathInfo = request.getPathInfo();
//enhanced security check
if (pathInfo != null && SandboxGuard.isJailBreak(pathInfo)) {
throw new WebletException("Security Exception, the " + pathInfo +
" breaks out of the resource jail, no resource is served!");
}
WebletConfig webConfig = weblet.getWebletConfig();
String mimeType = null;
if(!StringUtils.isBlank(pathInfo)) {
mimeType = webConfig.getMimeType(pathInfo);
}
if(mimeType == null) {
mimeType = response.getDefaultContentType();
}
response.setContentType(mimeType);
Set allowedResources = webConfig.getAllowedResources();
if (allowedResources != null) {
String filetype = StringUtils.getExtension(pathInfo);
if (!allowedResources.contains(filetype.toLowerCase())) {
throw new WebletException("Security Exception, the " + pathInfo +
" resource cannot be served!");
/* not allowed no content delivered */
}
}
weblet.service(request, response);
}
public Weblet getWeblet(WebletRequest request) {
String webletName = request.getWebletName();
return getWeblet(webletName);
}
public Weblet getWeblet(String webletName) {
Weblet weblet = (Weblet) _weblets.get(webletName);
if (weblet == null) {
try {
WebletConfigImpl config = (WebletConfigImpl) _webletConfigs.get(webletName);
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class webletClass = loader.loadClass(config.getWebletClass());
weblet = (Weblet) webletClass.newInstance();
weblet.init(config);
_weblets.put(webletName, weblet);
}
catch (ClassNotFoundException e) {
throw new WebletException(e);
}
catch (InstantiationException e) {
throw new WebletException(e);
}
catch (IllegalAccessException e) {
throw new WebletException(e);
}
}
return weblet;
}
/**
* new since 1.1 we now can get the weblet also
* as local stream for furhter external processing
* if we have our weblet request.
* <p/>
* We can add a dummy request
*
* @param request
* @return an input stream on our current weblet resource
* @throws WebletException
* @throws IOException
*/
public InputStream serviceStream(WebletRequest request, String mimetype) throws WebletException, IOException {
Weblet weblet = getWeblet(request);
return weblet.serviceStream(request.getPathInfo(), mimetype);
}
public String getResourceUri(
String webletName,
String pathInfo) throws WebletException {
WebletConfig config = (WebletConfig) _webletConfigs.get(webletName);
if (config == null)
throw new WebletException("Missing Weblet configuration for '" + webletName + "'");
String webletPath = (String) _webletPaths.get(webletName);
if (webletPath == null)
throw new WebletException("Missing Weblet mapping for '" + webletName + "'");
String webletVersion = config.getWebletVersion();
// URL-syntax /webletPath[$version]/pathInfo
StringBuffer buffer = new StringBuffer();
buffer.append(webletPath);
if (webletVersion != null) {
buffer.append('$');
buffer.append(webletVersion);
}
if (pathInfo != null) {
buffer.append(pathInfo);
}
String webletURL = buffer.toString();
if (_webletURLFormat != null)
webletURL = _webletURLFormat.format(new Object[]{webletURL});
return webletURL;
}
public InputStream getResourceStream(WebletRequest request, String mimeType) throws WebletException {
Weblet weblet = (Weblet) _weblets.get(request.getWebletName());
if (weblet == null)
return null;
try {
return weblet.serviceStream(request.getPathInfo(), mimeType);
} catch (IOException ex) {
return null;
}
}
/**
* registers the configuration file
* in its internal data structure
*
*
* @param webletsConfig the weblets config to be registered
*/
public void registerConfig(
URL webletsConfig) {
try {
InputStream in = webletsConfig.openStream();
try {
Digester digester = new Digester();
digester.setValidating(false);
digester.setClassLoader(getLoader());
digester.setEntityResolver(DisconnectedEntityResolver.sharedInstance());
digester.push(this);
digester.addFactoryCreate("weblets-config/weblet", WEBLET_CONFIG_FACTORY);
digester.addSetNext("weblets-config/weblet", "addWeblet", WebletConfigImpl.class.getName());
digester.addCallMethod("weblets-config/weblet/weblet-name",
"setWebletName", 0);
digester.addCallMethod("weblets-config/weblet/weblet-class",
"setWebletClass", 0);
digester.addCallMethod("weblets-config/weblet/weblet-version",
"setWebletVersion", 0);
digester.addCallMethod("weblets-config/weblet/init-param",
"addInitParam", 2);
digester.addCallParam("weblets-config/weblet/init-param/param-name", 0);
digester.addCallParam("weblets-config/weblet/init-param/param-value", 1);
digester.addCallMethod("weblets-config/weblet/mime-mapping",
"addMimeMapping", 2);
digester.addCallParam("weblets-config/weblet/mime-mapping/extension", 0);
digester.addCallParam("weblets-config/weblet/mime-mapping/mime-type", 1);
digester.addCallMethod("weblets-config/weblet-mapping",
"setWebletMapping", 2);
digester.addCallParam("weblets-config/weblet-mapping/weblet-name", 0);
digester.addCallParam("weblets-config/weblet-mapping/url-pattern", 1);
digester.parse(in);
}
finally {
in.close();
}
}
catch (IOException e) {
throw new WebletException(e);
}
catch (SAXException e) {
throw new WebletException(e);
}
}
public void addWeblet(WebletConfigImpl webletConfig) {
_webletConfigs.put(webletConfig.getWebletName(), webletConfig);
}
public void setWebletMapping(
String webletName,
String urlPattern) {
WebletConfig webletConfig = (WebletConfig) _webletConfigs.get(webletName);
if (webletConfig == null)
throw new WebletException("Weblet configuration not found: " + webletName);
Matcher matcher = _WEBLET_PATH_PATTERN.matcher(urlPattern);
if (matcher.matches()) {
String webletVersion = webletConfig.getWebletVersion();
String webletPath = matcher.group(1);
StringBuffer buffer = new StringBuffer();
//we have to prepend some optional mapping to cover
//the servlet case and some frameworks
//which add their own subcontext before triggering
//in framework servlets
buffer.append(".*(\\Q");
buffer.append(webletPath);
buffer.append("\\E)");
if (webletVersion != null) {
buffer.append("\\Q$");
buffer.append(webletVersion);
buffer.append("\\E");
}
buffer.append("(/.*)?");
;
_webletMappings.put(webletName, Pattern.compile(buffer.toString()));
_webletPaths.put(webletName, webletPath);
} else {
throw new IllegalArgumentException("Invalid weblet mapping: " + urlPattern);
}
}
public Map getRegisteredWeblets() {
return _weblets;
}
private Format _webletURLFormat;
private String _webletContextPath;
private Pattern _webletURLPattern;
private ServletContext _servletContext;
private Map _weblets = new HashMap();
private Map _webletPaths = new HashMap();
private Map _webletConfigs = new HashMap();
private Map _webletMappings = new LinkedHashMap();
private ObjectCreationFactory WEBLET_CONFIG_FACTORY = new ObjectCreationFactory() {
public Object createObject(Attributes attributes) throws Exception {
return new WebletConfigImpl(WebletContainerImpl.this);
}
public Digester getDigester() {
return digester;
}
public void setDigester(Digester digester) {
this.digester = digester;
}
private Digester digester;
};
/**
* Returns the mimetype of a file for the underlying hosting container
*/
public String getContainerMimeType(String pattern) {
return _servletContext.getMimeType(pattern);
}
static private final Pattern _WEBLET_PATH_PATTERN =
Pattern.compile("(/[^\\*]+)?/\\*");
}