| /* |
| * Copyright (c) 2013-2018 Oracle and/or its affiliates. All rights reserved. |
| * Copyright 2004 The Apache Software Foundation |
| * |
| * Licensed 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.io.InputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.text.MessageFormat; |
| import java.util.*; |
| import java.util.jar.JarEntry; |
| import java.util.jar.JarFile; |
| |
| import javax.naming.Binding; |
| import javax.naming.NameClassPair; |
| import javax.naming.NamingEnumeration; |
| import javax.naming.NamingException; |
| import javax.naming.directory.Attributes; |
| |
| import org.apache.naming.LogFacade; |
| import org.apache.naming.NamingEntry; |
| import org.apache.naming.NamingContextBindingsEnumeration; |
| import org.apache.naming.NamingContextEnumeration; |
| |
| /** |
| * Filesystem Directory Web Context implementation helper class. |
| * |
| * @author Shing Wai Chan |
| */ |
| |
| public class WebDirContext extends FileDirContext { |
| |
| // -------------------------------------------------------------- Constants |
| |
| protected static final String META_INF_RESOURCES = "META-INF/resources/"; |
| |
| |
| // ----------------------------------------------------------- Constructors |
| |
| |
| /** |
| * Builds a file directory context using the given environment. |
| */ |
| public WebDirContext() { |
| super(); |
| } |
| |
| |
| /** |
| * Builds a file directory context using the given environment. |
| */ |
| public WebDirContext(Hashtable<String, Object> env) { |
| super(env); |
| } |
| |
| |
| // ----------------------------------------------------- Instance Variables |
| |
| protected JarFileResourcesProvider jarFileResourcesProvider = null; |
| |
| protected String jarResourceBase = META_INF_RESOURCES; |
| |
| |
| // ------------------------------------------------------------- Properties |
| |
| public void setJarFileResourcesProvider( |
| JarFileResourcesProvider jarFileResourcesProvider) { |
| this.jarFileResourcesProvider = jarFileResourcesProvider; |
| } |
| |
| void setJarResourceBase(String jarResourceBase) { |
| this.jarResourceBase = jarResourceBase; |
| } |
| |
| // --------------------------------------------------------- Public Methods |
| |
| |
| /** |
| * Release any resources allocated for this directory context. |
| */ |
| @Override |
| public void release() { |
| |
| jarFileResourcesProvider = null; |
| jarResourceBase = null; |
| super.release(); |
| |
| } |
| |
| |
| // -------------------------------------------------------- Context Methods |
| |
| |
| /** |
| * Retrieves the named object. |
| * |
| * @param name the name of the object to look up |
| * @return the object bound to name |
| * @exception NamingException if a naming exception is encountered |
| */ |
| @Override |
| public Object lookup(String name) |
| throws NamingException { |
| Object result = null; |
| File file = file(name); |
| JarFileEntry jfEntry = null; |
| |
| if (file == null) { |
| jfEntry = lookupFromJars(name); |
| if (jfEntry == null) { |
| throw new NamingException |
| (MessageFormat.format(rb.getString(LogFacade.RESOURCES_NOT_FOUND), name)); |
| } |
| } |
| |
| if ((file != null && file.isDirectory()) || |
| (jfEntry != null && jfEntry.getJarEntry().isDirectory())) { |
| WebDirContext tempContext = new WebDirContext(env); |
| tempContext.docBase = name; |
| tempContext.setAllowLinking(getAllowLinking()); |
| tempContext.setCaseSensitive(isCaseSensitive()); |
| tempContext.setJarFileResourcesProvider(jarFileResourcesProvider); |
| tempContext.setJarResourceBase(getAbsoluteJarResourceName(name)); |
| result = tempContext; |
| } else if (file != null) { |
| result = new FileResource(file); |
| } else if (jfEntry != null) { |
| result = new JarResource(jfEntry.jarFile, jfEntry.jarEntry); |
| } |
| |
| return result; |
| |
| } |
| |
| |
| /** |
| * Enumerates the names bound in the named context, along with the class |
| * names of objects bound to them. The contents of any subcontexts are |
| * not included. |
| * <p> |
| * If a binding is added to or removed from this context, its effect on |
| * an enumeration previously returned is undefined. |
| * |
| * @param name the name of the context to list |
| * @return an enumeration of the names and class names of the bindings in |
| * this context. Each element of the enumeration is of type NameClassPair. |
| * @exception NamingException if a naming exception is encountered |
| */ |
| @Override |
| public NamingEnumeration<NameClassPair> list(String name) |
| throws NamingException { |
| |
| List<NamingEntry> namingEntries = null; |
| File file = file(name); |
| if (file != null) { |
| namingEntries = list(file); |
| } |
| List<JarFileEntry> jfeEntries = lookupAllFromJars(name); |
| for (JarFileEntry jfeEntry : jfeEntries) { |
| List<NamingEntry> jfList = list(jfeEntry); |
| if (namingEntries != null) { |
| namingEntries.addAll(jfList); |
| } else { |
| namingEntries = jfList; |
| } |
| } |
| |
| if (file == null && jfeEntries.size() == 0) { |
| throw new NamingException |
| (MessageFormat.format(rb.getString(LogFacade.RESOURCES_NOT_FOUND), name)); |
| } |
| return new NamingContextEnumeration(namingEntries.iterator()); |
| } |
| |
| |
| /** |
| * Enumerates the names bound in the named context, along with the |
| * objects bound to them. The contents of any subcontexts are not |
| * included. |
| * <p> |
| * If a binding is added to or removed from this context, its effect on |
| * an enumeration previously returned is undefined. |
| * |
| * @param name the name of the context to list |
| * @return an enumeration of the bindings in this context. |
| * Each element of the enumeration is of type Binding. |
| * @exception NamingException if a naming exception is encountered |
| */ |
| @Override |
| public NamingEnumeration<Binding> listBindings(String name) |
| throws NamingException { |
| |
| List<NamingEntry> namingEntries = null; |
| File file = file(name); |
| if (file != null) { |
| namingEntries = list(file); |
| } |
| List<JarFileEntry> jfeEntries = lookupAllFromJars(name); |
| for (JarFileEntry jfeEntry : jfeEntries) { |
| List<NamingEntry> jfeList = list(jfeEntry); |
| if (namingEntries != null) { |
| namingEntries.addAll(jfeList); |
| } else { |
| namingEntries = jfeList; |
| } |
| } |
| |
| if (file == null && jfeEntries.size() == 0) { |
| throw new NamingException |
| (MessageFormat.format(rb.getString(LogFacade.RESOURCES_NOT_FOUND), name)); |
| } |
| |
| return new NamingContextBindingsEnumeration(namingEntries.iterator(), |
| this); |
| |
| } |
| |
| |
| // ----------------------------------------------------- DirContext Methods |
| |
| |
| /** |
| * Retrieves selected attributes associated with a named object. |
| * See the class description regarding attribute models, attribute type |
| * names, and operational attributes. |
| * |
| * @return the requested attributes; never null |
| * @param name the name of the object from which to retrieve attributes |
| * @param attrIds the identifiers of the attributes to retrieve. null |
| * indicates that all attributes should be retrieved; an empty array |
| * indicates that none should be retrieved |
| * @exception NamingException if a naming exception is encountered |
| */ |
| @Override |
| public Attributes getAttributes(String name, String[] attrIds) |
| throws NamingException { |
| |
| // Building attribute list |
| File file = file(name); |
| |
| if (file == null) { |
| JarFileEntry jfEntry = lookupFromJars(name); |
| if (jfEntry == null) { |
| throw new NamingException |
| (MessageFormat.format(rb.getString(LogFacade.RESOURCES_NOT_FOUND), name)); |
| } else { |
| return new JarResourceAttributes(jfEntry.getJarEntry()); |
| } |
| } else { |
| return new FileResourceAttributes(file); |
| } |
| |
| } |
| |
| |
| // ------------------------------------------------------ Protected Methods |
| |
| protected JarFileEntry lookupFromJars(String name) { |
| JarFileEntry result = null; |
| JarFile[] jarFiles = null; |
| if (jarFileResourcesProvider != null) { |
| jarFiles = jarFileResourcesProvider.getJarFiles(); |
| } |
| if (jarFiles != null) { |
| String jeName = getAbsoluteJarResourceName(name); |
| for (JarFile jarFile : jarFiles) { |
| JarEntry jarEntry = null; |
| if (jeName.charAt(jeName.length() - 1) != '/') { |
| jarEntry = jarFile.getJarEntry(jeName + '/'); |
| if (jarEntry != null) { |
| result = new JarFileEntry(jarFile, jarEntry); |
| break; |
| } |
| } |
| jarEntry = jarFile.getJarEntry(jeName); |
| if (jarEntry != null) { |
| result = new JarFileEntry(jarFile, jarEntry); |
| break; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| protected List<JarFileEntry> lookupAllFromJars(String name) { |
| List<JarFileEntry> results = new ArrayList<JarFileEntry>(); |
| JarFile[] jarFiles = null; |
| if (jarFileResourcesProvider != null) { |
| jarFiles = jarFileResourcesProvider.getJarFiles(); |
| } |
| if (jarFiles != null) { |
| String jeName = getAbsoluteJarResourceName(name); |
| for (JarFile jarFile : jarFiles) { |
| JarEntry jarEntry = null; |
| if (jeName.charAt(jeName.length() - 1) != '/') { |
| jarEntry = jarFile.getJarEntry(jeName + '/'); |
| if (jarEntry != null) { |
| results.add(new JarFileEntry(jarFile, jarEntry)); |
| } |
| } |
| jarEntry = jarFile.getJarEntry(jeName); |
| if (jarEntry != null) { |
| results.add(new JarFileEntry(jarFile, jarEntry)); |
| } |
| } |
| } |
| |
| return results; |
| } |
| |
| protected List<NamingEntry> list(JarFileEntry jfeEntry) { |
| List<NamingEntry> entries = new ArrayList<NamingEntry>(); |
| JarFile jarFile = jfeEntry.jarFile; |
| JarEntry jarEntry = jfeEntry.jarEntry; |
| if (!jarEntry.isDirectory()) { |
| return entries; |
| } |
| |
| String prefix = jarEntry.getName(); |
| int prefixLength = prefix.length(); |
| |
| Enumeration<JarEntry> e = jarFile.entries(); |
| while (e.hasMoreElements()) { |
| JarEntry je = e.nextElement(); |
| String name = je.getName(); |
| if (name.length() > prefixLength && name.startsWith(prefix)) { |
| int endIndex = name.indexOf('/', prefixLength); |
| if (endIndex != -1 && endIndex != name.length() - 1) { |
| // more levels |
| continue; |
| } |
| String subName = ((endIndex != -1)? |
| name.substring(prefixLength, endIndex) : |
| name.substring(prefixLength)); |
| |
| Object object = null; |
| if (je.isDirectory()) { |
| WebDirContext tempContext = new WebDirContext(env); |
| tempContext.docBase = name; |
| tempContext.setAllowLinking(getAllowLinking()); |
| tempContext.setCaseSensitive(isCaseSensitive()); |
| tempContext.setJarFileResourcesProvider(jarFileResourcesProvider); |
| tempContext.setJarResourceBase(name); |
| object = tempContext; |
| } else { |
| object = new JarResource(jarFile, je); |
| } |
| |
| entries.add(new NamingEntry(subName, object, NamingEntry.ENTRY)); |
| |
| } |
| } |
| |
| return entries; |
| } |
| |
| protected String getAbsoluteJarResourceName(String name) { |
| if (name.length() == 0) { |
| return jarResourceBase; |
| } |
| boolean firstEndsWithSlash = (jarResourceBase.charAt(jarResourceBase.length() -1) == '/'); |
| // name has length > 0 here |
| boolean secondStartWithSlash = (name.charAt(0) == '/'); |
| if (firstEndsWithSlash && secondStartWithSlash) { |
| return jarResourceBase.substring(0, jarResourceBase.length() - 1) + name; |
| } else if (!firstEndsWithSlash && !secondStartWithSlash) { |
| return jarResourceBase + '/' + name; |
| } else { |
| return jarResourceBase + name; |
| } |
| } |
| |
| // ----------------------------------------------- FileResource Inner Class |
| |
| |
| // ------------------------------------- JarFileEntry Inner Class |
| protected static class JarFileEntry { |
| private JarFile jarFile = null; |
| private JarEntry jarEntry = null; |
| |
| protected JarFileEntry(JarFile jf, JarEntry je) { |
| jarFile = jf; |
| jarEntry = je; |
| } |
| |
| JarFile getJarFile() { |
| return jarFile; |
| } |
| |
| JarEntry getJarEntry() { |
| return jarEntry; |
| } |
| } |
| |
| |
| // ------------------------------------- JarResource Inner Class |
| |
| /** |
| * This specialized resource implementation avoids opening the InputStream |
| * to the jar entry right away (which would put a lock on the jar file). |
| */ |
| protected static class JarResource extends Resource { |
| |
| |
| // -------------------------------------------------------- Constructor |
| |
| |
| public JarResource(JarFile jarFile, JarEntry jarEntry) { |
| this.jarFile = jarFile; |
| this.jarEntry = jarEntry; |
| } |
| |
| |
| // --------------------------------------------------- Member Variables |
| |
| |
| /** |
| * Associated JarFile object. |
| */ |
| protected JarFile jarFile; |
| |
| |
| /** |
| * Associated JarEntry object. |
| */ |
| protected JarEntry jarEntry; |
| |
| |
| // --------------------------------------------------- Resource Methods |
| |
| |
| /** |
| * Content accessor. |
| * |
| * @return InputStream |
| */ |
| public InputStream streamContent() |
| throws IOException { |
| if (binaryContent == null) { |
| InputStream jin = jarFile.getInputStream(jarEntry); |
| inputStream = jin; |
| return jin; |
| } |
| return super.streamContent(); |
| } |
| |
| |
| } |
| |
| // ------------------------------------- JarResourceAttributes Inner Class |
| |
| |
| /** |
| * This specialized resource attribute implementation does some lazy |
| * reading (to speed up simple checks, like checking the last modified |
| * date). |
| */ |
| protected static class JarResourceAttributes extends ResourceAttributes { |
| |
| |
| // -------------------------------------------------------- Constructor |
| |
| |
| public JarResourceAttributes(JarEntry jarEntry) { |
| this.jarEntry = jarEntry; |
| getCreation(); |
| getLastModified(); |
| } |
| |
| // --------------------------------------------------- Member Variables |
| |
| |
| protected transient JarEntry jarEntry; |
| |
| |
| protected boolean accessed = false; |
| |
| |
| // ----------------------------------------- ResourceAttributes Methods |
| |
| |
| /** |
| * Is collection. |
| */ |
| public boolean isCollection() { |
| if (!accessed) { |
| collection = jarEntry.isDirectory(); |
| accessed = true; |
| } |
| return super.isCollection(); |
| } |
| |
| |
| /** |
| * Get content length. |
| * |
| * @return content length value |
| */ |
| public long getContentLength() { |
| if (contentLength != -1L) |
| return contentLength; |
| contentLength = jarEntry.getSize(); |
| return contentLength; |
| } |
| |
| |
| /** |
| * Get creation time. |
| * |
| * @return creation time value |
| */ |
| public long getCreation() { |
| if (creation != -1L) |
| return creation; |
| creation = getLastModified(); |
| return creation; |
| } |
| |
| |
| /** |
| * Get creation date. |
| * |
| * @return Creation date value |
| */ |
| public Date getCreationDate() { |
| if (creation == -1L) { |
| creation = jarEntry.getTime(); |
| } |
| return super.getCreationDate(); |
| } |
| |
| |
| /** |
| * Get last modified time. |
| * |
| * @return lastModified time value |
| */ |
| public long getLastModified() { |
| if (lastModified != -1L) |
| return lastModified; |
| lastModified = jarEntry.getTime(); |
| return lastModified; |
| } |
| |
| |
| /** |
| * Get lastModified date. |
| * |
| * @return LastModified date value |
| */ |
| public Date getLastModifiedDate() { |
| if (lastModified == -1L) { |
| lastModified = jarEntry.getTime(); |
| } |
| return super.getLastModifiedDate(); |
| } |
| |
| |
| /** |
| * Get name. |
| * |
| * @return Name value |
| */ |
| public String getName() { |
| if (name == null) |
| name = jarEntry.getName(); |
| return name; |
| } |
| |
| |
| /** |
| * Get resource type. |
| * |
| * @return String resource type |
| */ |
| public String getResourceType() { |
| if (!accessed) { |
| collection = jarEntry.isDirectory(); |
| accessed = true; |
| } |
| return super.getResourceType(); |
| } |
| |
| |
| /** |
| * Get canonical path. |
| * |
| * @return String the file's canonical path |
| */ |
| public String getCanonicalPath() { |
| return null; |
| } |
| |
| // ----------------------------------------- Serializables Methods |
| |
| private void readObject(ObjectInputStream ois) throws IOException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| private void writeObject(ObjectOutputStream oos) throws IOException { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| } |
| |