Java tutorial
/* * (C) Copyright 2012, IBM Corporation * * 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 com.ibm.jaggr.service.impl.transport; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; import java.text.MessageFormat; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import org.apache.commons.io.input.ReaderInputStream; import org.apache.wink.json4j.JSONException; import org.apache.wink.json4j.JSONObject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExecutableExtension; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; import org.osgi.framework.Bundle; import org.osgi.framework.BundleException; import org.osgi.framework.ServiceRegistration; import com.ibm.jaggr.service.BadRequestException; import com.ibm.jaggr.service.IAggregator; import com.ibm.jaggr.service.IAggregatorExtension; import com.ibm.jaggr.service.IShutdownListener; import com.ibm.jaggr.service.cachekeygenerator.ICacheKeyGenerator; import com.ibm.jaggr.service.config.IConfigModifier; import com.ibm.jaggr.service.readers.AggregationReader; import com.ibm.jaggr.service.resource.IResource; import com.ibm.jaggr.service.resource.IResourceVisitor; import com.ibm.jaggr.service.resource.IResourceVisitor.Resource; import com.ibm.jaggr.service.transport.IHttpTransport; import com.ibm.jaggr.service.util.Features; import com.ibm.jaggr.service.util.TypeUtil; /** * Implements common functionality useful for all Http Transport implementation * and defines abstract methods that subclasses need to implement */ public abstract class AbstractHttpTransport implements IHttpTransport, IExecutableExtension, IConfigModifier, IShutdownListener { private static final Logger log = Logger.getLogger(DojoHttpTransport.class.getName()); public static final String PATH_ATTRNAME = "path"; //$NON-NLS-1$ public static final String PATHS_PROPNAME = "paths"; //$NON-NLS-1$ public static final String REQUESTEDMODULES_REQPARAM = "modules"; //$NON-NLS-1$ public static final String REQUESTEDMODULESCOUNT_REQPARAM = "count"; //$NON-NLS-1$ public static final String REQUIRED_REQPARAM = "required"; //$NON-NLS-1$ public static final String FEATUREMAP_REQPARAM = "has"; //$NON-NLS-1$ public static final String FEATUREMAPHASH_REQPARAM = "hashash"; //$NON-NLS-1$ public static final String[] OPTIMIZATIONLEVEL_REQPARAMS = { "optimize", "opt" }; //$NON-NLS-1$ //$NON-NLS-2$ public static final String[] EXPANDREQUIRELISTS_REQPARAMS = { "expandRequire", "re" //$NON-NLS-1$ //$NON-NLS-2$ , "reqexp" // backwards compatibility, to be removed. //$NON-NLS-1$ }; public static final String[] EXPORTMODULENAMES_REQPARAMS = { "exportNames", "en" }; //$NON-NLS-1$ //$NON-NLS-2$ public static final String[] SHOWFILENAMES_REQPARAMS = { "showFilenames", "fn" }; //$NON-NLS-1$ //$NON-NLS-2$ public static final String[] NOCACHE_REQPARAMS = { "noCache", "nc" }; //$NON-NLS-1$ //$NON-NLS-2$ public static final String[] REQUESTEDLOCALES_REQPARAMS = { "locales", "locs" }; //$NON-NLS-1$ //$NON-NLS-2$ public static final String[] HASPLUGINBRANCHING_REQPARAMS = { "hasBranching", "hb" }; //$NON-NLS-1$ //$NON-NLS-2$ public static final String CONFIGVARNAME_REQPARAM = "configVarName"; //$NON-NLS-1$ public static final String LAYERCONTRIBUTIONSTATE_REQATTRNAME = AbstractHttpTransport.class.getName() + ".LayerContributionState"; //$NON-NLS-1$ /** A cache of folded module list strings to expanded file name lists. Used by LayerImpl cache */ private Map<String, Collection<String>> _encJsonMap = new ConcurrentHashMap<String, Collection<String>>(); private static Pattern DECODE_JSON = Pattern.compile("([!()|*])"); //$NON-NLS-1$ private static Pattern REQUOTE_JSON = Pattern.compile("([{,:])([^{},:\"]+)([},:])"); //$NON-NLS-1$ private String resourcePathId; private ServiceRegistration configModifierReg; private IAggregator aggregator = null; private List<String> extensionContributions = new LinkedList<String>(); /** * Returns the URI to the folder containing the javascript resources * for this transport. * * @return the combo resource URI */ protected abstract URI getComboUri(); /* (non-Javadoc) * @see com.ibm.jaggr.service.transport.IHttpTransport#decorateRequest(javax.servlet.http.HttpServletRequest, com.ibm.jaggr.service.IAggregator) */ @Override public void decorateRequest(HttpServletRequest request) throws IOException { // Get module list from request request.setAttribute(REQUESTEDMODULES_REQATTRNAME, getModuleListFromRequest(request)); // Get the feature list, if any request.setAttribute(FEATUREMAP_REQATTRNAME, getFeaturesFromRequest(request)); request.setAttribute(OPTIMIZATIONLEVEL_REQATTRNAME, getOptimizationLevelFromRequest(request)); String value = getParameter(request, EXPANDREQUIRELISTS_REQPARAMS); if ("log".equals(value)) { //$NON-NLS-1$ request.setAttribute(EXPANDREQLOGGING_REQATTRNAME, Boolean.TRUE); request.setAttribute(EXPANDREQUIRELISTS_REQATTRNAME, Boolean.TRUE); } else { request.setAttribute(EXPANDREQUIRELISTS_REQATTRNAME, TypeUtil.asBoolean(value)); } request.setAttribute(EXPORTMODULENAMES_REQATTRNAME, TypeUtil.asBoolean(getParameter(request, EXPORTMODULENAMES_REQPARAMS), true)); request.setAttribute(SHOWFILENAMES_REQATTRNAME, TypeUtil.asBoolean(getParameter(request, SHOWFILENAMES_REQPARAMS))); request.setAttribute(NOCACHE_REQATTRNAME, TypeUtil.asBoolean(getParameter(request, NOCACHE_REQPARAMS))); request.setAttribute(HASPLUGINBRANCHING_REQATTRNAME, TypeUtil.asBoolean(getParameter(request, HASPLUGINBRANCHING_REQPARAMS), true)); request.setAttribute(REQUESTEDLOCALES_REQATTRNAME, getRequestedLocales(request)); if (request.getParameter(CONFIGVARNAME_REQPARAM) != null) { request.setAttribute(CONFIGVARNAME_REQATTRNAME, request.getParameter(CONFIGVARNAME_REQPARAM)); } } /** * Unfolds the folded module list in the request into a {@code Collection<String>} * of module names. * * @param request the request object * @return the Collection of module names * @throws IOException */ protected Collection<String> getModuleListFromRequest(HttpServletRequest request) throws IOException { List<String> moduleList = new LinkedList<String>(); String moduleQueryArg = request.getParameter(REQUESTEDMODULES_REQPARAM); String countParam = request.getParameter(REQUESTEDMODULESCOUNT_REQPARAM); int count = 0; if (countParam != null) { count = Integer.parseInt(request.getParameter(REQUESTEDMODULESCOUNT_REQPARAM)); } if (moduleQueryArg == null) { return Collections.emptySet(); } try { moduleQueryArg = URLDecoder.decode(moduleQueryArg, "UTF-8"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new BadRequestException(e.getMessage()); } if (count > 0) { if (_encJsonMap.containsKey(moduleQueryArg)) moduleList.addAll(_encJsonMap.get(moduleQueryArg)); else { try { moduleList.addAll(Arrays.asList(unfoldModules(decodeModules(moduleQueryArg), count))); } catch (JSONException e) { throw new IOException(e); } // Save buildReader so we don't have to do this again. _encJsonMap.put(moduleQueryArg, moduleList); } } else { // Hand crafted URL; get module names from one or more module query args moduleList.addAll(Arrays.asList(moduleQueryArg.split("\\s*,\\s*", 0))); //$NON-NLS-1$ String required = request.getParameter(REQUIRED_REQPARAM); if (required != null) { Set<String> requiredSet = new HashSet<String>(Arrays.asList(required.split("\\s*,\\s*"))); //$NON-NLS-1$ request.setAttribute(REQUIRED_REQATTRNAME, Collections.unmodifiableSet(requiredSet)); } } return Collections.unmodifiableCollection(new ModuleList(moduleList, moduleQueryArg)); } /** * Returns the requested locales as a collection of locale strings * * @param request the request object * @return the locale strings */ protected Collection<String> getRequestedLocales(HttpServletRequest request) { String[] locales; String sLocales = getParameter(request, REQUESTEDLOCALES_REQPARAMS); if (sLocales != null) { locales = sLocales.split(","); //$NON-NLS-1$ } else { locales = new String[0]; } return Collections.unmodifiableCollection(Arrays.asList(locales)); } /** * Decode JSON object encoded for url transport. * Enforces ordering of object keys and mangles JSON format to prevent encoding of frequently used characters. * Assumes that keynames and values are valid filenames, and do not contain illegal filename chars. * See http://www.w3.org/Addressing/rfc1738.txt for small set of safe chars. */ protected JSONObject decodeModules(String encstr) throws IOException { StringBuffer json = new StringBuffer(encstr.length() * 2); Matcher m = DECODE_JSON.matcher(encstr); while (m.find()) { String match = m.group(1); if (match.equals("!")) //$NON-NLS-1$ m.appendReplacement(json, ":"); //$NON-NLS-1$ else if (match.equals("(")) //$NON-NLS-1$ m.appendReplacement(json, "{"); //$NON-NLS-1$ else if (match.equals(")")) //$NON-NLS-1$ m.appendReplacement(json, "}"); //$NON-NLS-1$ else if (match.equals("|")) //$NON-NLS-1$ m.appendReplacement(json, "!"); //$NON-NLS-1$ else if (match.equals("*")) //$NON-NLS-1$ m.appendReplacement(json, ","); //$NON-NLS-1$ else if (match.equals("<")) //$NON-NLS-1$ m.appendReplacement(json, "("); //$NON-NLS-1$ else if (match.equals(">")) //$NON-NLS-1$ m.appendReplacement(json, ")"); //$NON-NLS-1$ } m.appendTail(json); JSONObject decoded = null; String jsonstr = json.toString(); jsonstr = REQUOTE_JSON.matcher(jsonstr).replaceAll("$1\"$2\"$3"); // matches all keys //$NON-NLS-1$ jsonstr = REQUOTE_JSON.matcher(jsonstr).replaceAll("$1\"$2\"$3"); // matches all values //$NON-NLS-1$ try { decoded = new JSONObject(jsonstr); } catch (JSONException e) { throw new BadRequestException(e); } return decoded; } /** * Unfolds a folded module name list into a String array of unfolded names * <p> * The returned list must be sorted the same way it was requested * ordering modules in the same way as in the companion js extension to amd loader * order provided in the folded module leaf * * @param modules The folded module name list * @param count The count of modules in the list * @return The unfolded module name list */ protected String[] unfoldModules(JSONObject modules, int count) throws IOException, JSONException { String[] ret = new String[count]; Iterator<?> it = modules.keys(); while (it.hasNext()) { String key = (String) it.next(); unfoldModulesHelper(modules.get(key), key, ret); } return ret; } /** * Helper routine to unfold folded module names * * @param obj * @param path * @param modules */ protected void unfoldModulesHelper(Object obj, String path, String[] modules) throws IOException, JSONException { if (obj instanceof JSONObject) { JSONObject jsonobj = (JSONObject) obj; Iterator<?> it = jsonobj.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); String newpath = path + "/" + key; //$NON-NLS-1$ unfoldModulesHelper(jsonobj.get(key), newpath, modules); } } else if (obj instanceof String) { String[] values = ((String) obj).split("-"); //$NON-NLS-1$ try { modules[Integer.parseInt(values[0])] = values.length > 1 ? (values[1] + "!" + path) : path; //$NON-NLS-1$ } catch (Exception e) { if (log.isLoggable(Level.SEVERE)) log.log(Level.SEVERE, e.getMessage(), e); } } else { throw new BadRequestException(); } } /** * Returns a map containing the has-condition/value pairs specified in the request * * @param request The http request object * @return The map containing the has-condition/value pairs. * @throws */ protected static Features getFeaturesFromRequest(HttpServletRequest request) throws IOException { Features features = new Features(); String has = getHasConditionsFromRequest(request); if (has != null) { if (log.isLoggable(Level.FINEST)) log.finest("Adding has parameters from request: " + has); //$NON-NLS-1$ for (String s : has.split(";")) { //$NON-NLS-1$ boolean value = true; if (s.startsWith("!")) { //$NON-NLS-1$ s = s.substring(1); value = false; } features.put(s, value); } if (log.isLoggable(Level.FINEST)) log.finest("features = " + features.toString()); //$NON-NLS-1$ } return features.unmodifiableFeatures(); } /** * This method checks the request for the has conditions which may either be contained in URL * query arguments or in a cookie sent from the client. * * @return The has conditions from the request. * @throws UnsupportedEncodingException */ protected static String getHasConditionsFromRequest(HttpServletRequest request) throws IOException { String ret = null; if (request.getParameter(FEATUREMAPHASH_REQPARAM) != null) { // The cookie called 'has' contains the has conditions Cookie[] cookies = request.getCookies(); if (cookies != null) { for (int i = 0; ret == null && i < cookies.length; i++) { Cookie cookie = cookies[i]; if (cookie.getName().equals(FEATUREMAP_REQPARAM) && cookie.getValue() != null) { ret = URLDecoder.decode(cookie.getValue(), "US-ASCII"); //$NON-NLS-1$ break; } } } if (ret == null) { if (log.isLoggable(Level.WARNING)) { StringBuffer url = request.getRequestURL(); if (url != null) { // might be null if using mock request for unit testing url.append("?").append(request.getQueryString()).toString(); //$NON-NLS-1$ log.warning(MessageFormat.format(Messages.AbstractHttpTransport_0, new Object[] { url, request.getHeader("User-Agent") })); //$NON-NLS-1$ } } } } else ret = request.getParameter(FEATUREMAP_REQPARAM); return ret; } /** * Returns the value of the requested parameter from the request, or null * * @param request * the request object * @param aliases * array of query arg names by which the request may be specified * @return the value of the param, or null if it is not specified under the * specified names */ protected static String getParameter(HttpServletRequest request, String[] aliases) { Map<String, String[]> params = request.getParameterMap(); String result = null; for (Map.Entry<String, String[]> entry : params.entrySet()) { String name = entry.getKey(); for (String alias : aliases) { if (alias.equalsIgnoreCase(name)) { String[] values = entry.getValue(); result = values[values.length - 1]; // return last value in array } } } return result; } /** * Returns the requested optimization level from the request. * * @param request the request object * @return the optimization level specified in the request */ protected OptimizationLevel getOptimizationLevelFromRequest(HttpServletRequest request) { // Get the optimization level specified in the request and set the ComilationLevel String optimize = getParameter(request, OPTIMIZATIONLEVEL_REQPARAMS); OptimizationLevel level = OptimizationLevel.SIMPLE; if (optimize != null && !optimize.equals("")) { //$NON-NLS-1$ if (optimize.equalsIgnoreCase("whitespace")) //$NON-NLS-1$ level = OptimizationLevel.WHITESPACE; else if (optimize.equalsIgnoreCase("advanced")) //$NON-NLS-1$ level = OptimizationLevel.ADVANCED; else if (optimize.equalsIgnoreCase("none")) //$NON-NLS-1$ level = OptimizationLevel.NONE; } return level; } /** * Extends the default implementation of {@link LinkedList} to override * the {@code toString) method (used in generating layer cache keys) so * that we can return the folded module name list (allowing for more * compact cache keys). */ protected static class ModuleList extends LinkedList<String> { private static final long serialVersionUID = 1520863743688358581L; private String stringized; ModuleList(List<String> source, String stringized) { super(source); this.stringized = stringized; } /* (non-Javadoc) * @see java.util.AbstractCollection#toString() */ @Override public String toString() { return (stringized != null) ? stringized : super.toString(); } } /* (non-Javadoc) * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object) */ @Override public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException { // Make sure the contributing bundle is started Bundle contributingBundle = Platform.getBundle(config.getNamespaceIdentifier()); if (contributingBundle.getState() != Bundle.ACTIVE) { try { contributingBundle.start(); } catch (BundleException e) { throw new CoreException( new Status(Status.ERROR, config.getNamespaceIdentifier(), e.getMessage(), e)); } } // Get the resource (combo) path name from the this extension's // path attribute resourcePathId = config.getAttribute(PATH_ATTRNAME); if (resourcePathId == null) { throw new CoreException(new Status(Status.ERROR, config.getNamespaceIdentifier(), MessageFormat.format(Messages.AbstractHttpTransport_1, new Object[] { config.getDeclaringExtension().getUniqueIdentifier() }))); } } /* (non-Javadoc) * @see com.ibm.jaggr.service.config.IConfigModifier#modifyConfig(com.ibm.jaggr.service.IAggregator, java.util.Map) */ @Override public void modifyConfig(IAggregator aggregator, Scriptable config) { // The server-side AMD config has been updated. Add an entry to the // {@code paths} property to map the resource (combo) path to the // location of the combo resources on the server. Context context = Context.enter(); try { Object pathsObj = config.get(PATHS_PROPNAME, config); if (pathsObj == Scriptable.NOT_FOUND) { // If not present, add it. config.put("paths", config, context.newObject(config)); //$NON-NLS-1$ pathsObj = (Scriptable) config.get(PATHS_PROPNAME, config); } ((Scriptable) pathsObj).put(getResourcePathId(), (Scriptable) pathsObj, getComboUri().toString()); } finally { Context.exit(); } } /* (non-Javadoc) * @see com.ibm.jaggr.service.IExtensionInitializer#initialize(com.ibm.jaggr.service.IAggregator, com.ibm.jaggr.service.IAggregatorExtension, com.ibm.jaggr.service.IExtensionInitializer.IExtensionRegistrar) */ @Override public void initialize(IAggregator aggregator, IAggregatorExtension extension, IExtensionRegistrar reg) { this.aggregator = aggregator; // register a config listener so that we get notified of changes to // the server-side AMD config file. String name = aggregator.getName(); Properties dict = new Properties(); dict.put("name", name); //$NON-NLS-1$ configModifierReg = aggregator.getBundleContext().registerService(IConfigModifier.class.getName(), this, dict); } /* (non-Javadoc) * @see com.ibm.jaggr.service.IShutdownListener#shutdown(com.ibm.jaggr.service.IAggregator) */ @Override public void shutdown(IAggregator aggregator) { // unregister our config modifier if (configModifierReg != null) { configModifierReg.unregister(); } } /** * Returns the aggregator instance that created this transport * * @return the aggregator */ protected IAggregator getAggregator() { return aggregator; } /** * Returns the resource (combo) path id specified by the transport's * plugin extension {@code path} attribute * * @return the resource path id */ protected String getResourcePathId() { return resourcePathId; } /* (non-Javadoc) * @see com.ibm.jaggr.service.transport.IHttpTransport#contributeLoaderExtensionJavaScript(java.lang.String) */ @Override public void contributeLoaderExtensionJavaScript(String contribution) { extensionContributions.add(contribution); } /* (non-Javadoc) * @see com.ibm.jaggr.service.transport.IHttpTransport#getLayerContribution(javax.servlet.http.HttpServletRequest, com.ibm.jaggr.service.transport.IHttpTransport.LayerContributionType, java.lang.String) */ @Override public abstract String getLayerContribution(HttpServletRequest request, LayerContributionType type, Object arg); /* (non-Javadoc) * @see com.ibm.jaggr.service.transport.IHttpTransport#isServerExpandable(javax.servlet.http.HttpServletRequest, java.lang.String) */ public abstract boolean isServerExpandable(HttpServletRequest request, String mid); /* (non-Javadoc) * @see com.ibm.jaggr.service.transport.IHttpTransport#getCacheKeyGenerators() */ @Override public abstract List<ICacheKeyGenerator> getCacheKeyGenerators(); /** * Returns the extension contributions that have been registered with this * transport * * @return the extension contributions */ protected List<String> getExtensionContributions() { return extensionContributions; } /** * Returns the dynamic portion of the loader extension javascript for this * transport. This includes all registered extension contributions. * * @return the dynamic portion of the loader extension javascript */ protected String getDynamicLoaderExtensionJavaScript() { StringBuffer sb = new StringBuffer(); for (String contribution : getExtensionContributions()) { sb.append(contribution).append("\r\n"); //$NON-NLS-1$ } String cacheBust = aggregator.getConfig().getCacheBust(); String optionsCb = aggregator.getOptions().getCacheBust(); if (optionsCb != null && optionsCb.length() > 0) { cacheBust = (cacheBust != null && cacheBust.length() > 0) ? (cacheBust + "-" + optionsCb) : optionsCb; //$NON-NLS-1$ } if (cacheBust != null && cacheBust.length() > 0) { sb.append("if (!combo.cacheBust){combo.cacheBust = '") //$NON-NLS-1$ .append(cacheBust).append("';}\r\n"); //$NON-NLS-1$ } return sb.toString(); } /** * Validate the {@link LayerContributionState} and argument type specified * in a call to * {@link #getLayerContribution(HttpServletRequest, com.ibm.jaggr.service.transport.IHttpTransport.LayerContributionType, Object)} * * @param request * The http request object * @param type * The layer contribution (see * {@link IHttpTransport.LayerContributionType}) * @param arg * The argument value */ protected void validateLayerContributionState(HttpServletRequest request, LayerContributionType type, Object arg) { LayerContributionType previousType = (LayerContributionType) request .getAttribute(LAYERCONTRIBUTIONSTATE_REQATTRNAME); switch (type) { case BEGIN_RESPONSE: if (previousType != null) { throw new IllegalStateException(); } break; case BEGIN_MODULES: if (previousType != LayerContributionType.BEGIN_RESPONSE) { throw new IllegalStateException(); } break; case BEFORE_FIRST_MODULE: if (previousType != LayerContributionType.BEGIN_MODULES || !(arg instanceof String)) { throw new IllegalStateException(); } break; case BEFORE_SUBSEQUENT_MODULE: if (previousType != LayerContributionType.AFTER_MODULE || !(arg instanceof String)) { throw new IllegalStateException(); } break; case AFTER_MODULE: if (previousType != LayerContributionType.BEFORE_FIRST_MODULE && previousType != LayerContributionType.BEFORE_SUBSEQUENT_MODULE || !(arg instanceof String)) { throw new IllegalStateException(); } break; case END_MODULES: if (previousType != LayerContributionType.AFTER_MODULE) { throw new IllegalStateException(); } break; case BEGIN_REQUIRED_MODULES: if (previousType != LayerContributionType.BEGIN_RESPONSE && previousType != LayerContributionType.END_MODULES || !(arg instanceof Set)) { throw new IllegalStateException(); } break; case BEFORE_FIRST_REQUIRED_MODULE: if (previousType != LayerContributionType.BEGIN_REQUIRED_MODULES || !(arg instanceof String)) { throw new IllegalStateException(); } break; case BEFORE_SUBSEQUENT_REQUIRED_MODULE: if (previousType != LayerContributionType.AFTER_REQUIRED_MODULE || !(arg instanceof String)) { throw new IllegalStateException(); } break; case AFTER_REQUIRED_MODULE: if (previousType != LayerContributionType.BEFORE_FIRST_REQUIRED_MODULE && previousType != LayerContributionType.BEFORE_SUBSEQUENT_REQUIRED_MODULE || !(arg instanceof String)) { throw new IllegalStateException(); } break; case END_REQUIRED_MODULES: if (previousType != LayerContributionType.AFTER_REQUIRED_MODULE || !(arg instanceof Set)) { throw new IllegalStateException(); } break; case END_RESPONSE: if (previousType != LayerContributionType.END_MODULES && previousType != LayerContributionType.END_REQUIRED_MODULES) { throw new IllegalStateException(); } break; } request.setAttribute(LAYERCONTRIBUTIONSTATE_REQATTRNAME, type); } /** * Implementation of an {@link IResource} that aggregates the various * sources (dynamic and static content) of the loader extension * javascript for this transport */ protected class LoaderExtensionResource implements IResource, IResourceVisitor.Resource { IResource res; public LoaderExtensionResource(IResource res) { this.res = res; } /* (non-Javadoc) * @see com.ibm.jaggr.service.resource.IResource#getURI() */ @Override public URI getURI() { return res.getURI(); } /* (non-Javadoc) * @see com.ibm.jaggr.service.resource.IResource#exists() */ @Override public boolean exists() { return res.exists(); } /* (non-Javadoc) * @see com.ibm.jaggr.service.resource.IResource#lastModified() */ @Override public long lastModified() { return res.lastModified(); } /* (non-Javadoc) * @see com.ibm.jaggr.service.resource.IResource#getInputStream() */ @Override public InputStream getInputStream() throws IOException { return new ReaderInputStream(getReader(), "UTF-8"); //$NON-NLS-1$ } /* (non-Javadoc) * @see com.ibm.jaggr.service.resource.IResource#getReader() */ @Override public Reader getReader() throws IOException { // Return an aggregation reader for the loader extension javascript return new AggregationReader("(function(){", //$NON-NLS-1$ res.getReader(), getDynamicLoaderExtensionJavaScript(), "})();"); //$NON-NLS-1$ } /* (non-Javadoc) * @see com.ibm.jaggr.service.resource.IResource#walkTree(com.ibm.jaggr.service.resource.IResourceVisitor) */ @Override public void walkTree(IResourceVisitor visitor) throws IOException { } /* (non-Javadoc) * @see com.ibm.jaggr.service.resource.IResource#asVisitorResource() */ @Override public Resource asVisitorResource() throws IOException { return this; } /* (non-Javadoc) * @see com.ibm.jaggr.service.resource.IResourceVisitor.Resource#isFolder() */ @Override public boolean isFolder() { return false; } /* (non-Javadoc) * @see com.ibm.jaggr.service.resource.IResource#resolve(java.lang.String) */ @Override public IResource resolve(String relative) { throw new UnsupportedOperationException(); } } }