Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.glassfish.web.loader.ServletContainerInitializerUtil Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2016 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2016] [Payara Foundation and/or its affiliates]
package org.glassfish.web.loader;
import org.glassfish.deployment.common.ClassDependencyBuilder;
import org.glassfish.hk2.classmodel.reflect.*;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.annotation.HandlesTypes;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.MessageFormat;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Utility class - contains util methods used for implementation of
* pluggable Shared Library features
*
* @author Vijay Ramachandran
*/
public class ServletContainerInitializerUtil {
private static final Logger log = LogFacade.getLogger();
private static final ResourceBundle rb = log.getResourceBundle();
/**
* Given a class loader, check for ServletContainerInitializer
* implementations in any JAR file in the classpath
*
* @param webFragmentMap
* @param absoluteOrderingList
* @param cl The ClassLoader to be used to find JAR files
* @param hasOthers
* @param servletInitializersEnabled
*
* @return Iterable over all ServletContainerInitializers that were found
*/
public static Iterable getServletContainerInitializers(
Map webFragmentMap, List absoluteOrderingList,
boolean hasOthers, ClassLoader cl, boolean servletInitializersEnabled) {
/*
* If there is an absoluteOrderingList specified, then make sure that
* any ServletContainerInitializers included in fragment JARs
* NOT listed in the absoluteOrderingList will be ignored.
* For this, we remove any unwanted fragment JARs from the class
* loader's URL
*/
if (!servletInitializersEnabled) {
try {
final URLClassLoader webAppCl = (URLClassLoader) cl;
cl = AccessController.doPrivileged(new PrivilegedAction() {
@Override
public URLClassLoader run() {
return new URLClassLoader(new URL[0], webAppCl.getParent());
}
});
} finally {
/* intentionally left blank */
}
}
if((absoluteOrderingList != null) && !hasOthers) {
if(!(cl instanceof URLClassLoader)) {
log.log(Level.WARNING,
LogFacade.WRONG_CLASSLOADER_TYPE,
cl.getClass().getCanonicalName());
return null;
}
final URLClassLoader webAppCl = (URLClassLoader) cl;
// Create a new List of URLs with missing fragments removed from
// the currentUrls
List newClassLoaderUrlList = new ArrayList();
for (URL classLoaderUrl : webAppCl.getURLs()) {
// Check that the URL is using file protocol, else ignore it
if (!"file".equals(classLoaderUrl.getProtocol())) {
continue;
}
File file = new File(classLoaderUrl.getFile());
try {
file = file.getCanonicalFile();
} catch (IOException e) {
// Ignore
}
if (!file.exists()) {
continue;
}
String path = file.getAbsolutePath();
if (!path.endsWith(".jar")) {
continue;
}
if (!isFragmentMissingFromAbsoluteOrdering(file.getName(),
webFragmentMap, absoluteOrderingList)) {
newClassLoaderUrlList.add(classLoaderUrl);
}
}
// Create temporary classloader for ServiceLoader#load
// TODO: Have temporary classloader honor delegate flag from
// sun-web.xml
URL[] urlsForNewClassLoader =
new URL[newClassLoaderUrlList.size()];
final URL[] urlArray = newClassLoaderUrlList.toArray(urlsForNewClassLoader);
cl = AccessController.doPrivileged(new PrivilegedAction() {
@Override
public URLClassLoader run() {
return new URLClassLoader(urlArray, webAppCl.getParent());
}
});
}
return ServiceLoader.load(ServletContainerInitializer.class, cl);
}
/**
* Builds a mapping of classes to the list of ServletContainerInitializers
* interested in them
*
* @param initializers an Iterable over all ServletContainerInitializers
* that need to be considered
*
* @return Mapping of classes to list of ServletContainerInitializers
* interested in them
*/
public static Map, List>> getInterestList(Iterable initializers) {
if (null == initializers) {
return null;
}
Map, List>> interestList = null;
// Build a list of the classes / annotations in which the
// initializers are interested
for (ServletContainerInitializer sc : initializers) {
if(interestList == null) {
interestList = new HashMap, List>>();
}
Class extends ServletContainerInitializer> sciClass = sc.getClass();
HandlesTypes ann = (HandlesTypes) sciClass.getAnnotation(HandlesTypes.class);
if(ann == null) {
// This initializer does not contain @HandlesTypes
// This means it should always be called for all web apps
// So map it with a special token
List> currentInitializerList =
interestList.get(ServletContainerInitializerUtil.class);
if(currentInitializerList == null) {
List> arr =
new ArrayList>();
arr.add(sciClass);
interestList.put(ServletContainerInitializerUtil.class, arr);
} else {
currentInitializerList.add(sciClass);
}
} else {
Class[] interestedClasses = ann.value();
if( (interestedClasses != null) && (interestedClasses.length != 0) ) {
for(Class c : interestedClasses) {
List> currentInitializerList =
interestList.get(c);
if(currentInitializerList == null) {
List> arr =
new ArrayList>();
arr.add(sciClass);
interestList.put(c, arr);
} else {
currentInitializerList.add(sciClass);
}
}
}
}
}
return interestList;
}
/**
* Given an interestlist that was built above, and a class loader, scan the entire web app's classes and libraries
* looking for classes that extend/implement/use the annotations of a class present in the interest list
*
* @param initializers Iterable over all ServletContainerInitializers that
* were discovered
* @param interestList The interestList built by the previous util method
* @param cl The classloader to be used to load classes in WAR
* @return Map<Class<? extends ServletContainerInitializer>, Set<Class<?>>>
* A Map of ServletContainerInitializer classes to be called and arguments to be passed
* to them
*/
public static Map, Set>> getInitializerList(
Iterable initializers,
Map, List>> interestList,
Types types,
ClassLoader cl, boolean isStandalone) {
if (interestList == null) {
return null;
}
// This contains the final list of initializers and the set of
// classes to be passed to them as arg
Map, Set>> initializerList = null;
// If an initializer was present without any @HandleTypes, it
// must be called with a null set of classes
if(interestList.containsKey(ServletContainerInitializerUtil.class)) {
initializerList = new HashMap, Set>>();
List> initializersWithoutHandleTypes =
interestList.get(ServletContainerInitializerUtil.class);
for(Class extends ServletContainerInitializer> c : initializersWithoutHandleTypes) {
initializerList.put(c, null);
}
}
/*
* Now scan every class in this app's WEB-INF/classes and WEB-INF/lib
* to see if any class uses the annotation or extends/implements a
* class in our interest list.
* Do this scanning only if we have ServletContainerinitializers that
* have expressed specific interest
*/
if( (interestList.keySet().size() > 1) ||
((interestList.keySet().size() == 1) &&
(!interestList.containsKey(ServletContainerInitializerUtil.class)))) {
/*
* Create an instance of ClassDependencyBuilder that looks at the byte code and keeps
* the information for every class in this app
*
*/
if (types==null || Boolean.getBoolean("org.glassfish.web.parsing")) {
ClassDependencyBuilder classInfo = new ClassDependencyBuilder();
if (cl instanceof URLClassLoader) {
URLClassLoader ucl = (URLClassLoader) cl;
for(URL u : ucl.getURLs()) {
String path = u.getPath();
try {
if(path.endsWith(".jar")) {
JarFile jf = new JarFile(path);
try {
Enumeration entries = jf.entries();
while(entries.hasMoreElements()) {
JarEntry anEntry = entries.nextElement();
if(anEntry.isDirectory())
continue;
if(!anEntry.getName().endsWith(".class"))
continue;
InputStream jarInputStream = null;
try {
jarInputStream = jf.getInputStream(anEntry);
int size = (int) anEntry.getSize();
byte[] classData = new byte[size];
for(int bytesRead = 0; bytesRead < size;) {
int r2 = jarInputStream.read(classData, bytesRead, size - bytesRead);
bytesRead += r2;
}
classInfo.loadClassData(classData);
} catch (Throwable t) {
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE,
LogFacade.CLASS_LOADING_ERROR,
new Object[] {
anEntry.getName(),
t.toString()});
}
continue;
} finally {
if(jarInputStream != null) {
jarInputStream.close();
}
}
}
} finally {
jf.close();
}
} else {
File file = new File(path);
if (file.exists()) {
if (file.isDirectory()) {
scanDirectory(file, classInfo);
} else {
log.log(Level.WARNING,
LogFacade.INVALID_URL_CLASS_LOADER_PATH,
path);
}
}
}
} catch(IOException ioex) {
String msg = rb.getString(LogFacade.IO_ERROR);
msg = MessageFormat.format(msg,
new Object[] { path });
log.log(Level.SEVERE, msg, ioex);
return null;
}
}
}
initializerList = checkAgainstInterestList(classInfo, interestList, initializerList, cl, isStandalone);
} else {
initializerList = checkAgainstInterestList(types, interestList, initializerList, cl, isStandalone);
}
}
/*
* If a ServletContainerInitializer was annotated with HandlesTypes,
* but none of the application classes match, we must still invoke
* it at its onStartup method, passing in a null Set of classes
*/
for (ServletContainerInitializer initializer : initializers) {
if (!initializerList.containsKey(initializer.getClass())) {
initializerList.put(initializer.getClass(), null);
}
}
return initializerList;
}
/**
* Checks if a given JAR file is to be excluded while searching for ServletContainerInitializer implementations
* @param jarName the JAR file
* @param webFragmentMap fragment information from deployment desc
* @param absoluteOrderingList give ordering list
* @return true if the given JAR file is NOT present in the absolute ordering list
*/
private static boolean isFragmentMissingFromAbsoluteOrdering(
String jarName, Map webFragmentMap,
List absoluteOrderingList) {
return (webFragmentMap != null &&
absoluteOrderingList != null &&
!absoluteOrderingList.contains(
webFragmentMap.get(jarName)));
}
/**
* Given a directory, scan all sub directories looking for classes and
* build the interest list
*
* @param dir the directory to be scanned
* @param classInfo the ClassDependencyBuilder that holds info on all classes
*/
private static void scanDirectory(File dir, ClassDependencyBuilder classInfo) {
File[] files = dir.listFiles();
for (File file : files) {
if (file.isFile()) {
String fileName = file.getPath();
if (fileName.endsWith(".class")) {
try {
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
is = new BufferedInputStream(new FileInputStream(fileName));
byte[] bs = new byte[2048];
int size = -1;
while ((size = is.read(bs)) >= 0) {
baos.write(bs, 0, size);
}
classInfo.loadClassData(baos.toByteArray());
} finally {
if (is != null) {
is.close();
}
baos.close();
}
} catch (Throwable t) {
if (log.isLoggable(Level.WARNING)) {
log.log(Level.WARNING,
LogFacade.CLASS_LOADING_ERROR,
new Object[] {fileName, t.toString()});
}
continue;
}
}
} else {
scanDirectory(file, classInfo);
}
}
return;
}
/**
* Given the interestList, checks in the Types metadata if a given class
* uses any of the annotations, subclasses any of the type; If so, builds
* the initializer list
*
*/
private static Map, Set>> checkAgainstInterestList(
Types classInfo,
Map, List>> interestList,
Map, Set>> initializerList,
ClassLoader cl, boolean isStandalone) {
if (classInfo==null) {
return initializerList;
}
for (Map.Entry, List>> e:
interestList.entrySet()) {
Class> c = e.getKey();
Type type = classInfo.getBy(c.getName());
if (type==null)
continue;
Set> resultSet = new HashSet>();
if (type instanceof AnnotationType) {
for (AnnotatedElement ae : ((AnnotationType) type).allAnnotatedTypes()) {
if (ae instanceof Member) {
ae = ((Member) ae).getDeclaringType();
} else if (ae instanceof Parameter) {
ae = ((Parameter) ae).getMethod().getDeclaringType();
}
if (ae instanceof Type) {
try {
resultSet.add(cl.loadClass(ae.getName()));
} catch (Throwable t) {
if (log.isLoggable(getStandaloneWarningLevel(isStandalone))) {
log.log(getStandaloneWarningLevel(isStandalone),
LogFacade.CLASS_LOADING_ERROR,
new Object[] {ae.getName(), t.toString()});
}
}
}
}
} else {
Collection classes;
if (type instanceof InterfaceModel) {
classes = ((InterfaceModel) type).allImplementations();
} else {
classes = ((ClassModel) type).allSubTypes();
}
for (ClassModel classModel : classes) {
try {
resultSet.add(cl.loadClass(classModel.getName()));
} catch (Throwable t) {
if (log.isLoggable(getStandaloneWarningLevel(isStandalone))) {
log.log(getStandaloneWarningLevel(isStandalone),
LogFacade.CLASS_LOADING_ERROR,
new Object[] {classModel.getName(), t.toString()});
}
}
}
}
if(initializerList == null) {
initializerList = new HashMap, Set>>();
}
List> containerInitializers = e.getValue();
for(Class extends ServletContainerInitializer> initializer : containerInitializers) {
Set> classSet = initializerList.get(initializer);
if(classSet == null) {
classSet = new HashSet>();
}
classSet.addAll(resultSet);
initializerList.put(initializer, classSet);
}
}
return initializerList;
}
/**
* Given the interestList, checks if a given class uses any of the
* annotations; If so, builds the initializer list
*
* @param classInfo the ClassDependencyBuilder instance that holds info on all classes
* @param interestList the interestList built earlier
* @param initializerList the initializerList built so far
* @param cl the ClassLoader to be used to load the class
* @return the updated initializer list
*/
private static Map, Set>> checkAgainstInterestList(
ClassDependencyBuilder classInfo,
Map, List>> interestList,
Map, Set>> initializerList,
ClassLoader cl, boolean isStandalone) {
for(Map.Entry, List>> e :
interestList.entrySet()) {
Class> c = e.getKey();
Set resultFromClassInfo = classInfo.computeResult(c.getName());
if(resultFromClassInfo.isEmpty()) {
continue;
}
Set> resultSet = new HashSet>();
for(Iterator iter = resultFromClassInfo.iterator(); iter.hasNext();) {
String className = iter.next().replace('/', '.');
try {
Class aClass = cl.loadClass(className);
resultSet.add(aClass);
} catch (Throwable t) {
if (log.isLoggable(getStandaloneWarningLevel(isStandalone))) {
log.log(getStandaloneWarningLevel(isStandalone),
LogFacade.CLASS_LOADING_ERROR,
new Object[] {className, t.toString()});
}
}
}
if(initializerList == null) {
initializerList = new HashMap, Set>>();
}
List> containerInitializers = e.getValue();
for(Class extends ServletContainerInitializer> initializer : containerInitializers) {
Set> classSet = initializerList.get(initializer);
if(classSet == null) {
classSet = new HashSet>();
}
classSet.addAll(resultSet);
initializerList.put(initializer, classSet);
}
}
return initializerList;
}
private static Level getStandaloneWarningLevel(boolean isStandalone) {
return isStandalone? Level.WARNING : Level.FINE;
}
}