/*
* 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.myfaces.config.annotation;
import java.io.DataInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.CustomScoped;
import javax.faces.bean.NoneScoped;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.SessionScoped;
import javax.faces.bean.ViewScoped;
import javax.faces.component.FacesComponent;
import javax.faces.component.behavior.Behavior;
import javax.faces.component.behavior.FacesBehavior;
import javax.faces.context.ExternalContext;
import javax.faces.convert.FacesConverter;
import javax.faces.event.*;
import javax.faces.render.ClientBehaviorRenderer;
import javax.faces.render.FacesBehaviorRenderer;
import javax.faces.render.FacesRenderer;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.Renderer;
import javax.faces.validator.FacesValidator;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
import org.apache.myfaces.config.FacesConfigDispenser;
import org.apache.myfaces.config.NamedEventManager;
import org.apache.myfaces.config.RuntimeConfig;
import org.apache.myfaces.config.impl.digester.elements.FacesConfig;
import org.apache.myfaces.shared_impl.util.ClassUtils;
import org.apache.myfaces.view.facelets.util.Classpath;
/**
* Configure all annotations that needs to be defined at startup.
*
* <ul>
* <li>{@link javax.faces.component.FacesComponent}</li>
* <li>{@link javax.faces.convert.FacesConverter}</li>
* <li>{@link javax.faces.validator.FacesValidator}</li>
* <li>{@link javax.faces.render.FacesRenderer}</li>
* <li>{@link javax.faces.bean.ManagedBean}</li>
* <li>{@link javax.faces.bean.ManagedProperty}</li>
* <li>{@link javax.faces.render.FacesBehaviorRenderer}</li>
* </ul>
* <p>
* Some parts copied from org.apache.shale.tiger.view.faces.LifecycleListener2
* </p>
*
* @since 2.0
* @author Leonardo Uribe (latest modification by $Author: lu4242 $)
* @version $Revision: 931412 $ $Date: 2010-04-06 21:56:36 -0500 (Tue, 06 Apr 2010) $
*/
public class AnnotationConfigurator
{
//private static final Log log = LogFactory.getLog(AnnotationConfigurator.class);
private static final Logger log = Logger.getLogger(AnnotationConfigurator.class.getName());
private static final String META_INF_PREFIX = "META-INF/";
private static final String FACES_CONFIG_SUFFIX = ".faces-config.xml";
private static final String STANDARD_FACES_CONFIG_RESOURCE = "META-INF/standard-faces-config.xml";
/**
* <p> Servlet context init parameter which defines which packages to scan
* for beans, separated by commas.</p>
*/
@JSFWebConfigParam(since="2.0")
public static final String SCAN_PACKAGES = "org.apache.myfaces.annotation.SCAN_PACKAGES";
/**
* <p>Context relative path to the default <code>faces-config.xml</code>
* resource for a JavaServer Faces application.</p>
*/
private static final String FACES_CONFIG_DEFAULT = "/WEB-INF/faces-config.xml";
/**
* <p>Resource path used to acquire implicit resources buried
* inside application JARs.</p>
*/
private static final String FACES_CONFIG_IMPLICIT = "META-INF/faces-config.xml";
/**
* <p>Prefix path used to locate web application classes for this
* web application.</p>
*/
private static final String WEB_CLASSES_PREFIX = "/WEB-INF/classes/";
/**
* <p>Prefix path used to locate web application libraries for this
* web application.</p>
*/
private static final String WEB_LIB_PREFIX = "/WEB-INF/lib/";
private final ExternalContext _externalContext;
private final _ClassByteCodeAnnotationFilter _filter;
/**
* This set contains the annotation names that this AnnotationConfigurator is able to scan
* in the format that is read from .class file.
*/
private static Set<String> byteCodeAnnotationsNames;
static
{
Set<String> bcan = new HashSet<String>(10, 1f);
bcan.add("Ljavax/faces/component/FacesComponent;");
bcan.add("Ljavax/faces/component/behavior/FacesBehavior;");
bcan.add("Ljavax/faces/convert/FacesConverter;");
bcan.add("Ljavax/faces/validator/FacesValidator;");
bcan.add("Ljavax/faces/render/FacesRenderer;");
bcan.add("Ljavax/faces/bean/ManagedBean;");
bcan.add("Ljavax/faces/event/NamedEvent;");
//bcan.add("Ljavax/faces/event/ListenerFor;");
//bcan.add("Ljavax/faces/event/ListenersFor;");
bcan.add("Ljavax/faces/render/FacesBehaviorRenderer;");
byteCodeAnnotationsNames = Collections.unmodifiableSet(bcan);
}
public AnnotationConfigurator(ExternalContext externalContext)
{
_externalContext = externalContext;
_filter = new _ClassByteCodeAnnotationFilter();
}
public void configure(final Application application,
final FacesConfigDispenser<FacesConfig> dispenser,
boolean metadataComplete) throws FacesException
{
List<Class> classes;
List<JarFile> archives = null;
// Here we have two cases, if metadataComplete we have only to scan
// annotations on myfaces impl jar file, otherwise, scan as usual.
if (!metadataComplete)
{
// Scan annotation available in org.apache.myfaces.annotation.SCAN_PACKAGES
// init param
String scanPackages = _externalContext.getInitParameter(SCAN_PACKAGES);
if (scanPackages != null)
{
// Scan the classes configured by the scan_packages context parameter
try
{
classes = packageClasses(_externalContext, scanPackages);
}
catch (ClassNotFoundException e)
{
throw new FacesException(e);
}
catch (IOException e)
{
throw new FacesException(e);
}
}
else
{
// Scan the classes in /WEB-INF/classes for interesting annotations
try
{
classes = webClasses(_externalContext);
}
catch (ClassNotFoundException e)
{
throw new FacesException(e);
}
}
try
{
for (Class clazz : classes)
{
configureClass(application, dispenser, clazz);
}
}
catch (Exception e)
{
throw new FacesException(e);
}
// Scan the classes in /WEB-INF/lib for interesting annotations
try
{
archives = webArchives(_externalContext);
if (log.isLoggable(Level.FINEST))
{
log.finest("Receiving " + archives.size() + " jar files to check");
}
for (JarFile archive : archives)
{
classes = archiveClasses(_externalContext, archive);
for (Class clazz : classes)
{
configureClass(application, dispenser, clazz);
}
}
}
catch (Exception e)
{
throw new FacesException(e);
}
}
//Read only annotations available myfaces-impl
try
{
//Also scan jar including META-INF/standard-faces-config.xml
//(myfaces-impl jar file)
JarFile jarFile = getMyfacesImplJarFile();
if (jarFile != null)
{
classes = archiveClasses(_externalContext, jarFile);
for (Class clazz : classes)
{
configureClass(application, dispenser, clazz);
}
}
}
catch (Exception e)
{
throw new FacesException(e);
}
}
/**
* Return the JarFile related to myfaces-impl, based on standard-faces-config.xml is
* inside it.
*
* @return myfaces JarFile instance
* @throws IOException
*/
private JarFile getMyfacesImplJarFile() throws IOException
{
URL url = getClassLoader().getResource(STANDARD_FACES_CONFIG_RESOURCE);
if (url == null)
{
url = getClass().getClassLoader().getResource(STANDARD_FACES_CONFIG_RESOURCE);
}
return getJarFile(url);
}
private JarFile getJarFile(URL url) throws IOException
{
URLConnection conn = url.openConnection();
conn.setUseCaches(false);
conn.setDefaultUseCaches(false);
JarFile jarFile;
if (conn instanceof JarURLConnection)
{
jarFile = ((JarURLConnection) conn).getJarFile();
}
else
{
jarFile = _getAlternativeJarFile(url);
}
return jarFile;
}
/**
* <p>Return a list of the classes defined within the given packages
* If there are no such classes, a zero-length list will be returned.</p>
*
* @param scanPackages the package configuration
*
* @exception ClassNotFoundException if a located class cannot be loaded
* @exception IOException if an input/output error occurs
*/
private List<Class> packageClasses(final ExternalContext externalContext,
final String scanPackages) throws ClassNotFoundException, IOException
{
List<Class> list = new ArrayList<Class>();
String[] scanPackageTokens = scanPackages.split(",");
for (String scanPackageToken : scanPackageTokens)
{
if (scanPackageToken.toLowerCase().endsWith(".jar"))
{
URL jarResource = externalContext.getResource(WEB_LIB_PREFIX
+ scanPackageToken);
String jarURLString = "jar:" + jarResource.toString() + "!/";
URL url = new URL(jarURLString);
JarFile jarFile = ((JarURLConnection) url.openConnection())
.getJarFile();
list.addAll(archiveClasses(externalContext, jarFile));
}
else
{
_PackageInfo.getInstance().getClasses(list, scanPackageToken);
}
}
return list;
}
/**
* <p>Return a list of the JAR archives defined under the
* <code>/WEB-INF/lib</code> directory of this web application
* that contain a <code>META-INF/faces-config.xml</code> resource
* (even if that resource is empty). If there are no such JAR archives,
* a zero-length list will be returned.</p>
*
* @param externalContext <code>ExternalContext</code> instance for
* this application
*
* @exception IOException if an input/output error occurs
*/
private List<JarFile> webArchives(ExternalContext externalContext)
throws IOException
{
List<JarFile> list = new ArrayList<JarFile>();
Set<String> paths = externalContext.getResourcePaths(WEB_LIB_PREFIX);
if (paths == null || paths.isEmpty())
{
//This usually happens when maven-jetty-plugin is used
//Scan jars looking for paths including META-INF/faces-config.xml
Iterator<URL> it = ClassUtils.getResources(FACES_CONFIG_IMPLICIT,
this);
while (it.hasNext())
{
URL url = it.next();
JarFile jarFile = getJarFile(url);
if (jarFile != null)
{
list.add(jarFile);
}
}
//Scan files inside META-INF ending with .faces-config.xml
URL[] urls = Classpath.search(getClassLoader(), META_INF_PREFIX, FACES_CONFIG_SUFFIX);
for (int i = 0; i < urls.length; i++)
{
JarFile jarFile = getJarFile(urls[i]);
if (jarFile != null)
{
list.add(jarFile);
}
}
}
else
{
//Scan jars available on path
for (Object pathObject : paths)
{
String path = (String) pathObject;
if (!path.endsWith(".jar"))
{
continue;
}
URL url = externalContext.getResource(path);
String jarURLString = "jar:" + url.toString() + "!/";
url = new URL(jarURLString);
JarFile jarFile = ((JarURLConnection) url.openConnection())
.getJarFile();
// Skip this JAR file if it does not have a META-INF/faces-config.xml
// resource (even if that resource is empty)
JarEntry signal = jarFile.getJarEntry(FACES_CONFIG_IMPLICIT);
if (signal == null)
{
//Look for files starting with META-INF/ and ending with .faces-config.xml
for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements();)
{
JarEntry entry = e.nextElement();
String name = entry.getName();
if (name.startsWith(META_INF_PREFIX) &&
name.endsWith(FACES_CONFIG_SUFFIX))
{
signal = entry;
break;
}
}
}
if (signal == null)
{
if (log.isLoggable(Level.FINEST))
{
log
.finest("Skip JAR file "
+ path
+ " because it has no META-INF/faces-config.xml resource");
}
continue;
}
list.add(jarFile);
}
}
return list;
}
/**
* <p>Return a list of classes to examine from the specified JAR archive.
* If this archive has no classes in it, a zero-length list is returned.</p>
*
* @param context <code>ExternalContext</code> instance for
* this application
* @param jar <code>JarFile</code> for the archive to be scanned
*
* @exception ClassNotFoundException if a located class cannot be loaded
*/
private List<Class> archiveClasses(ExternalContext context, JarFile jar)
throws ClassNotFoundException
{
// Accumulate and return a list of classes in this JAR file
List<Class> list = new ArrayList<Class>();
ClassLoader loader = ClassUtils.getContextClassLoader();
if (loader == null)
{
loader = this.getClass().getClassLoader();
}
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements())
{
JarEntry entry = entries.nextElement();
if (entry.isDirectory())
{
continue; // This is a directory
}
String name = entry.getName();
if (name.startsWith("META-INF/"))
{
continue; // Attribute files
}
if (!name.endsWith(".class"))
{
continue; // This is not a class
}
DataInputStream in = null;
boolean couldContainAnnotation = false;
try
{
in = new DataInputStream(jar.getInputStream(entry));
couldContainAnnotation = _filter
.couldContainAnnotationsOnClassDef(in,
byteCodeAnnotationsNames);
}
catch (IOException e)
{
// Include this class - we can't scan this class using
// the filter, but it could be valid, so we need to
// load it using the classLoader. Anyway, log a debug
// message.
couldContainAnnotation = true;
if (log.isLoggable(Level.FINE))
{
log.fine("IOException when filtering class " + name
+ " for annotations");
}
}
finally
{
if (in != null)
try
{
in.close();
}
catch (IOException e)
{
// No Op
}
}
if (couldContainAnnotation)
{
name = name.substring(0, name.length() - 6); // Trim ".class"
Class clazz = null;
try
{
clazz = loader.loadClass(name.replace('/', '.'));
}
catch (NoClassDefFoundError e)
{
; // Skip this class - we cannot analyze classes we cannot load
}
catch (Exception e)
{
; // Skip this class - we cannot analyze classes we cannot load
}
if (clazz != null)
{
list.add(clazz);
}
}
}
return list;
}
/**
* <p>Return a list of the classes defined under the
* <code>/WEB-INF/classes</code> directory of this web
* application. If there are no such classes, a zero-length list
* will be returned.</p>
*
* @param externalContext <code>ExternalContext</code> instance for
* this application
*
* @exception ClassNotFoundException if a located class cannot be loaded
*/
private List<Class> webClasses(ExternalContext externalContext)
throws ClassNotFoundException
{
List<Class> list = new ArrayList<Class>();
webClasses(externalContext, WEB_CLASSES_PREFIX, list);
return list;
}
/**
* <p>Add classes found in the specified directory to the specified
* list, recursively calling this method when a directory is encountered.</p>
*
* @param externalContext <code>ExternalContext</code> instance for
* this application
* @param prefix Prefix specifying the "directory path" to be searched
* @param list List to be appended to
*
* @exception ClassNotFoundException if a located class cannot be loaded
*/
private void webClasses(ExternalContext externalContext, String prefix,
List<Class> list) throws ClassNotFoundException
{
ClassLoader loader = getClassLoader();
Set<String> paths = externalContext.getResourcePaths(prefix);
if(paths == null)
{
return; //need this in case there is no WEB-INF/classes directory
}
if (log.isLoggable(Level.FINEST))
{
log.finest("webClasses(" + prefix + ") - Received " + paths.size()
+ " paths to check");
}
String path = null;
if (paths.isEmpty())
{
if (log.isLoggable(Level.WARNING))
{
log
.warning("AnnotationConfigurator does not found classes "
+ "for annotations in "
+ prefix
+ " ."
+ " This could happen because maven jetty plugin is used"
+ " (goal jetty:run). Try configure "
+ SCAN_PACKAGES + " init parameter "
+ "or use jetty:run-exploded instead.");
}
}
else
{
for (Object pathObject : paths)
{
path = (String) pathObject;
if (path.endsWith("/"))
{
webClasses(externalContext, path, list);
}
else if (path.endsWith(".class"))
{
DataInputStream in = null;
boolean couldContainAnnotation = false;
try
{
in = new DataInputStream(externalContext
.getResourceAsStream(path));
couldContainAnnotation = _filter
.couldContainAnnotationsOnClassDef(in,
byteCodeAnnotationsNames);
}
catch (IOException e)
{
// Include this class - we can't scan this class using
// the filter, but it could be valid, so we need to
// load it using the classLoader. Anyway, log a debug
// message.
couldContainAnnotation = true;
if (log.isLoggable(Level.FINE))
{
log.fine("IOException when filtering class " + path
+ " for annotations");
}
}
finally
{
if (in != null)
try
{
in.close();
}
catch (IOException e)
{
// No Op
}
}
if (couldContainAnnotation)
{
//Load it and add it to list for later processing
path = path.substring(WEB_CLASSES_PREFIX.length()); // Strip prefix
path = path.substring(0, path.length() - 6); // Strip suffix
path = path.replace('/', '.'); // Convert to FQCN
Class clazz = null;
try
{
clazz = loader.loadClass(path);
}
catch (NoClassDefFoundError e)
{
; // Skip this class - we cannot analyze classes we cannot load
}
catch (Exception e)
{
; // Skip this class - we cannot analyze classes we cannot load
}
if (clazz != null)
{
list.add(clazz);
}
}
}
}
}
}
/**
*
*
* @param application
* @param dispenser
* @param clazz
*/
protected void configureClass( Application application, FacesConfigDispenser<FacesConfig> dispenser, Class clazz)
{
if (log.isLoggable(Level.FINEST))
{
log.finest("registerClass(" + clazz.getName() + ")");
}
FacesComponent comp = (FacesComponent) clazz
.getAnnotation(FacesComponent.class);
if (comp != null)
{
if (log.isLoggable(Level.FINEST))
{
log.finest("addComponent(" + comp.value() + ","
+ clazz.getName() + ")");
}
//If there is a previous entry on Application Configuration Resources,
//the entry there takes precedence
if (dispenser.getComponentClass(comp.value()) == null)
{
application.addComponent(comp.value(), clazz.getName());
}
}
FacesConverter conv = (FacesConverter) clazz
.getAnnotation(FacesConverter.class);
if (conv != null)
{
if (log.isLoggable(Level.FINEST))
{
log.finest("addConverter(" + conv.value() + ","
+ clazz.getName() + ")");
}
//If there is a previous entry on Application Configuration Resources,
//the entry there takes precedence
if (!Object.class.equals(conv.forClass()))
{
application.addConverter(conv.forClass(), clazz.getName());
}
if (dispenser.getConverterClassById(conv.value()) == null &&
conv.value() != null && !"".equals(conv.value()))
{
application.addConverter(conv.value(), clazz.getName());
}
}
FacesValidator val = (FacesValidator) clazz
.getAnnotation(FacesValidator.class);
if (val != null)
{
if (log.isLoggable(Level.FINEST))
{
log.finest("addValidator(" + val.value() + "," + clazz.getName()
+ ")");
}
//If there is a previous entry on Application Configuration Resources,
//the entry there takes precedence
if (dispenser.getValidatorClass(val.value()) == null)
{
application.addValidator(val.value(), clazz.getName());
if (val.isDefault())
{
application.addDefaultValidatorId(val.value());
}
}
}
FacesRenderer rend = (FacesRenderer) clazz
.getAnnotation(FacesRenderer.class);
if (rend != null)
{
String renderKitId = rend.renderKitId();
if (renderKitId == null)
{
renderKitId = RenderKitFactory.HTML_BASIC_RENDER_KIT;
}
if (log.isLoggable(Level.FINEST))
{
log.finest("addRenderer(" + renderKitId + ", "
+ rend.componentFamily() + ", " + rend.rendererType()
+ ", " + clazz.getName() + ")");
}
try
{
RenderKit rk = renderKitFactory().getRenderKit(null,
renderKitId);
if (rk == null)
{
if (log.isLoggable(Level.SEVERE))
{
log.severe("RenderKit "+renderKitId+" not found when adding @FacesRenderer annotation");
}
throw new FacesException("RenderKit "+renderKitId+" not found when adding @FacesRenderer annotation");
}
rk.addRenderer(rend.componentFamily(), rend.rendererType(),
(Renderer) clazz.newInstance());
}
catch (Exception e)
{
throw new FacesException(e);
}
}
javax.faces.bean.ManagedBean bean =
(javax.faces.bean.ManagedBean) clazz.getAnnotation(javax.faces.bean.ManagedBean.class);
if (bean != null)
{
if (log.isLoggable(Level.FINE))
{
log.fine("Class '" + clazz.getName() + "' has an @ManagedBean annotation");
}
RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(_externalContext);
org.apache.myfaces.config.impl.digester.elements.ManagedBean mbc =
new org.apache.myfaces.config.impl.digester.elements.ManagedBean();
String beanName = bean.name();
if ((beanName == null) || beanName.equals ("")) {
int index;
// Missing name attribute algorithm: take the unqualified name and make the
// first character lowercase.
beanName = clazz.getName();
index = beanName.lastIndexOf (".");
if (index != -1) {
beanName = beanName.substring (index + 1);
}
beanName = Character.toLowerCase (beanName.charAt (0)) +
beanName.substring (1);
}
mbc.setName(beanName);
mbc.setBeanClass(clazz.getName());
ApplicationScoped appScoped = (ApplicationScoped) clazz.getAnnotation(ApplicationScoped.class);
if (appScoped != null)
{
mbc.setScope("application");
}
else
{
NoneScoped noneScoped = (NoneScoped) clazz.getAnnotation(NoneScoped.class);
if (noneScoped != null)
{
mbc.setScope("none");
}
else
{
RequestScoped requestScoped = (RequestScoped) clazz.getAnnotation(RequestScoped.class);
if (requestScoped != null)
{
mbc.setScope("request");
}
else
{
SessionScoped sessionScoped = (SessionScoped) clazz.getAnnotation(SessionScoped.class);
if (sessionScoped != null)
{
mbc.setScope("session");
}
else
{
ViewScoped viewScoped = (ViewScoped) clazz.getAnnotation(ViewScoped.class);
if (viewScoped != null)
{
mbc.setScope("view");
}
else {
CustomScoped customScoped = (CustomScoped) clazz.getAnnotation(CustomScoped.class);
if (customScoped != null)
{
mbc.setScope(customScoped.value());
}
else
{
// No scope annotation means default of "request".
mbc.setScope ("request");
}
}
}
}
}
}
Field[] fields = fields(clazz);
for (Field field : fields)
{
if (log.isLoggable(Level.FINEST))
{
log.finest(" Scanning field '" + field.getName() + "'");
}
javax.faces.bean.ManagedProperty property = (javax.faces.bean.ManagedProperty) field
.getAnnotation(javax.faces.bean.ManagedProperty.class);
if (property != null)
{
if (log.isLoggable(Level.FINE))
{
log.fine(" Field '" + field.getName()
+ "' has a @ManagedProperty annotation");
}
org.apache.myfaces.config.impl.digester.elements.ManagedProperty mpc =
new org.apache.myfaces.config.impl.digester.elements.ManagedProperty();
String name = property.name();
if ((name == null) || "".equals(name))
{
name = field.getName();
}
mpc.setPropertyName(name);
mpc.setPropertyClass(field.getType().getName()); // FIXME - primitives, arrays, etc.
mpc.setValue(property.value());
mbc.addProperty(mpc);
continue;
}
}
runtimeConfig.addManagedBean(mbc.getManagedBeanName(), mbc);
}
NamedEvent namedEvent = (NamedEvent) clazz.getAnnotation (NamedEvent.class);
if (namedEvent != null) {
// Can only apply @NamedEvent to ComponentSystemEvent subclasses.
if (!ComponentSystemEvent.class.isAssignableFrom (clazz)) {
// Just log this. We'll catch it later in the runtime.
if (log.isLoggable(Level.WARNING)) {
log.warning (clazz.getName() + " is annotated with @javax.faces.event.NamedEvent, but does " +
"not extend javax.faces.event.ComponentSystemEvent");
}
return;
}
// Have to register @NamedEvent annotations with the NamedEventManager class since
// we need to get access to this info later and can't from the dispenser (it's not a
// singleton).
NamedEventManager.getInstance().addNamedEvent (namedEvent.shortName(),
(Class<? extends ComponentSystemEvent>) clazz);
}
FacesBehavior facesBehavior = (FacesBehavior) clazz.getAnnotation (FacesBehavior.class);
if (facesBehavior != null) {
// Can only apply @FacesBehavior to Behavior implementors.
if (!Behavior.class.isAssignableFrom (clazz)) {
// Just log this. We'll catch it later in the runtime.
if (log.isLoggable(Level.WARNING)) {
log.warning (clazz.getName() + " is annotated with @javax.faces.component.behavior.FacesBehavior, " +
"but does not implement javax.faces.component.behavior.Behavior");
}
}
if (log.isLoggable(Level.FINEST)) {
log.finest ("addBehavior(" + facesBehavior.value() + ", " + clazz.getName() + ")");
}
application.addBehavior (facesBehavior.value(), clazz.getName());
}
FacesBehaviorRenderer facesBehaviorRenderer = (FacesBehaviorRenderer) clazz.getAnnotation (FacesBehaviorRenderer.class);
if (facesBehaviorRenderer != null) {
String renderKitId = facesBehaviorRenderer.renderKitId();
RenderKit renderKit;
if (log.isLoggable(Level.FINEST)) {
log.finest ("addClientBehaviorRenderer(" + renderKitId + ", " + facesBehaviorRenderer.rendererType() + ", " +
clazz.getName() + ")");
}
try {
ClientBehaviorRenderer clientBehaviorRenderer;
renderKit = renderKitFactory().getRenderKit (null, renderKitId);
clientBehaviorRenderer = (ClientBehaviorRenderer) clazz.newInstance();
renderKit.addClientBehaviorRenderer(facesBehaviorRenderer.rendererType(), clientBehaviorRenderer);
}
catch (Throwable e) {
throw new FacesException (e);
}
}
/*
ListenerFor listenerFor = (ListenerFor) clazz.getAnnotation(ListenerFor.class);
if (listenerFor != null)
{
processListenerFor(application, clazz, listenerFor);
}
ListenersFor listenersFor = (ListenersFor) clazz.getAnnotation(ListenersFor.class);
if (listenersFor != null)
{
for (ListenerFor item : listenersFor.value())
{
processListenerFor(application, clazz, item);
}
}*/
// TODO: All annotations scanned at startup must be configured here!
}
/*
private void processListenerFor(Application application, Class clazz, ListenerFor listenerFor)
{
try
{
if (SystemEventListener.class.isAssignableFrom(clazz))
{
Class sourceClass = listenerFor.sourceClass();
Class<? extends SystemEvent> systemEventClass = listenerFor.systemEventClass();
if (Void.class.equals(sourceClass))
{
application.subscribeToEvent(systemEventClass, (SystemEventListener) clazz.newInstance());
}
else
{
application.subscribeToEvent(systemEventClass, sourceClass, (SystemEventListener) clazz.newInstance());
}
}
}
catch (InstantiationException e)
{
throw new FacesException(e);
}
catch (IllegalAccessException e)
{
throw new FacesException(e);
}
}*/
/**
* <p>Return an array of all <code>Field</code>s reflecting declared
* fields in this class, or in any superclass other than
* <code>java.lang.Object</code>.</p>
*
* @param clazz Class to be analyzed
*/
private Field[] fields(Class clazz) {
Map<String,Field> fields = new HashMap<String,Field>();
do {
for (Field field : clazz.getDeclaredFields()) {
if (!fields.containsKey(field.getName())) {
fields.put(field.getName(), field);
}
}
} while ((clazz = clazz.getSuperclass()) != Object.class);
return (Field[]) fields.values().toArray(new Field[fields.size()]);
}
/**
* <p>The render kit factory for this application.</p>
*/
private RenderKitFactory rkFactory = null;
/**
* <p>Return the <code>RenderKitFactory</code> for this application.</p>
*/
private RenderKitFactory renderKitFactory()
{
if (rkFactory == null)
{
rkFactory = (RenderKitFactory) FactoryFinder
.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
}
return rkFactory;
}
private ClassLoader getClassLoader()
{
ClassLoader loader = ClassUtils.getContextClassLoader();
if (loader == null)
{
loader = this.getClass().getClassLoader();
}
return loader;
}
/**
* taken from org.apache.myfaces.view.facelets.util.Classpath
*
* For URLs to JARs that do not use JarURLConnection - allowed by the servlet spec - attempt to produce a JarFile
* object all the same. Known servlet engines that function like this include Weblogic and OC4J. This is not a full
* solution, since an unpacked WAR or EAR will not have JAR "files" as such.
*/
private static JarFile _getAlternativeJarFile(URL url) throws IOException
{
String urlFile = url.getFile();
// Trim off any suffix - which is prefixed by "!/" on Weblogic
int separatorIndex = urlFile.indexOf("!/");
// OK, didn't find that. Try the less safe "!", used on OC4J
if (separatorIndex == -1)
{
separatorIndex = urlFile.indexOf('!');
}
if (separatorIndex != -1)
{
String jarFileUrl = urlFile.substring(0, separatorIndex);
// And trim off any "file:" prefix.
if (jarFileUrl.startsWith("file:"))
{
jarFileUrl = jarFileUrl.substring("file:".length());
}
return new JarFile(jarFileUrl);
}
return null;
}
}