blob: eba84280fb4459408df8f9d693500c5dd6c3b65c [file] [log] [blame]
/*
* 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();
}
}
}