| /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| ~ 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.jsp; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import javax.naming.NamingException; |
| import javax.servlet.ServletException; |
| |
| import org.apache.sling.api.SlingException; |
| import org.apache.sling.api.SlingServletException; |
| import org.apache.sling.api.scripting.SlingBindings; |
| import org.apache.sling.commons.compiler.source.JavaEscapeHelper; |
| import org.apache.sling.scripting.jsp.jasper.Options; |
| import org.apache.sling.scripting.jsp.jasper.compiler.JspRuntimeContext; |
| import org.apache.sling.scripting.jsp.jasper.runtime.AnnotationProcessor; |
| import org.apache.sling.scripting.jsp.jasper.runtime.HttpJspBase; |
| import org.apache.sling.scripting.jsp.jasper.servlet.JspServletWrapper; |
| import org.apache.sling.scripting.spi.bundle.BundledRenderUnit; |
| import org.osgi.framework.Bundle; |
| |
| public class PrecompiledJSPRunner { |
| |
| private final Options options; |
| |
| private final ConcurrentHashMap<HttpJspBase, JspHolder> holders = new ConcurrentHashMap<>(); |
| |
| public PrecompiledJSPRunner(final Options options) { |
| this.options = options; |
| } |
| |
| boolean callPrecompiledJSP(JspRuntimeContext runtimeContext, JspRuntimeContext.JspFactoryHandler jspFactoryHandler, JspServletConfig jspServletConfig, |
| SlingBindings bindings) { |
| boolean found = false; |
| final BundledRenderUnit bundledRenderUnit = (BundledRenderUnit) bindings.get(BundledRenderUnit.VARIABLE); |
| if (bundledRenderUnit != null && bundledRenderUnit.getUnit() instanceof HttpJspBase) { |
| found = true; |
| final HttpJspBase jsp = (HttpJspBase) bundledRenderUnit.getUnit(); |
| final JspHolder holder = holders.computeIfAbsent(jsp, key -> new JspHolder()); |
| if (holder.wrapper == null) { |
| synchronized (holder) { |
| if (holder.wrapper == null ) { |
| try { |
| final PrecompiledServletConfig servletConfig = new PrecompiledServletConfig(jspServletConfig, bundledRenderUnit); |
| final AnnotationProcessor annotationProcessor = (AnnotationProcessor) jspServletConfig.getServletContext() |
| .getAttribute(AnnotationProcessor.class.getName()); |
| if (annotationProcessor != null) { |
| annotationProcessor.processAnnotations(jsp); |
| annotationProcessor.postConstruct(jsp); |
| } |
| |
| final JspServletWrapper wrapper = new JspServletWrapper(servletConfig, this.options, bundledRenderUnit.getPath(), false, runtimeContext, jsp); |
| jsp.init(servletConfig); |
| |
| holder.wrapper = wrapper; |
| } catch ( final ServletException se ) { |
| throw new SlingServletException(se); |
| } catch (IllegalAccessException | InvocationTargetException | NamingException e) { |
| throw new SlingException("Unable to process annotations for servlet " + jsp.getClass().getName() + ".", e); |
| } catch (NoClassDefFoundError ignored) { |
| // wave your hands like we don't care - we're missing support for precompiled JSPs |
| } |
| } |
| } |
| } |
| |
| holder.wrapper.service(bindings); |
| } |
| return found; |
| } |
| |
| public void cleanup() { |
| final Set<JspHolder> holders = new HashSet<>(this.holders.values()); |
| this.holders.clear(); |
| try { |
| for(final JspHolder h : holders) { |
| if ( h.wrapper != null ) { |
| h.wrapper.destroy(true); |
| } |
| } |
| } catch ( final Throwable t) { |
| // we ignore this exception as this might be called during shutdown |
| } |
| } |
| |
| private static class PrecompiledServletConfig extends JspServletConfig { |
| |
| private final BundledRenderUnit bundledRenderUnit; |
| private String servletName; |
| |
| PrecompiledServletConfig(JspServletConfig jspServletConfig, BundledRenderUnit bundledRenderUnit) { |
| super(jspServletConfig.getServletContext(), new HashMap<>(jspServletConfig.getProperties())); |
| this.bundledRenderUnit = bundledRenderUnit; |
| } |
| |
| @Override |
| public String getServletName() { |
| if (servletName == null && bundledRenderUnit.getUnit() != null) { |
| Bundle bundle = bundledRenderUnit.getBundle(); |
| Object jsp = bundledRenderUnit.getUnit(); |
| String originalName = |
| JavaEscapeHelper.unescapeAll(jsp.getClass().getPackage().getName()) + "/" + JavaEscapeHelper.unescapeAll(jsp.getClass().getSimpleName()); |
| servletName = bundle.getSymbolicName() + ": " + originalName; |
| } |
| return servletName; |
| } |
| } |
| |
| public static final class JspHolder { |
| |
| public volatile JspServletWrapper wrapper; |
| |
| } |
| } |