blob: b9ae37b593198a393cb7a0c45d65f17f337e060c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.sling.scripting.freemarker.internal;
import java.io.IOException;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.utility.DeepUnwrap;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.request.RequestDispatcherOptions;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.resource.SyntheticResource;
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.scripting.core.servlet.CaptureResponseWrapper;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(
service = {
TemplateModel.class
},
property = {
"namespace=sling",
"name=include"
}
)
public class IncludeDirective implements TemplateDirectiveModel {
private static final String ADD_SELECTORS_PARAMETER_NAME = "addSelectors";
private static final String REPLACE_SELECTORS_PARAMETER_NAME = "replaceSelectors";
private static final String REPLACE_SUFFIX_PARAMETER_NAME = "replaceSuffix";
private static final String RESOURCE_TYPE_PARAMETER_NAME = "resourceType";
private final Logger logger = LoggerFactory.getLogger(IncludeDirective.class);
public IncludeDirective() { //
}
@Override
public void execute(final Environment environment, final Map parameters, final TemplateModel[] loopVars, final TemplateDirectiveBody body) throws TemplateException, IOException {
final SlingHttpServletRequest slingHttpServletRequest = (SlingHttpServletRequest) DeepUnwrap.unwrap(environment.getVariable(SlingBindings.REQUEST));
if (slingHttpServletRequest == null) {
throw new TemplateException("request is null", environment);
}
final SlingHttpServletResponse slingHttpServletResponse = (SlingHttpServletResponse) DeepUnwrap.unwrap(environment.getVariable(SlingBindings.RESPONSE));
if (slingHttpServletResponse == null) {
throw new TemplateException("response is null", environment);
}
final TemplateModel templateModel = (TemplateModel) parameters.get("include");
if (templateModel == null) {
throw new TemplateException("include is null", environment);
}
final Object include = DeepUnwrap.unwrap(templateModel);
if (include == null) {
throw new TemplateException("unwrapping include failed", environment);
}
String path = null;
if (include instanceof String) {
path = (String) include;
}
Resource resource = null;
if (include instanceof Resource) {
resource = (Resource) include;
}
if (path == null && resource == null) {
throw new TemplateException("path and resource are null", environment);
}
// request dispatcher options
final RequestDispatcherOptions requestDispatcherOptions = prepareRequestDispatcherOptions(parameters);
// dispatch
final String content = dispatch(resource, path, slingHttpServletRequest, slingHttpServletResponse, requestDispatcherOptions);
if (content == null) {
throw new TemplateException("dispatching request failed, content is null", environment);
}
environment.getOut().write(content);
}
protected <T> T unwrapParameter(final String name, final Map params, final Class<T> type) throws TemplateModelException {
final Object parameter = params.get(name);
final TemplateModel templateModel = (TemplateModel) parameter;
return (T) DeepUnwrap.unwrap(templateModel);
}
protected RequestDispatcherOptions prepareRequestDispatcherOptions(final Map params) throws TemplateModelException {
final String resourceType = unwrapParameter(RESOURCE_TYPE_PARAMETER_NAME, params, String.class);
final String replaceSelectors = unwrapParameter(REPLACE_SELECTORS_PARAMETER_NAME, params, String.class);
final String addSelectors = unwrapParameter(ADD_SELECTORS_PARAMETER_NAME, params, String.class);
final String replaceSuffix = unwrapParameter(REPLACE_SUFFIX_PARAMETER_NAME, params, String.class);
final RequestDispatcherOptions options = new RequestDispatcherOptions();
options.setForceResourceType(resourceType);
options.setReplaceSelectors(replaceSelectors);
options.setAddSelectors(addSelectors);
options.setReplaceSuffix(replaceSuffix);
return options;
}
/**
* @param resource the resource to include
* @param path the path to include
* @param slingHttpServletRequest the current request
* @param slingHttpServletResponse the current response
* @param requestDispatcherOptions the options for the request dispatcher
* @return the character response from the include call to request dispatcher
* @see "org.apache.sling.scripting.jsp.taglib.IncludeTagHandler"
*/
protected String dispatch(Resource resource, String path, final SlingHttpServletRequest slingHttpServletRequest, final SlingHttpServletResponse slingHttpServletResponse, final RequestDispatcherOptions requestDispatcherOptions) {
// ensure the path (if set) is absolute and normalized
if (path != null) {
if (!path.startsWith("/")) {
path = slingHttpServletRequest.getResource().getPath() + "/" + path;
}
path = ResourceUtil.normalize(path);
}
// check the resource
if (resource == null) {
if (path == null) {
// neither resource nor path is defined, use current resource
resource = slingHttpServletRequest.getResource();
} else {
// check whether the path (would) resolve, else SyntheticRes.
final String resourceType = requestDispatcherOptions.getForceResourceType();
final Resource tmp = slingHttpServletRequest.getResourceResolver().resolve(path);
if (tmp == null && resourceType != null) {
resource = new SyntheticResource(slingHttpServletRequest.getResourceResolver(), path, resourceType); // TODO DispatcherSyntheticResource?
// remove resource type overwrite as synthetic resource is correctly typed as requested
requestDispatcherOptions.remove(RequestDispatcherOptions.OPT_FORCE_RESOURCE_TYPE);
}
}
}
try {
// create a dispatcher for the resource or path
final RequestDispatcher dispatcher;
if (resource != null) {
dispatcher = slingHttpServletRequest.getRequestDispatcher(resource, requestDispatcherOptions);
} else {
dispatcher = slingHttpServletRequest.getRequestDispatcher(path, requestDispatcherOptions);
}
if (dispatcher != null) {
try {
final CaptureResponseWrapper wrapper = new CaptureResponseWrapper(slingHttpServletResponse);
dispatcher.include(slingHttpServletRequest, wrapper);
if (!wrapper.isBinaryResponse()) {
return wrapper.getCapturedCharacterResponse();
}
} catch (ServletException e) {
logger.error(e.getMessage(), e);
}
} else {
logger.error("no request dispatcher: unable to include {}/'{}'", resource, path);
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return null;
}
}