blob: decf7a49a7715b01f9b90a2704558b3873723566 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.plus.webapp;
import java.util.Iterator;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import org.eclipse.jetty.jndi.NamingUtil;
import org.eclipse.jetty.plus.annotation.Injection;
import org.eclipse.jetty.plus.annotation.InjectionCollection;
import org.eclipse.jetty.plus.annotation.LifeCycleCallback;
import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
import org.eclipse.jetty.plus.annotation.PostConstructCallback;
import org.eclipse.jetty.plus.annotation.PreDestroyCallback;
import org.eclipse.jetty.plus.annotation.RunAsCollection;
import org.eclipse.jetty.plus.jndi.EnvEntry;
import org.eclipse.jetty.plus.jndi.Link;
import org.eclipse.jetty.plus.jndi.NamingEntry;
import org.eclipse.jetty.plus.jndi.NamingEntryUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.Descriptor;
import org.eclipse.jetty.webapp.FragmentDescriptor;
import org.eclipse.jetty.webapp.IterativeDescriptorProcessor;
import org.eclipse.jetty.webapp.Origin;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlParser;
/**
* PlusDescriptorProcessor
*/
public class PlusDescriptorProcessor extends IterativeDescriptorProcessor
{
private static final Logger LOG = Log.getLogger(PlusDescriptorProcessor.class);
public PlusDescriptorProcessor ()
{
try
{
registerVisitor("env-entry", getClass().getMethod("visitEnvEntry", __signature));
registerVisitor("resource-ref", getClass().getMethod("visitResourceRef", __signature));
registerVisitor("resource-env-ref", getClass().getMethod("visitResourceEnvRef", __signature));
registerVisitor("message-destination-ref", getClass().getMethod("visitMessageDestinationRef", __signature));
registerVisitor("post-construct", getClass().getMethod("visitPostConstruct", __signature));
registerVisitor("pre-destroy", getClass().getMethod("visitPreDestroy", __signature));
}
catch (Exception e)
{
throw new IllegalStateException(e);
}
}
/**
* @see org.eclipse.jetty.webapp.IterativeDescriptorProcessor#start(WebAppContext, org.eclipse.jetty.webapp.Descriptor)
*/
public void start(WebAppContext context, Descriptor descriptor)
{
InjectionCollection injections = (InjectionCollection)context.getAttribute(InjectionCollection.INJECTION_COLLECTION);
if (injections == null)
{
injections = new InjectionCollection();
context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
}
LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
if (callbacks == null)
{
callbacks = new LifeCycleCallbackCollection();
context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, callbacks);
}
RunAsCollection runAsCollection = (RunAsCollection)context.getAttribute(RunAsCollection.RUNAS_COLLECTION);
if (runAsCollection == null)
{
runAsCollection = new RunAsCollection();
context.setAttribute(RunAsCollection.RUNAS_COLLECTION, runAsCollection);
}
}
/**
* {@inheritDoc}
*/
public void end(WebAppContext context,Descriptor descriptor)
{
}
/**
* JavaEE 5.4.1.3
*
* @param context the context
* @param descriptor the descriptor
* @param node the xml node
* @throws Exception if unable to process jndi bindings
*/
public void visitEnvEntry (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
throws Exception
{
String name=node.getString("env-entry-name",false,true);
String type = node.getString("env-entry-type",false,true);
String valueStr = node.getString("env-entry-value",false,true);
//if there's no value there's no point in making a jndi entry
//nor processing injection entries
if (valueStr==null || valueStr.equals(""))
{
LOG.warn("No value for env-entry-name "+name);
return;
}
Origin o = context.getMetaData().getOrigin("env-entry."+name);
switch (o)
{
case NotSet:
{
//no descriptor has configured an env-entry of this name previously
context.getMetaData().setOrigin("env-entry."+name, descriptor);
//the javaee_5.xsd says that the env-entry-type is optional
//if there is an <injection> element, because you can get
//type from the element, but what to do if there is more
//than one <injection> element, do you just pick the type
//of the first one?
addInjections (context, descriptor, node, name, TypeUtil.fromName(type));
Object value = TypeUtil.valueOf(type,valueStr);
bindEnvEntry(name, value);
break;
}
case WebXml:
case WebDefaults:
case WebOverride:
{
//ServletSpec 3.0 p75. web.xml (or web-override/web-defaults) declared
//the env-entry. A fragment is not allowed to change that, except unless
//the web.xml did not declare any injections.
if (!(descriptor instanceof FragmentDescriptor))
{
//We're processing web-defaults, web.xml or web-override. Any of them can
//set or change the env-entry.
context.getMetaData().setOrigin("env-entry."+name, descriptor);
addInjections (context, descriptor, node, name, TypeUtil.fromName(type));
Object value = TypeUtil.valueOf(type,valueStr);
bindEnvEntry(name, value);
}
else
{
//A web.xml declared the env-entry. Check to see if any injections have been
//declared for it. If it was declared in web.xml then don't merge any injections.
//If it was declared in a web-fragment, then we can keep merging fragments.
Descriptor d = context.getMetaData().getOriginDescriptor("env-entry."+name+".injection");
if (d==null || d instanceof FragmentDescriptor)
addInjections(context, descriptor, node, name, TypeUtil.fromName(type));
}
break;
}
case WebFragment:
{
//ServletSpec p.75. No declaration in web.xml, but in multiple web-fragments. Error.
throw new IllegalStateException("Conflicting env-entry "+name+" in "+descriptor.getResource());
}
}
}
/**
* Common Annotations Spec section 2.3:
* <p>
* resource-ref is for:
* <ul>
* <li>javax.sql.DataSource</li>
* <li>javax.jms.ConnectionFactory</li>
* <li>javax.jms.QueueConnectionFactory</li>
* <li>javax.jms.TopicConnectionFactory</li>
* <li>javax.mail.Session</li>
* <li>java.net.URL</li>
* <li>javax.resource.cci.ConnectionFactory</li>
* <li>org.omg.CORBA_2_3.ORB</li>
* <li>any other connection factory defined by a resource adapter</li>
* </ul>
*
* If web.xml contains a resource-ref with injection targets, all resource-ref entries
* of the same name are ignored in web fragments. If web.xml does not contain any
* injection-targets, then they are merged from all the fragments.
* If web.xml does not contain a resource-ref element of same name, but 2 fragments
* declare the same name it is an error.
* resource-ref entries are ONLY for connection factories
* the resource-ref says how the app will reference the jndi lookup relative
* to java:comp/env, but it is up to the deployer to map this reference to
* a real resource in the environment. At the moment, we insist that the
* jetty.xml file name of the resource has to be exactly the same as the
* name in web.xml deployment descriptor, but it shouldn't have to be
*
* <p>
* Maintenance update 3.0a to spec:
* <p>
* Update Section 8.2.3.h.ii with the following - If a resource reference
* element is specified in two fragments, while absent from the main web.xml,
* and all the attributes and child elements of the resource reference element
* are identical, the resource reference will be merged into the main web.xml.
* It is considered an error if a resource reference element has the same name
* specified in two fragments, while absent from the main web.xml and the attributes
* and child elements are not identical in the two fragments. For example, if two
* web fragments declare a <code>&lt;resource-ref&gt;</code> with the same <code>&lt;resource-ref-name&gt;</code> element
* but the type in one is specified as javax.sql.DataSource while the type in the
* other is that of a java mail resource, then an error must be reported and the
* application MUST fail to deploy.
*
* @param context the context
* @param descriptor the descriptor
* @param node the xml node
* @throws Exception if unable to bind nodes, or load classes
*/
public void visitResourceRef (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
throws Exception
{
String jndiName = node.getString("res-ref-name",false,true);
String type = node.getString("res-type", false, true);
String auth = node.getString("res-auth", false, true);
String shared = node.getString("res-sharing-scope", false, true);
Origin o = context.getMetaData().getOrigin("resource-ref."+jndiName);
switch (o)
{
case NotSet:
{
//No descriptor or annotation previously declared a resource-ref of this name.
context.getMetaData().setOrigin("resource-ref."+jndiName, descriptor);
//check for <injection> elements
Class<?> typeClass = TypeUtil.fromName(type);
if (typeClass==null)
typeClass = context.loadClass(type);
addInjections(context, descriptor, node, jndiName, typeClass);
bindResourceRef(context,jndiName, typeClass);
break;
}
case WebXml:
case WebDefaults:
case WebOverride:
{
//A web xml previously declared the resource-ref.
if (!(descriptor instanceof FragmentDescriptor))
{
//We're processing web-defaults, web.xml or web-override. Any of them can
//set or change the resource-ref.
context.getMetaData().setOrigin("resource-ref."+jndiName, descriptor);
//check for <injection> elements
Class<?> typeClass = TypeUtil.fromName(type);
if (typeClass==null)
typeClass = context.loadClass(type);
addInjections(context, descriptor, node, jndiName, typeClass);
//bind the entry into jndi
bindResourceRef(context,jndiName, typeClass);
}
else
{
//A web xml declared the resource-ref and we're processing a
//web-fragment. Check to see if any injections were declared for it by web.xml.
//If any injection was declared in web.xml then don't merge any injections.
//If it was declared in a web-fragment, then we can keep merging fragments.
Descriptor d = context.getMetaData().getOriginDescriptor("resource-ref."+jndiName+".injection");
if (d==null || d instanceof FragmentDescriptor)
{
Class<?> typeClass = TypeUtil.fromName(type);
if (typeClass==null)
typeClass = context.loadClass(type);
addInjections(context, descriptor, node, jndiName, TypeUtil.fromName(type));
}
}
break;
}
case WebFragment:
{
Descriptor otherFragment = context.getMetaData().getOriginDescriptor("resource-ref."+jndiName);
XmlParser.Node otherFragmentRoot = otherFragment.getRoot();
Iterator<Object> iter = otherFragmentRoot.iterator();
XmlParser.Node otherNode = null;
while (iter.hasNext() && otherNode == null)
{
Object obj = iter.next();
if (!(obj instanceof XmlParser.Node)) continue;
XmlParser.Node n = (XmlParser.Node)obj;
if ("resource-ref".equals(n.getTag()) && jndiName.equals(n.getString("res-ref-name",false,true)))
otherNode = n;
}
//If declared in another web-fragment
if (otherNode != null)
{
//declarations of the resource-ref must be the same in both fragment descriptors
String otherType = otherNode.getString("res-type", false, true);
String otherAuth = otherNode.getString("res-auth", false, true);
String otherShared = otherNode.getString("res-sharing-scope", false, true);
//otherType, otherAuth and otherShared must be the same as type, auth, shared
type = (type == null?"":type);
otherType = (otherType == null?"":otherType);
auth = (auth == null?"":auth);
otherAuth = (otherAuth == null?"":otherAuth);
shared = (shared == null?"":shared);
otherShared = (otherShared == null?"":otherShared);
//ServletSpec p.75. No declaration of resource-ref in web xml, but different in multiple web-fragments. Error.
if (!type.equals(otherType) || !auth.equals(otherAuth) || !shared.equals(otherShared))
throw new IllegalStateException("Conflicting resource-ref "+jndiName+" in "+descriptor.getResource());
//same in multiple web-fragments, merge the injections
addInjections(context, descriptor, node, jndiName, TypeUtil.fromName(type));
}
else
throw new IllegalStateException("resource-ref."+jndiName+" not found in declaring descriptor "+otherFragment);
}
}
}
/**
* Common Annotations Spec section 2.3:
* <p>
* resource-env-ref is for:
* <ul>
* <li>javax.transaction.UserTransaction</li>
* <li>javax.resource.cci.InteractionSpec</li>
* <li>anything else that is not a connection factory</li>
* </ul>
*
* @param context the context
* @param descriptor the descriptor
* @param node the xml node
* @throws Exception if unable to load classes, or bind jndi entries
*/
public void visitResourceEnvRef (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
throws Exception
{
String jndiName = node.getString("resource-env-ref-name",false,true);
String type = node.getString("resource-env-ref-type", false, true);
Origin o = context.getMetaData().getOrigin("resource-env-ref."+jndiName);
switch (o)
{
case NotSet:
{
//First declaration of resource-env-ref with this jndiName
//JavaEE Spec sec 5.7.1.3 says the resource-env-ref-type
//is mandatory, but the schema says it is optional!
Class<?> typeClass = TypeUtil.fromName(type);
if (typeClass==null)
typeClass = context.loadClass(type);
addInjections (context, descriptor, node, jndiName, typeClass);
bindResourceEnvRef(context,jndiName, typeClass);
break;
}
case WebXml:
case WebDefaults:
case WebOverride:
{
//A resource-env-ref of this name has been declared first in a web xml.
//Only allow other web-default, web.xml, web-override to change it.
if (!(descriptor instanceof FragmentDescriptor))
{
//We're processing web-defaults, web.xml or web-override. Any of them can
//set or change the resource-env-ref.
context.getMetaData().setOrigin("resource-env-ref."+jndiName, descriptor);
Class<?> typeClass = TypeUtil.fromName(type);
if (typeClass==null)
typeClass = context.loadClass(type);
addInjections (context, descriptor, node, jndiName, typeClass);
bindResourceEnvRef(context,jndiName, typeClass);
}
else
{
//We're processing a web-fragment. It can only contribute injections if the
//there haven't been any injections declared yet, or they weren't declared in a WebXml file.
Descriptor d = context.getMetaData().getOriginDescriptor("resource-env-ref."+jndiName+".injection");
if (d == null || d instanceof FragmentDescriptor)
{
Class<?> typeClass = TypeUtil.fromName(type);
if (typeClass==null)
typeClass = context.loadClass(type);
addInjections (context, descriptor, node, jndiName, typeClass);
}
}
break;
}
case WebFragment:
{
Descriptor otherFragment = context.getMetaData().getOriginDescriptor("resource-env-ref."+jndiName);
XmlParser.Node otherFragmentRoot = otherFragment.getRoot();
Iterator<Object> iter = otherFragmentRoot.iterator();
XmlParser.Node otherNode = null;
while (iter.hasNext() && otherNode == null)
{
Object obj = iter.next();
if (!(obj instanceof XmlParser.Node)) continue;
XmlParser.Node n = (XmlParser.Node)obj;
if ("resource-env-ref".equals(n.getTag()) && jndiName.equals(n.getString("resource-env-ref-name",false,true)))
otherNode = n;
}
if (otherNode != null)
{
//declarations of the resource-ref must be the same in both fragment descriptors
String otherType = otherNode.getString("resource-env-ref-type", false, true);
//types must be the same
type = (type == null?"":type);
otherType = (otherType == null?"":otherType);
//ServletSpec p.75. No declaration of resource-ref in web xml, but different in multiple web-fragments. Error.
if (!type.equals(otherType))
throw new IllegalStateException("Conflicting resource-env-ref "+jndiName+" in "+descriptor.getResource());
//same in multiple web-fragments, merge the injections
addInjections(context, descriptor, node, jndiName, TypeUtil.fromName(type));
}
else
throw new IllegalStateException("resource-env-ref."+jndiName+" not found in declaring descriptor "+otherFragment);
}
}
}
/**
* Common Annotations Spec section 2.3:
* <p>
* message-destination-ref is for:
* <ul>
* <li>javax.jms.Queue</li>
* <li>javax.jms.Topic</li>
* </ul>
*
* @param context the context
* @param descriptor the descriptor
* @param node the xml node
* @throws Exception if unable to load classes or bind jndi entries
*/
public void visitMessageDestinationRef (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
throws Exception
{
String jndiName = node.getString("message-destination-ref-name",false,true);
String type = node.getString("message-destination-type",false,true);
String usage = node.getString("message-destination-usage",false,true);
Origin o = context.getMetaData().getOrigin("message-destination-ref."+jndiName);
switch (o)
{
case NotSet:
{
//A message-destination-ref of this name has not been previously declared
Class<?> typeClass = TypeUtil.fromName(type);
if (typeClass==null)
typeClass = context.loadClass(type);
addInjections(context, descriptor, node, jndiName, typeClass);
bindMessageDestinationRef(context,jndiName, typeClass);
context.getMetaData().setOrigin("message-destination-ref."+jndiName, descriptor);
break;
}
case WebXml:
case WebDefaults:
case WebOverride:
{
//A message-destination-ref of this name has been declared first in a web xml.
//Only allow other web-default, web.xml, web-override to change it.
if (!(descriptor instanceof FragmentDescriptor))
{
Class<?> typeClass = TypeUtil.fromName(type);
if (typeClass==null)
typeClass = context.loadClass(type);
addInjections(context, descriptor, node, jndiName, typeClass);
bindMessageDestinationRef(context,jndiName, typeClass);
context.getMetaData().setOrigin("message-destination-ref."+jndiName, descriptor);
}
else
{
//A web-fragment has declared a message-destination-ref with the same name as a web xml.
//It can only contribute injections, and only if the web xml didn't declare any.
Descriptor d = context.getMetaData().getOriginDescriptor("message-destination-ref."+jndiName+".injection");
if (d == null || d instanceof FragmentDescriptor)
{
Class<?> typeClass = TypeUtil.fromName(type);
if (typeClass==null)
typeClass = context.loadClass(type);
addInjections(context, descriptor, node, jndiName, typeClass);
}
}
break;
}
case WebFragment:
{
Descriptor otherFragment = context.getMetaData().getOriginDescriptor("message-destination-ref."+jndiName);
XmlParser.Node otherFragmentRoot = otherFragment.getRoot();
Iterator<Object> iter = otherFragmentRoot.iterator();
XmlParser.Node otherNode = null;
while (iter.hasNext() && otherNode == null)
{
Object obj = iter.next();
if (!(obj instanceof XmlParser.Node)) continue;
XmlParser.Node n = (XmlParser.Node)obj;
if ("message-destination-ref".equals(n.getTag()) && jndiName.equals(n.getString("message-destination-ref-name",false,true)))
otherNode = n;
}
if (otherNode != null)
{
String otherType = node.getString("message-destination-type",false,true);
String otherUsage = node.getString("message-destination-usage",false,true);
type = (type==null?"":type);
usage = (usage==null?"":usage);
if (!type.equals(otherType) || !usage.equalsIgnoreCase(otherUsage))
throw new IllegalStateException("Conflicting message-destination-ref "+jndiName+" in "+descriptor.getResource());
//same in multiple web-fragments, merge the injections
addInjections(context, descriptor, node, jndiName, TypeUtil.fromName(type));
}
else
throw new IllegalStateException("message-destination-ref."+jndiName+" not found in declaring descriptor "+otherFragment);
}
}
}
/**
* If web.xml has at least 1 post-construct, then all post-constructs in fragments
* are ignored. Otherwise, post-constructs from fragments are merged.
* post-construct is the name of a class and method to call after all
* resources have been setup but before the class is put into use
*
* @param context the context
* @param descriptor the descriptor
* @param node the xml node
*/
public void visitPostConstruct(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
{
String className = node.getString("lifecycle-callback-class", false, true);
String methodName = node.getString("lifecycle-callback-method", false, true);
if (className==null || className.equals(""))
{
LOG.warn("No lifecycle-callback-class specified");
return;
}
if (methodName==null || methodName.equals(""))
{
LOG.warn("No lifecycle-callback-method specified for class "+className);
return;
}
//ServletSpec 3.0 p80 If web.xml declares a post-construct then all post-constructs
//in fragments must be ignored. Otherwise, they are additive.
Origin o = context.getMetaData().getOrigin("post-construct");
switch (o)
{
case NotSet:
{
//No post-constructs have been declared previously.
context.getMetaData().setOrigin("post-construct", descriptor);
try
{
Class<?> clazz = context.loadClass(className);
LifeCycleCallback callback = new PostConstructCallback();
callback.setTarget(clazz, methodName);
((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
}
catch (ClassNotFoundException e)
{
LOG.warn("Couldn't load post-construct target class "+className);
}
break;
}
case WebXml:
case WebDefaults:
case WebOverride:
{
//A web xml first declared a post-construct. Only allow other web xml files (web-defaults, web-overrides etc)
//to add to it
if (!(descriptor instanceof FragmentDescriptor))
{
try
{
Class<?> clazz = context.loadClass(className);
LifeCycleCallback callback = new PostConstructCallback();
callback.setTarget(clazz, methodName);
((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
}
catch (ClassNotFoundException e)
{
LOG.warn("Couldn't load post-construct target class "+className);
}
}
break;
}
case WebFragment:
{
//A web-fragment first declared a post-construct. Allow all other web-fragments to merge in their post-constructs
try
{
Class<?> clazz = context.loadClass(className);
LifeCycleCallback callback = new PostConstructCallback();
callback.setTarget(clazz, methodName);
((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
}
catch (ClassNotFoundException e)
{
LOG.warn("Couldn't load post-construct target class "+className);
}
break;
}
}
}
/**
*
* pre-destroy is the name of a class and method to call just as
* the instance is being destroyed
*
* @param context the context
* @param descriptor the descriptor
* @param node the xml node
*/
public void visitPreDestroy(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
{
String className = node.getString("lifecycle-callback-class", false, true);
String methodName = node.getString("lifecycle-callback-method", false, true);
if (className==null || className.equals(""))
{
LOG.warn("No lifecycle-callback-class specified for pre-destroy");
return;
}
if (methodName==null || methodName.equals(""))
{
LOG.warn("No lifecycle-callback-method specified for pre-destroy class "+className);
return;
}
Origin o = context.getMetaData().getOrigin("pre-destroy");
switch(o)
{
case NotSet:
{
//No pre-destroys have been declared previously. Record this descriptor
//as the first declarer.
context.getMetaData().setOrigin("pre-destroy", descriptor);
try
{
Class<?> clazz = context.loadClass(className);
LifeCycleCallback callback = new PreDestroyCallback();
callback.setTarget(clazz, methodName);
((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
}
catch (ClassNotFoundException e)
{
LOG.warn("Couldn't load pre-destory target class "+className);
}
break;
}
case WebXml:
case WebDefaults:
case WebOverride:
{
//A web xml file previously declared a pre-destroy. Only allow other web xml files
//(not web-fragments) to add to them.
if (!(descriptor instanceof FragmentDescriptor))
{
try
{
Class<?> clazz = context.loadClass(className);
LifeCycleCallback callback = new PreDestroyCallback();
callback.setTarget(clazz, methodName);
((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
}
catch (ClassNotFoundException e)
{
LOG.warn("Couldn't load pre-destory target class "+className);
}
}
break;
}
case WebFragment:
{
//No pre-destroys in web xml, so allow all fragments to merge their pre-destroys.
try
{
Class<?> clazz = context.loadClass(className);
LifeCycleCallback callback = new PreDestroyCallback();
callback.setTarget(clazz, methodName);
((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
}
catch (ClassNotFoundException e)
{
LOG.warn("Couldn't load pre-destory target class "+className);
}
break;
}
}
}
/**
* Iterate over the <code>&lt;injection-target&gt;</code> entries for a node
*
* @param context the context
* @param descriptor the descriptor
* @param node the xml node
* @param jndiName the jndi name
* @param valueClass the value class
*/
public void addInjections (WebAppContext context, Descriptor descriptor, XmlParser.Node node, String jndiName, Class<?> valueClass)
{
Iterator<XmlParser.Node> itor = node.iterator("injection-target");
while(itor.hasNext())
{
XmlParser.Node injectionNode = itor.next();
String targetClassName = injectionNode.getString("injection-target-class", false, true);
String targetName = injectionNode.getString("injection-target-name", false, true);
if ((targetClassName==null) || targetClassName.equals(""))
{
LOG.warn("No classname found in injection-target");
continue;
}
if ((targetName==null) || targetName.equals(""))
{
LOG.warn("No field or method name in injection-target");
continue;
}
InjectionCollection injections = (InjectionCollection)context.getAttribute(InjectionCollection.INJECTION_COLLECTION);
if (injections == null)
{
injections = new InjectionCollection();
context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
}
// comments in the javaee_5.xsd file specify that the targetName is looked
// for first as a java bean property, then if that fails, as a field
try
{
Class<?> clazz = context.loadClass(targetClassName);
Injection injection = new Injection();
injection.setJndiName(jndiName);
injection.setTarget(clazz, targetName, valueClass);
injections.add(injection);
//Record which was the first descriptor to declare an injection for this name
if (context.getMetaData().getOriginDescriptor(node.getTag()+"."+jndiName+".injection") == null)
context.getMetaData().setOrigin(node.getTag()+"."+jndiName+".injection", descriptor);
}
catch (ClassNotFoundException e)
{
LOG.warn("Couldn't load injection target class "+targetClassName);
}
}
}
/**
* @param name the jndi name
* @param value the value
* @throws Exception if unable to bind entry
*/
public void bindEnvEntry(String name, Object value) throws Exception
{
InitialContext ic = null;
boolean bound = false;
//check to see if we bound a value and an EnvEntry with this name already
//when we processed the server and the webapp's naming environment
//@see EnvConfiguration.bindEnvEntries()
ic = new InitialContext();
try
{
NamingEntry ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntryUtil.makeNamingEntryName(ic.getNameParser(""), name));
if (ne!=null && ne instanceof EnvEntry)
{
EnvEntry ee = (EnvEntry)ne;
bound = ee.isOverrideWebXml();
}
}
catch (NameNotFoundException e)
{
bound = false;
}
if (!bound)
{
//either nothing was bound or the value from web.xml should override
Context envCtx = (Context)ic.lookup("java:comp/env");
NamingUtil.bind(envCtx, name, value);
}
}
/**
* Bind a resource reference.
* <p>
* If a resource reference with the same name is in a jetty-env.xml
* file, it will already have been bound.
*
* @param context the context
* @param name the jndi name
* @param typeClass the type class
* @throws Exception if unable to bind resource
*/
public void bindResourceRef(WebAppContext context, String name, Class<?> typeClass)
throws Exception
{
bindEntry(context, name, typeClass);
}
public void bindResourceEnvRef(WebAppContext context, String name, Class<?> typeClass)
throws Exception
{
bindEntry(context, name, typeClass);
}
public void bindMessageDestinationRef(WebAppContext context, String name, Class<?> typeClass)
throws Exception
{
bindEntry(context, name, typeClass);
}
/**
* Bind a resource with the given name from web.xml of the given type
* with a jndi resource from either the server or the webapp's naming
* environment.
* <p>
* As the servlet spec does not cover the mapping of names in web.xml with
* names from the execution environment, jetty uses the concept of a Link, which is
* a subclass of the NamingEntry class. A Link defines a mapping of a name
* from web.xml with a name from the execution environment (ie either the server or the
* webapp's naming environment).
*
* @param context the context
* @param name name of the resource from web.xml
* @param typeClass the type class
* @throws Exception the exception
*/
protected void bindEntry (WebAppContext context, String name, Class<?> typeClass)
throws Exception
{
String nameInEnvironment = name;
boolean bound = false;
//check if the name in web.xml has been mapped to something else
//check a context-specific naming environment first
Object scope = context;
NamingEntry ne = NamingEntryUtil.lookupNamingEntry(scope, name);
if (ne!=null && (ne instanceof Link))
{
//if we found a mapping, get out name it is mapped to in the environment
nameInEnvironment = ((Link)ne).getLink();
}
//try finding that mapped name in the webapp's environment first
scope = context;
bound = NamingEntryUtil.bindToENC(scope, name, nameInEnvironment);
if (bound)
return;
//try the server's environment
scope = context.getServer();
bound = NamingEntryUtil.bindToENC(scope, name, nameInEnvironment);
if (bound)
return;
//try the jvm environment
bound = NamingEntryUtil.bindToENC(null, name, nameInEnvironment);
if (bound)
return;
//There is no matching resource so try a default name.
//The default name syntax is: the [res-type]/default
//eg javax.sql.DataSource/default
nameInEnvironment = typeClass.getName()+"/default";
//First try the server scope
NamingEntry defaultNE = NamingEntryUtil.lookupNamingEntry(context.getServer(), nameInEnvironment);
if (defaultNE==null)
defaultNE = NamingEntryUtil.lookupNamingEntry(null, nameInEnvironment);
if (defaultNE!=null)
defaultNE.bindToENC(name);
else
throw new IllegalStateException("Nothing to bind for name " + name);
}
}