/* * 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.naming.resources; import java.io.File; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import javax.naming.NamingException; import javax.naming.directory.Attributes; import org.apache.naming.NamingEntry; /** * Extended FileDirContext implementation that will allow loading of tld files * from the META-INF directory (or subdirectories) in classpath. This will fully * mimic the behavior of compressed jars also when using unjarred resources. Tld * files can be loaded indifferently from WEB-INF webapp dir (or subdirs) or * from META-INF dir from jars available in the classpath: using this DirContext * implementation you will be able to use unexpanded jars during development and * to make any tld in them virtually available to the webapp. * * Sample context xml configuration: * * <code> * <Context docBase="\webapps\mydocbase"> * <Resources className="org.apache.naming.resources.VirtualDirContext" * virtualClasspath="\dir\classes;\somedir\somejar.jar"/> * </Resources> * </code> * * * <strong>This is not meant to be used for production. * Its meant to ease development with IDE's without the * need for fully republishing jars in WEB-INF/lib</strong> * * * @author Fabrizio Giustina * */ public class VirtualDirContext extends FileDirContext { /** * Map containing generated virtual names for tld files under WEB-INF and * the actual file reference. */ private Map<String, File> virtualMappings; /** * Map containing a mapping for tag files that should be loaded from the * META-INF dir of referenced jar files. */ private Map<String, File> tagfileMappings; /** * <code>;</code> separated list of virtual path elements. */ private String virtualClasspath; /** * <code>virtualClasspath</code> attribute that will be automatically set * from the <code>Context</code> <code>virtualClasspath</code> attribute * from the context xml file. * @param path <code>;</code> separated list of path elements. */ public void setVirtualClasspath(String path) { virtualClasspath = path; } /** * {@inheritDoc} */ @Override public void allocate() { super.allocate(); virtualMappings = new Hashtable<String, File>(); tagfileMappings = new Hashtable<String, File>(); // looks into any META-INF dir found in classpath entries for tld files. StringTokenizer tkn = new StringTokenizer(virtualClasspath, ";"); while (tkn.hasMoreTokens()) { File file = new File(tkn.nextToken(), "META-INF"); if (!file.exists() || !file.isDirectory()) { continue; } scanForTlds(file); } } /** * {@inheritDoc} */ @Override public void release() { super.release(); virtualMappings = null; } @Override public Attributes getAttributes(String name) throws NamingException { // handle "virtual" tlds if (name.startsWith("/WEB-INF/") && name.endsWith(".tld")) { String tldName = name.substring(name.lastIndexOf("/") + 1); if (virtualMappings.containsKey(tldName)) { return new FileResourceAttributes(virtualMappings.get(tldName)); } } else if (name.startsWith("/META-INF/tags") && name.endsWith(".tag") || name.endsWith(".tagx")) { // already loaded tag file if (tagfileMappings.containsKey(name)) { return new FileResourceAttributes(tagfileMappings.get(name)); } // unknown tagfile, search for it in virtualClasspath StringTokenizer tkn = new StringTokenizer(virtualClasspath, ";"); while (tkn.hasMoreTokens()) { File file = new File(tkn.nextToken(), name); if (file.exists()) { tagfileMappings.put(name, file); return new FileResourceAttributes(file); } } } return super.getAttributes(name); } @Override @SuppressWarnings("unchecked") protected ArrayList list(File file) { ArrayList entries = super.list(file); // adds virtual tlds for WEB-INF listing if ("WEB-INF".equals(file.getName())) { entries.addAll(getVirtualNamingEntries()); } return entries; } @Override public Object lookup(String name) throws NamingException { // handle "virtual" tlds if (name.startsWith("/WEB-INF/") && name.endsWith(".tld")) { String tldName = name.substring(name.lastIndexOf("/") + 1); if (virtualMappings.containsKey(tldName)) { return new FileResource(virtualMappings.get(tldName)); } } else if (name.startsWith("/META-INF/tags") && name.endsWith(".tag") || name.endsWith(".tagx")) { // already loaded tag file: we are sure that getAttributes() has // already been called if we are here File tagFile = tagfileMappings.get(name); if (tagFile != null) { return new FileResource(tagFile); } } return super.lookup(name); } /** * Scan a given dir for tld files. Any found tld will be added to the * virtualMappings. * @param dir Dir to scan for tlds */ private void scanForTlds(File dir) { File[] files = dir.listFiles(); for (int j = 0; j < files.length; j++) { File file = files[j]; if (file.isDirectory()) { scanForTlds(file); } else if (file.getName().endsWith(".tld")) { // just generate a random name using the current timestamp, name // doesn't matter since it needs to be referenced by URI String virtualTldName = "~" + System.currentTimeMillis() + "~" + file.getName(); virtualMappings.put(virtualTldName, file); } } } /** * Returns a list of virtual naming entries. * @return list of naming entries, containing tlds in virtualMappings */ private List<NamingEntry> getVirtualNamingEntries() { List<NamingEntry> virtual = new ArrayList<NamingEntry>(); for (String name : virtualMappings.keySet()) { File file = virtualMappings.get(name); NamingEntry entry = new NamingEntry(name, new FileResource(file), NamingEntry.ENTRY); virtual.add(entry); } return virtual; } }