| /* |
| * 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.io.InputStream; |
| import java.io.PrintWriter; |
| import java.net.URL; |
| import java.util.Dictionary; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.SortedMap; |
| import java.util.TreeMap; |
| |
| import org.apache.sling.scripting.jsp.jasper.JasperException; |
| import org.apache.sling.scripting.jsp.jasper.compiler.TldLocationsCache; |
| import org.apache.sling.scripting.jsp.jasper.xmlparser.ParserUtils; |
| import org.apache.sling.scripting.jsp.jasper.xmlparser.TreeNode; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleEvent; |
| import org.osgi.framework.BundleListener; |
| import org.osgi.framework.ServiceRegistration; |
| |
| /** |
| * The <code>SlingTldLocationsCache</code> TODO |
| */ |
| public class SlingTldLocationsCache |
| extends TldLocationsCache implements BundleListener { |
| |
| private static final String TLD_SCHEME = "tld:"; |
| |
| private final Map<String, TldLocationEntry> tldLocations = new HashMap<>(); |
| |
| private ServiceRegistration serviceRegistration; |
| |
| private final BundleContext bundleContext; |
| |
| public SlingTldLocationsCache(final BundleContext context) { |
| this.bundleContext = context; |
| context.addBundleListener(this); |
| final Bundle[] bundles = context.getBundles(); |
| for (int i = 0; i < bundles.length; i++) { |
| if (bundles[i].getState() == Bundle.RESOLVED || bundles[i].getState() == Bundle.ACTIVE ) { |
| addBundle(bundles[i]); |
| } |
| } |
| |
| Dictionary<String, Object> tldConfigPrinterProperties = new Hashtable<>(); |
| tldConfigPrinterProperties.put("felix.webconsole.label", "jsptaglibs"); |
| tldConfigPrinterProperties.put("felix.webconsole.title", "JSP Taglibs"); |
| tldConfigPrinterProperties.put("felix.webconsole.configprinter.modes", "always"); |
| this.serviceRegistration = context.registerService(Object.class.getName(), |
| this, tldConfigPrinterProperties); |
| |
| } |
| |
| public void deactivate(final BundleContext context) { |
| if (this.serviceRegistration != null) { |
| this.serviceRegistration.unregister(); |
| this.serviceRegistration = null; |
| } |
| context.removeBundleListener(this); |
| } |
| |
| // ---------- Tld Location URL support ------------------------------------- |
| |
| @Override |
| public void bundleChanged(final BundleEvent event) { |
| if ( event.getType() == BundleEvent.RESOLVED ) { |
| this.addBundle(event.getBundle()); |
| } else if ( event.getType() == BundleEvent.UNRESOLVED ) { |
| this.removeBundle(event.getBundle()); |
| } |
| } |
| |
| public URL getTldLocationURL(String tldLocation) { |
| if (tldLocation.startsWith(TLD_SCHEME)) { |
| tldLocation = tldLocation.substring(TLD_SCHEME.length()); |
| |
| TldLocationEntry tle; |
| synchronized (tldLocations) { |
| tle = tldLocations.get(tldLocation); |
| } |
| |
| if (tle != null) { |
| return tle.getTldURL(); |
| } |
| } |
| |
| return null; |
| } |
| |
| // ---------- TldLocationsCache support ------------------------------------ |
| |
| @Override |
| public String[] getLocation(final String uri) throws JasperException { |
| synchronized (tldLocations) { |
| final TldLocationEntry entry = tldLocations.get(uri); |
| if ( entry != null ) { |
| return new String[] { TLD_SCHEME + uri, entry.getTldURL().toString() }; |
| } |
| } |
| |
| return null; |
| } |
| |
| private void addBundle(final Bundle bundle) { |
| // currently only META-INF/*.tld is supported, this should |
| // be extended for registration in a Bundle Manifest Header |
| |
| final Enumeration<?> entries = bundle.findEntries("META-INF", "*.tld", false); |
| if (entries != null) { |
| while (entries.hasMoreElements()) { |
| final URL taglib = (URL) entries.nextElement(); |
| final String uri = getUriFromTld(taglib); |
| |
| synchronized (tldLocations) { |
| if (uri != null && !tldLocations.containsKey(uri)) { |
| tldLocations.put(uri, new TldLocationEntry(bundle, |
| taglib.getPath())); |
| } |
| } |
| } |
| } |
| } |
| |
| private void removeBundle(final Bundle bundle) { |
| synchronized (tldLocations) { |
| final Iterator<Map.Entry<String, TldLocationEntry>> i = tldLocations.entrySet().iterator(); |
| while ( i.hasNext() ) { |
| final Map.Entry<String, TldLocationEntry> entry = i.next(); |
| if (entry.getValue().getBundleId() == bundle.getBundleId()) { |
| i.remove(); |
| } |
| } |
| } |
| } |
| |
| /* |
| * Returns the value of the uri element of the given TLD, or null if the |
| * given TLD does not contain any such element. |
| */ |
| private String getUriFromTld(final URL resource) { |
| InputStream stream = null; |
| try { |
| stream = resource.openStream(); |
| |
| // Parse the tag library descriptor at the specified resource path |
| TreeNode tld = new ParserUtils().parseXMLDocument( |
| resource.toString(), stream); |
| TreeNode uri = tld.findChild("uri"); |
| if (uri != null) { |
| String body = uri.getBody(); |
| if (body != null) { |
| return body; |
| } |
| } |
| } catch (Exception e) { |
| // TODO: handle |
| } finally { |
| if (stream != null) { |
| try { |
| stream.close(); |
| } catch (Throwable t) { |
| // do nothing |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| public void printConfiguration(final PrintWriter pw) { |
| pw.println("Currently available JSP Taglibs:"); |
| final SortedMap<String, String> taglibs = new TreeMap<>(); |
| |
| for (final Map.Entry<String, TldLocationEntry> entry : tldLocations.entrySet()) { |
| final long bundleId = entry.getValue().getBundleId(); |
| final Bundle bundle = bundleContext.getBundle(bundleId); |
| if (bundle != null) { |
| taglibs.put(entry.getKey(), String.format("%s (%s)", bundle.getSymbolicName(), bundleId)); |
| } else { |
| // really shouldn't happen |
| taglibs.put(entry.getKey(), String.format("INVALID BUNDLE ID: %s", bundleId)); |
| } |
| } |
| |
| for (final Map.Entry<String, String> entry : taglibs.entrySet()) { |
| pw.printf(" %s - %s\n", entry.getKey(), entry.getValue()); |
| } |
| } |
| |
| |
| private static final class TldLocationEntry { |
| private final long bundleId; |
| |
| private final URL tldURL; |
| |
| private TldLocationEntry(final Bundle bundle, final String tldPath) { |
| this.bundleId = bundle.getBundleId(); |
| this.tldURL = bundle.getEntry(tldPath); |
| } |
| |
| long getBundleId() { |
| return this.bundleId; |
| } |
| |
| URL getTldURL() { |
| return this.tldURL; |
| } |
| } |
| } |