blob: fa7024a6ca9959b02b91a5eb9712f723cd33211c [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.core;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.SlingIOException;
import org.apache.sling.api.SlingServletException;
import org.apache.sling.api.request.RequestDispatcherOptions;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.scripting.InvalidServiceFilterSyntaxException;
import org.apache.sling.api.scripting.SlingScript;
import org.apache.sling.api.scripting.SlingScriptHelper;
import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;
import org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper;
import org.apache.sling.scripting.core.impl.helper.OnDemandReaderRequest;
import org.apache.sling.scripting.core.impl.helper.OnDemandWriterResponse;
import org.jetbrains.annotations.NotNull;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.slf4j.LoggerFactory;
/**
* Simple script helper providing access to the (wrapped) response, the
* on-demand writer and a simple API for request inclusion. Instances of this
* class are made available to the scripts as the global <code>sling</code>
* variable.
*
* Client code using this object should take care to call {@link #cleanup()}
* when the object is not used anymore!
*/
public class ScriptHelper implements SlingScriptHelper {
/** The corresponding script. */
private final SlingScript script;
/** The current request or <code>null</code>. */
private final SlingHttpServletRequest request;
/** The current response or <code>null</code>. */
private final SlingHttpServletResponse response;
/** The bundle context. */
protected final BundleContext bundleContext;
/**
* The list of references - we don't need to synchronize this as we are
* running in one single request.
*/
protected List<ServiceReference<?>> references;
/** A map of found services. */
protected Map<String, Object> services;
public ScriptHelper(final BundleContext ctx, final SlingScript script) {
if (ctx == null ) {
throw new IllegalArgumentException("Bundle context must not be null.");
}
this.request = null;
this.response = null;
this.script = script;
this.bundleContext = ctx;
}
public ScriptHelper(final BundleContext ctx,
final SlingScript script,
final SlingHttpServletRequest request,
final SlingHttpServletResponse response) {
if (ctx == null ) {
throw new IllegalArgumentException("Bundle context must not be null.");
}
this.script = script;
this.request = wrapIfNeeded(request);
this.response = wrapIfNeeded(response);
this.bundleContext = ctx;
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#getScript()
*/
public SlingScript getScript() {
return script;
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#getRequest()
*/
public SlingHttpServletRequest getRequest() {
return request;
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#getResponse()
*/
public SlingHttpServletResponse getResponse() {
return response;
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#include(java.lang.String)
*/
public void include(String path) {
include(path, (RequestDispatcherOptions) null);
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#include(java.lang.String, java.lang.String)
*/
public void include(String path, String options) {
include(path, new RequestDispatcherOptions(options));
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#include(java.lang.String, org.apache.sling.api.request.RequestDispatcherOptions)
*/
public void include(String path, RequestDispatcherOptions options) {
final RequestDispatcher dispatcher = getRequest().getRequestDispatcher(
path, options);
if (dispatcher != null) {
try {
dispatcher.include(getRequest(), getResponse());
} catch (IOException ioe) {
throw new SlingIOException(ioe);
} catch (ServletException se) {
throw new SlingServletException(se);
}
}
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#forward(java.lang.String)
*/
public void forward(String path) {
forward(path, (RequestDispatcherOptions) null);
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#forward(java.lang.String, java.lang.String)
*/
public void forward(String path, String options) {
forward(path, new RequestDispatcherOptions(options));
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#forward(java.lang.String, org.apache.sling.api.request.RequestDispatcherOptions)
*/
public void forward(String path, RequestDispatcherOptions options) {
final RequestDispatcher dispatcher = getRequest().getRequestDispatcher(
path, options);
if (dispatcher != null) {
try {
dispatcher.forward(getRequest(), getResponse());
} catch (IOException ioe) {
throw new SlingIOException(ioe);
} catch (ServletException se) {
throw new SlingServletException(se);
}
}
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#dispose()
* @deprecated This method is deprecated and should never be called by clients!
*/
@Deprecated
public void dispose() {
LoggerFactory.getLogger(this.getClass()).error("ScriptHelper#dispose has been called. This method is deprecated and should never be called by clients!");
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#getService(java.lang.Class)
*/
@SuppressWarnings("unchecked")
public <T> T getService(Class<T> type) {
T service = (this.services == null ? null : (T) this.services.get(type.getName()));
if (service == null) {
final ServiceReference<T> ref = this.bundleContext.getServiceReference(type);
if (ref != null) {
service = this.bundleContext.getService(ref);
if ( service != null ) {
if ( this.services == null ) {
this.services = new HashMap<>();
}
if ( this.references == null ) {
this.references = new ArrayList<>();
}
this.references.add(ref);
this.services.put(type.getName(), service);
}
}
}
return service;
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#getServices(java.lang.Class, java.lang.String)
*/
@SuppressWarnings("unchecked")
public <T> T[] getServices(
Class<T> serviceType, String filter)
throws InvalidServiceFilterSyntaxException {
try {
Collection<ServiceReference<T>> refsCollection = this.bundleContext.getServiceReferences(
serviceType, filter);
T[] result = null;
if (refsCollection != null) {
// sort by service ranking (lowest first) (see ServiceReference#compareTo(Object))
List<ServiceReference<T>> refsList = new ArrayList<>(refsCollection);
Collections.sort(refsList);
// get the highest ranking first
Collections.reverse(refsList);
final List<T> objects = new ArrayList<>();
for (ServiceReference<T> reference : refsList) {
final T service = this.bundleContext.getService(reference);
if (service != null) {
if ( this.references == null ) {
this.references = new ArrayList<>();
}
this.references.add(reference);
objects.add(service);
}
}
if (!objects.isEmpty()) {
T[] srv = (T[]) Array.newInstance(serviceType, objects.size());
result = objects.toArray(srv);
}
}
return result;
} catch (InvalidSyntaxException ise) {
throw new InvalidServiceFilterSyntaxException(filter,
"Invalid filter syntax", ise);
}
}
/**
* Clean up this instance.
*/
public void cleanup() {
if ( this.references != null ) {
final Iterator<ServiceReference<?>> i = this.references.iterator();
while (i.hasNext()) {
final ServiceReference<?> ref = i.next();
this.bundleContext.ungetService(ref);
}
this.references.clear();
}
if ( this.services != null ) {
this.services.clear();
}
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#forward(org.apache.sling.api.resource.Resource)
*/
public void forward(Resource resource) {
forward(resource, (RequestDispatcherOptions) null);
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#forward(org.apache.sling.api.resource.Resource, java.lang.String)
*/
public void forward(Resource resource, String options) {
forward(resource, new RequestDispatcherOptions(options));
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#forward(org.apache.sling.api.resource.Resource, org.apache.sling.api.request.RequestDispatcherOptions)
*/
public void forward(Resource resource, RequestDispatcherOptions options) {
final RequestDispatcher dispatcher = getRequest().getRequestDispatcher(
resource, options);
if (dispatcher != null) {
try {
dispatcher.forward(getRequest(), getResponse());
} catch (IOException ioe) {
throw new SlingIOException(ioe);
} catch (ServletException se) {
throw new SlingServletException(se);
}
}
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#forward(org.apache.sling.api.resource.Resource)
*/
public void include(Resource resource) {
include(resource, (RequestDispatcherOptions) null);
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#include(org.apache.sling.api.resource.Resource, java.lang.String)
*/
public void include(Resource resource, String options) {
include(resource, new RequestDispatcherOptions(options));
}
/**
* @see org.apache.sling.api.scripting.SlingScriptHelper#include(org.apache.sling.api.resource.Resource, org.apache.sling.api.request.RequestDispatcherOptions)
*/
public void include(Resource resource, RequestDispatcherOptions options) {
final RequestDispatcher dispatcher = getRequest().getRequestDispatcher(
resource, options);
if (dispatcher != null) {
try {
dispatcher.include(getRequest(), getResponse());
} catch (IOException ioe) {
throw new SlingIOException(ioe);
} catch (ServletException se) {
throw new SlingServletException(se);
}
}
}
private SlingHttpServletRequest wrapIfNeeded(@NotNull SlingHttpServletRequest request) {
SlingHttpServletRequest initialRequest = request;
while (request instanceof SlingHttpServletRequestWrapper) {
if (request instanceof OnDemandReaderRequest) {
return initialRequest;
}
request = ((SlingHttpServletRequestWrapper) request).getSlingRequest();
}
return new OnDemandReaderRequest(initialRequest);
}
private SlingHttpServletResponse wrapIfNeeded(@NotNull SlingHttpServletResponse response) {
SlingHttpServletResponse initialResponse = response;
while (response instanceof SlingHttpServletResponseWrapper) {
if (response instanceof OnDemandWriterResponse) {
return initialResponse;
}
response = ((SlingHttpServletResponseWrapper) response).getSlingResponse();
}
return new OnDemandWriterResponse(initialResponse);
}
}