blob: 736ba10164fd982e2ee0fcac65f37f1cf2a8e926 [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.models.impl;
import static org.apache.sling.api.scripting.SlingBindings.LOG;
import static org.apache.sling.api.scripting.SlingBindings.OUT;
import static org.apache.sling.api.scripting.SlingBindings.READER;
import static org.apache.sling.api.scripting.SlingBindings.REQUEST;
import static org.apache.sling.api.scripting.SlingBindings.RESOURCE;
import static org.apache.sling.api.scripting.SlingBindings.RESPONSE;
import static org.apache.sling.api.scripting.SlingBindings.SLING;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import javax.script.Bindings;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.scripting.LazyBindings;
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.api.scripting.SlingScriptHelper;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.models.factory.ExportException;
import org.apache.sling.models.factory.MissingExporterException;
import org.apache.sling.models.factory.ModelFactory;
import org.apache.sling.scripting.api.BindingsValuesProvidersByContext;
import org.apache.sling.scripting.core.ScriptHelper;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings("serial")
class ExportServlet extends SlingSafeMethodsServlet {
private static final String APPLICATION_JSON = "application/json";
private static final String UTF_8 = "UTF-8";
private final Logger logger;
private final String exporterName;
private final String registeredSelector;
private final BundleContext bundleContext;
private final ModelFactory modelFactory;
private final BindingsValuesProvidersByContext bindingsValuesProvidersByContext;
private final SlingModelsScriptEngineFactory scriptEngineFactory;
private final ExportedObjectAccessor accessor;
private final Map<String, String> baseOptions;
public ExportServlet(BundleContext bundleContext, ModelFactory modelFactory,
BindingsValuesProvidersByContext bindingsValuesProvidersByContext, SlingModelsScriptEngineFactory scriptFactory,
Class<?> annotatedClass, String registeredSelector, String exporterName, ExportedObjectAccessor accessor,
Map<String, String> baseOptions) {
this.bundleContext = bundleContext;
this.modelFactory = modelFactory;
this.bindingsValuesProvidersByContext = bindingsValuesProvidersByContext;
this.scriptEngineFactory = scriptFactory;
this.registeredSelector = registeredSelector;
this.exporterName = exporterName;
this.accessor = accessor;
this.baseOptions = baseOptions;
String loggerName = ExportServlet.class.getName() + "." + annotatedClass.getName();
this.logger = LoggerFactory.getLogger(loggerName);
}
@Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
throws ServletException, IOException {
String contentType = request.getResponseContentType();
response.setContentType(contentType);
// workaround for SLING-7344
if (APPLICATION_JSON.equals(contentType)) {
response.setCharacterEncoding(UTF_8);
}
Map<String, String> options = createOptionMap(request);
ScriptHelper scriptHelper = new ScriptHelper(bundleContext, null, request, response);
try {
addScriptBindings(scriptHelper, request, response);
String exported;
try {
exported = accessor.getExportedString(request, options, modelFactory, exporterName);
} catch (ExportException e) {
logger.error("Could not perform export with " + exporterName + " requested by model.", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
} catch (MissingExporterException e) {
logger.error("Could not get exporter " + exporterName + " requested by model.", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
if (exported == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
response.getWriter().write(exported);
} finally {
scriptHelper.cleanup();
}
}
private void addScriptBindings(SlingScriptHelper scriptHelper, SlingHttpServletRequest request, SlingHttpServletResponse response)
throws IOException {
Bindings bindings = new LazyBindings();
bindings.put(SLING, scriptHelper);
bindings.put(RESOURCE, request.getResource());
bindings.put(SlingModelsScriptEngineFactory.RESOLVER, request.getResource().getResourceResolver());
bindings.put(REQUEST, request);
bindings.put(RESPONSE, response);
try {
bindings.put(READER, request.getReader());
} catch (Exception e) {
bindings.put(READER, new BufferedReader(new StringReader("")));
}
bindings.put(OUT, response.getWriter());
bindings.put(LOG, logger);
scriptEngineFactory.invokeBindingsValuesProviders(bindingsValuesProvidersByContext, bindings);
SlingBindings slingBindings = new SlingBindings();
slingBindings.putAll(bindings);
request.setAttribute(SlingBindings.class.getName(), slingBindings);
}
private Map<String, String> createOptionMap(SlingHttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
String[] selectors = request.getRequestPathInfo().getSelectors();
Map<String, String> result = new HashMap<String, String>(baseOptions.size() + parameterMap.size() + selectors.length - 1);
result.putAll(baseOptions);
for (String selector : selectors) {
if (!selector.equals(registeredSelector)) {
result.put(selector, "true");
}
}
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
if (entry.getValue().length == 0) {
result.put(entry.getKey(), Boolean.TRUE.toString());
} else {
result.put(entry.getKey(), entry.getValue()[0]);
}
}
return result;
}
public interface ExportedObjectAccessor {
String getExportedString(SlingHttpServletRequest request, Map<String, String> options, ModelFactory modelFactory, String exporterName) throws ExportException, MissingExporterException;
}
public static final class ResourceAccessor implements ExportedObjectAccessor {
private final Class<?> adapterClass;
public ResourceAccessor(Class<?> adapterClass) {
this.adapterClass = adapterClass;
}
@Override
public String getExportedString(SlingHttpServletRequest request, Map<String, String> options, ModelFactory modelFactory, String exporterName) throws ExportException, MissingExporterException {
Object adapter = modelFactory.createModel(request.getResource(), adapterClass);
return modelFactory.exportModel(adapter, exporterName, String.class, options);
}
}
public static final class RequestAccessor implements ExportedObjectAccessor {
private final Class<?> adapterClass;
public RequestAccessor(Class<?> adapterClass) {
this.adapterClass = adapterClass;
}
@Override
public String getExportedString(SlingHttpServletRequest request, Map<String, String> options, ModelFactory modelFactory, String exporterName) throws ExportException, MissingExporterException {
Object adapter = modelFactory.createModel(request, adapterClass);
return modelFactory.exportModel(adapter, exporterName, String.class, options);
}
};
}