blob: 2dc6f465af50402aa84752a7a1ac096c8ad43857 [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.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;
}
}
}