Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=50019
Add support for <lookup-name>.
Based on a patch by Gurkan Erdogdu.

git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc8.0.x/trunk@1831350 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties
index 02c9039..689c480 100644
--- a/java/org/apache/catalina/core/LocalStrings.properties
+++ b/java/org/apache/catalina/core/LocalStrings.properties
@@ -96,13 +96,16 @@
 jreLeakListener.authPolicyFail=Error whilst attempting to prevent memory leak in javax.security.auth.Policy class
 jreLeakListener.ldapPoolManagerFail=Failed to trigger creation of the com.sun.jndi.ldap.LdapPoolManager class during Tomcat start to prevent possible memory leaks. This is expected on non-Sun JVMs.
 jreLeakListener.classToInitializeFail=Failed to load class {0} during Tomcat start to prevent possible memory leaks.
-naming.wsdlFailed=Failed to find wsdl file: {0}
-naming.bindFailed=Failed to bind object: {0}
-naming.jmxRegistrationFailed=Failed to register in JMX: {0}
-naming.unbindFailed=Failed to unbind object: {0}
-naming.invalidEnvEntryType=Environment entry {0} has an invalid type
-naming.invalidEnvEntryValue=Environment entry {0} has an invalid value
-naming.namingContextCreationFailed=Creation of the naming context failed: {0}
+
+naming.addEnvEntry=Adding environment entry [{0}]
+naming.addResourceEnvRef=Adding resource env ref [{0}]
+naming.bindFailed=Failed to bind object: [{0}]
+naming.jmxRegistrationFailed=Failed to register in JMX: [{0}]
+naming.unbindFailed=Failed to unbind object: [{0}]
+naming.invalidEnvEntryType=Environment entry [{0}] has an invalid type
+naming.invalidEnvEntryValue=Environment entry [{0}] has an invalid value
+naming.namingContextCreationFailed=Creation of the naming context failed: [{0}]
+
 noPluggabilityServletContext.notAllowed=Section 4.4 of the Servlet 3.0 specification does not permit this method to be called from a ServletContextListener that was not defined in web.xml, a web-fragment.xml file nor annotated with @WebListener
 standardContext.invalidWrapperClass={0} is not a subclass of StandardWrapper
 standardContext.applicationListener=Error configuring application listener of class {0}
diff --git a/java/org/apache/catalina/core/NamingContextListener.java b/java/org/apache/catalina/core/NamingContextListener.java
index 84ce47e..cca75c6 100644
--- a/java/org/apache/catalina/core/NamingContextListener.java
+++ b/java/org/apache/catalina/core/NamingContextListener.java
@@ -53,6 +53,7 @@
 import org.apache.naming.ContextBindings;
 import org.apache.naming.EjbRef;
 import org.apache.naming.HandlerRef;
+import org.apache.naming.LookupRef;
 import org.apache.naming.NamingContext;
 import org.apache.naming.ResourceEnvRef;
 import org.apache.naming.ResourceLinkRef;
@@ -69,6 +70,8 @@
 import org.apache.tomcat.util.descriptor.web.ContextResourceLink;
 import org.apache.tomcat.util.descriptor.web.ContextService;
 import org.apache.tomcat.util.descriptor.web.ContextTransaction;
+import org.apache.tomcat.util.descriptor.web.MessageDestinationRef;
+import org.apache.tomcat.util.descriptor.web.ResourceBase;
 import org.apache.tomcat.util.modeler.Registry;
 import org.apache.tomcat.util.res.StringManager;
 
@@ -451,6 +454,19 @@
                     addLocalEjb(ejb);
                 }
             }
+        } else if (name.equals("messageDestinationRef")) {
+            if (oldValue != null) {
+                MessageDestinationRef mdr = (MessageDestinationRef) oldValue;
+                if (mdr.getName() != null) {
+                    removeMessageDestinationRef(mdr.getName());
+                }
+            }
+            if (newValue != null) {
+                MessageDestinationRef mdr = (MessageDestinationRef) newValue;
+                if (mdr.getName() != null) {
+                    addMessageDestinationRef(mdr);
+                }
+            }
         } else if (name.equals("resource")) {
             if (oldValue != null) {
                 ContextResource resource = (ContextResource) oldValue;
@@ -568,6 +584,12 @@
             addEjb(ejbs[i]);
         }
 
+        // Message Destination References
+        MessageDestinationRef[] mdrs = namingResources.findMessageDestinationRefs();
+        for (i = 0; i < mdrs.length; i++) {
+            addMessageDestinationRef(mdrs[i]);
+        }
+
         // WebServices references
         ContextService[] services = namingResources.findServices();
         for (i = 0; i < services.length; i++) {
@@ -659,24 +681,27 @@
      */
     public void addEjb(ContextEjb ejb) {
 
-        // Create a reference to the EJB.
-        Reference ref = new EjbRef
-            (ejb.getType(), ejb.getHome(), ejb.getRemote(), ejb.getLink());
-        // Adding the additional parameters, if any
-        Iterator<String> params = ejb.listProperties();
-        while (params.hasNext()) {
-            String paramName = params.next();
-            String paramValue = (String) ejb.getProperty(paramName);
-            StringRefAddr refAddr = new StringRefAddr(paramName, paramValue);
-            ref.add(refAddr);
+        Reference ref = lookForLookupRef(ejb);
+
+        if (ref == null) {
+            // Create a reference to the EJB.
+            ref = new EjbRef(ejb.getType(), ejb.getHome(), ejb.getRemote(), ejb.getLink());
+            // Adding the additional parameters, if any
+            Iterator<String> params = ejb.listProperties();
+            while (params.hasNext()) {
+                String paramName = params.next();
+                String paramValue = (String) ejb.getProperty(paramName);
+                StringRefAddr refAddr = new StringRefAddr(paramName, paramValue);
+                ref.add(refAddr);
+            }
         }
+
         try {
             createSubcontexts(envCtx, ejb.getName());
             envCtx.bind(ejb.getName(), ref);
         } catch (NamingException e) {
             logger.error(sm.getString("naming.bindFailed", e));
         }
-
     }
 
 
@@ -685,86 +710,89 @@
      */
     public void addEnvironment(ContextEnvironment env) {
 
-        Object value = null;
-        // Instantiating a new instance of the correct object type, and
-        // initializing it.
-        String type = env.getType();
-        try {
-            if (type.equals("java.lang.String")) {
-                value = env.getValue();
-            } else if (type.equals("java.lang.Byte")) {
-                if (env.getValue() == null) {
-                    value = Byte.valueOf((byte) 0);
-                } else {
-                    value = Byte.decode(env.getValue());
-                }
-            } else if (type.equals("java.lang.Short")) {
-                if (env.getValue() == null) {
-                    value = Short.valueOf((short) 0);
-                } else {
-                    value = Short.decode(env.getValue());
-                }
-            } else if (type.equals("java.lang.Integer")) {
-                if (env.getValue() == null) {
-                    value = Integer.valueOf(0);
-                } else {
-                    value = Integer.decode(env.getValue());
-                }
-            } else if (type.equals("java.lang.Long")) {
-                if (env.getValue() == null) {
-                    value = Long.valueOf(0);
-                } else {
-                    value = Long.decode(env.getValue());
-                }
-            } else if (type.equals("java.lang.Boolean")) {
-                value = Boolean.valueOf(env.getValue());
-            } else if (type.equals("java.lang.Double")) {
-                if (env.getValue() == null) {
-                    value = Double.valueOf(0);
-                } else {
-                    value = Double.valueOf(env.getValue());
-                }
-            } else if (type.equals("java.lang.Float")) {
-                if (env.getValue() == null) {
-                    value = Float.valueOf(0);
-                } else {
-                    value = Float.valueOf(env.getValue());
-                }
-            } else if (type.equals("java.lang.Character")) {
-                if (env.getValue() == null) {
-                    value = Character.valueOf((char) 0);
-                } else {
-                    if (env.getValue().length() == 1) {
-                        value = Character.valueOf(env.getValue().charAt(0));
+        Object value = lookForLookupRef(env);
+
+        if (value == null) {
+            // Instantiating a new instance of the correct object type, and
+            // initializing it.
+            String type = env.getType();
+            try {
+                if (type.equals("java.lang.String")) {
+                    value = env.getValue();
+                } else if (type.equals("java.lang.Byte")) {
+                    if (env.getValue() == null) {
+                        value = Byte.valueOf((byte) 0);
                     } else {
-                        throw new IllegalArgumentException();
+                        value = Byte.decode(env.getValue());
+                    }
+                } else if (type.equals("java.lang.Short")) {
+                    if (env.getValue() == null) {
+                        value = Short.valueOf((short) 0);
+                    } else {
+                        value = Short.decode(env.getValue());
+                    }
+                } else if (type.equals("java.lang.Integer")) {
+                    if (env.getValue() == null) {
+                        value = Integer.valueOf(0);
+                    } else {
+                        value = Integer.decode(env.getValue());
+                    }
+                } else if (type.equals("java.lang.Long")) {
+                    if (env.getValue() == null) {
+                        value = Long.valueOf(0);
+                    } else {
+                        value = Long.decode(env.getValue());
+                    }
+                } else if (type.equals("java.lang.Boolean")) {
+                    value = Boolean.valueOf(env.getValue());
+                } else if (type.equals("java.lang.Double")) {
+                    if (env.getValue() == null) {
+                        value = Double.valueOf(0);
+                    } else {
+                        value = Double.valueOf(env.getValue());
+                    }
+                } else if (type.equals("java.lang.Float")) {
+                    if (env.getValue() == null) {
+                        value = Float.valueOf(0);
+                    } else {
+                        value = Float.valueOf(env.getValue());
+                    }
+                } else if (type.equals("java.lang.Character")) {
+                    if (env.getValue() == null) {
+                        value = Character.valueOf((char) 0);
+                    } else {
+                        if (env.getValue().length() == 1) {
+                            value = Character.valueOf(env.getValue().charAt(0));
+                        } else {
+                            throw new IllegalArgumentException();
+                        }
+                    }
+                } else {
+                    value = constructEnvEntry(env.getType(), env.getValue());
+                    if (value == null) {
+                        log.error(sm.getString(
+                                "naming.invalidEnvEntryType", env.getName()));
                     }
                 }
-            } else {
-                value = constructEnvEntry(env.getType(), env.getValue());
-                if (value == null) {
-                    logger.error(sm.getString(
-                            "naming.invalidEnvEntryType", env.getName()));
-                }
+            } catch (NumberFormatException e) {
+                log.error(sm.getString("naming.invalidEnvEntryValue", env.getName()));
+            } catch (IllegalArgumentException e) {
+                log.error(sm.getString("naming.invalidEnvEntryValue", env.getName()));
             }
-        } catch (NumberFormatException e) {
-            logger.error(sm.getString("naming.invalidEnvEntryValue", env.getName()));
-        } catch (IllegalArgumentException e) {
-            logger.error(sm.getString("naming.invalidEnvEntryValue", env.getName()));
         }
 
         // Binding the object to the appropriate name
         if (value != null) {
             try {
-                if (logger.isDebugEnabled())
-                    logger.debug("  Adding environment entry " + env.getName());
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("naming.addEnvEntry", env.getName()));
+                }
                 createSubcontexts(envCtx, env.getName());
                 envCtx.bind(env.getName(), value);
             } catch (NamingException e) {
                 logger.error(sm.getString("naming.invalidEnvEntryValue", e));
             }
         }
-
     }
 
 
@@ -797,10 +825,26 @@
 
     /**
      * Set the specified local EJBs in the naming context.
+     *
+     * @param localEjb the local EJB descriptor (unused)
      */
     public void addLocalEjb(
             @SuppressWarnings("unused") ContextLocalEjb localEjb) {
         // NO-OP
+        // No factory in org.apache.naming.factory
+        // No reference in org.apache.naming
+    }
+
+
+    /**
+     * Set the specified message destination refs in the naming context.
+     *
+     * @param mdr the message destination ref descriptor (unused)
+     */
+    public void addMessageDestinationRef(MessageDestinationRef mdr) {
+        // NO-OP
+        // No factory in org.apache.naming.factory
+        // No reference in org.apache.naming
     }
 
 
@@ -809,128 +853,128 @@
      */
     public void addService(ContextService service) {
 
-        if (service.getWsdlfile() != null) {
-            URL wsdlURL = null;
+        Reference ref = lookForLookupRef(service);
 
-            try {
-                wsdlURL = new URL(service.getWsdlfile());
-            } catch (MalformedURLException e) {
-                // Ignore and carry on
-            }
-            if (wsdlURL == null) {
+        if (ref == null) {
+
+            if (service.getWsdlfile() != null) {
+                URL wsdlURL = null;
+
                 try {
-                    wsdlURL = ((Context) container).
-                                                    getServletContext().
-                                                    getResource(service.getWsdlfile());
+                    wsdlURL = new URL(service.getWsdlfile());
                 } catch (MalformedURLException e) {
                     // Ignore and carry on
                 }
-            }
-            if (wsdlURL == null) {
-                try {
-                    wsdlURL = ((Context) container).
-                                                    getServletContext().
-                                                    getResource("/" + service.getWsdlfile());
-                    logger.debug("  Changing service ref wsdl file for /"
-                                + service.getWsdlfile());
-                } catch (MalformedURLException e) {
-                    logger.error(sm.getString("naming.wsdlFailed", e));
+                if (wsdlURL == null) {
+                    try {
+                        wsdlURL = ((Context) container).getServletContext().getResource(
+                                service.getWsdlfile());
+                    } catch (MalformedURLException e) {
+                        // Ignore and carry on
+                    }
                 }
+                if (wsdlURL == null) {
+                    try {
+                        wsdlURL = ((Context) container).getServletContext().getResource(
+                                "/" + service.getWsdlfile());
+                        log.debug("  Changing service ref wsdl file for /"
+                                    + service.getWsdlfile());
+                    } catch (MalformedURLException e) {
+                        log.error(sm.getString("naming.wsdlFailed", e));
+                    }
+                }
+                if (wsdlURL == null)
+                    service.setWsdlfile(null);
+                else
+                    service.setWsdlfile(wsdlURL.toString());
             }
-            if (wsdlURL == null)
-                service.setWsdlfile(null);
-            else
-                service.setWsdlfile(wsdlURL.toString());
-        }
 
-        if (service.getJaxrpcmappingfile() != null) {
-            URL jaxrpcURL = null;
+            if (service.getJaxrpcmappingfile() != null) {
+                URL jaxrpcURL = null;
 
-            try {
-                jaxrpcURL = new URL(service.getJaxrpcmappingfile());
-            } catch (MalformedURLException e) {
-                // Ignore and carry on
-            }
-            if (jaxrpcURL == null) {
                 try {
-                    jaxrpcURL = ((Context) container).
-                                                    getServletContext().
-                                                    getResource(service.getJaxrpcmappingfile());
+                    jaxrpcURL = new URL(service.getJaxrpcmappingfile());
                 } catch (MalformedURLException e) {
                     // Ignore and carry on
                 }
-            }
-            if (jaxrpcURL == null) {
-                try {
-                    jaxrpcURL = ((Context) container).
-                                                    getServletContext().
-                                                    getResource("/" + service.getJaxrpcmappingfile());
-                    logger.debug("  Changing service ref jaxrpc file for /"
-                                + service.getJaxrpcmappingfile());
-                } catch (MalformedURLException e) {
-                    logger.error(sm.getString("naming.wsdlFailed", e));
+                if (jaxrpcURL == null) {
+                    try {
+                        jaxrpcURL = ((Context) container).getServletContext().getResource(
+                                service.getJaxrpcmappingfile());
+                    } catch (MalformedURLException e) {
+                        // Ignore and carry on
+                    }
                 }
+                if (jaxrpcURL == null) {
+                    try {
+                        jaxrpcURL = ((Context) container).getServletContext().getResource(
+                                "/" + service.getJaxrpcmappingfile());
+                        log.debug("  Changing service ref jaxrpc file for /"
+                                    + service.getJaxrpcmappingfile());
+                    } catch (MalformedURLException e) {
+                        log.error(sm.getString("naming.wsdlFailed", e));
+                    }
+                }
+                if (jaxrpcURL == null)
+                    service.setJaxrpcmappingfile(null);
+                else
+                    service.setJaxrpcmappingfile(jaxrpcURL.toString());
             }
-            if (jaxrpcURL == null)
-                service.setJaxrpcmappingfile(null);
-            else
-                service.setJaxrpcmappingfile(jaxrpcURL.toString());
-        }
 
-        // Create a reference to the resource.
-        Reference ref = new ServiceRef
-            (service.getName(), service.getInterface(), service.getServiceqname(),
-             service.getWsdlfile(), service.getJaxrpcmappingfile());
-        // Adding the additional port-component-ref, if any
-        Iterator<String> portcomponent = service.getServiceendpoints();
-        while (portcomponent.hasNext()) {
-            String serviceendpoint = portcomponent.next();
-            StringRefAddr refAddr = new StringRefAddr(ServiceRef.SERVICEENDPOINTINTERFACE, serviceendpoint);
-            ref.add(refAddr);
-            String portlink = service.getPortlink(serviceendpoint);
-            refAddr = new StringRefAddr(ServiceRef.PORTCOMPONENTLINK, portlink);
-            ref.add(refAddr);
-        }
-        // Adding the additional parameters, if any
-        Iterator<String> handlers = service.getHandlers();
-        while (handlers.hasNext()) {
-            String handlername = handlers.next();
-            ContextHandler handler = service.getHandler(handlername);
-            HandlerRef handlerRef = new HandlerRef(handlername, handler.getHandlerclass());
-            Iterator<String> localParts = handler.getLocalparts();
-            while (localParts.hasNext()) {
-                String localPart = localParts.next();
-                String namespaceURI = handler.getNamespaceuri(localPart);
-                handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_LOCALPART, localPart));
-                handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_NAMESPACE, namespaceURI));
+            // Create a reference to the resource.
+            ref = new ServiceRef(service.getName(), service.getInterface(),
+                    service.getServiceqname(), service.getWsdlfile(),
+                    service.getJaxrpcmappingfile());
+
+            // Adding the additional port-component-ref, if any
+            Iterator<String> portcomponent = service.getServiceendpoints();
+            while (portcomponent.hasNext()) {
+                String serviceendpoint = portcomponent.next();
+                StringRefAddr refAddr = new StringRefAddr(ServiceRef.SERVICEENDPOINTINTERFACE, serviceendpoint);
+                ref.add(refAddr);
+                String portlink = service.getPortlink(serviceendpoint);
+                refAddr = new StringRefAddr(ServiceRef.PORTCOMPONENTLINK, portlink);
+                ref.add(refAddr);
             }
-            Iterator<String> params = handler.listProperties();
-            while (params.hasNext()) {
-                String paramName = params.next();
-                String paramValue = (String) handler.getProperty(paramName);
-                handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_PARAMNAME, paramName));
-                handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_PARAMVALUE, paramValue));
+            // Adding the additional parameters, if any
+            Iterator<String> handlers = service.getHandlers();
+            while (handlers.hasNext()) {
+                String handlername = handlers.next();
+                ContextHandler handler = service.getHandler(handlername);
+                HandlerRef handlerRef = new HandlerRef(handlername, handler.getHandlerclass());
+                Iterator<String> localParts = handler.getLocalparts();
+                while (localParts.hasNext()) {
+                    String localPart = localParts.next();
+                    String namespaceURI = handler.getNamespaceuri(localPart);
+                    handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_LOCALPART, localPart));
+                    handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_NAMESPACE, namespaceURI));
+                }
+                Iterator<String> params = handler.listProperties();
+                while (params.hasNext()) {
+                    String paramName = params.next();
+                    String paramValue = (String) handler.getProperty(paramName);
+                    handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_PARAMNAME, paramName));
+                    handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_PARAMVALUE, paramValue));
+                }
+                for (int i = 0; i < handler.getSoapRolesSize(); i++) {
+                    handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_SOAPROLE, handler.getSoapRole(i)));
+                }
+                for (int i = 0; i < handler.getPortNamesSize(); i++) {
+                    handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_PORTNAME, handler.getPortName(i)));
+                }
+                ((ServiceRef) ref).addHandler(handlerRef);
             }
-            for (int i = 0; i < handler.getSoapRolesSize(); i++) {
-                handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_SOAPROLE, handler.getSoapRole(i)));
-            }
-            for (int i = 0; i < handler.getPortNamesSize(); i++) {
-                handlerRef.add(new StringRefAddr(HandlerRef.HANDLER_PORTNAME, handler.getPortName(i)));
-            }
-            ((ServiceRef) ref).addHandler(handlerRef);
         }
 
         try {
-            if (logger.isDebugEnabled()) {
-                logger.debug("  Adding service ref "
-                             + service.getName() + "  " + ref);
+            if (log.isDebugEnabled()) {
+                log.debug("  Adding service ref " + service.getName() + "  " + ref);
             }
             createSubcontexts(envCtx, service.getName());
             envCtx.bind(service.getName(), ref);
         } catch (NamingException e) {
             logger.error(sm.getString("naming.bindFailed", e));
         }
-
     }
 
 
@@ -939,23 +983,25 @@
      */
     public void addResource(ContextResource resource) {
 
-        // Create a reference to the resource.
-        Reference ref = new ResourceRef
-            (resource.getType(), resource.getDescription(),
-             resource.getScope(), resource.getAuth(),
-             resource.getSingleton());
-        // Adding the additional parameters, if any
-        Iterator<String> params = resource.listProperties();
-        while (params.hasNext()) {
-            String paramName = params.next();
-            String paramValue = (String) resource.getProperty(paramName);
-            StringRefAddr refAddr = new StringRefAddr(paramName, paramValue);
-            ref.add(refAddr);
+        Reference ref = lookForLookupRef(resource);
+
+        if (ref == null) {
+            // Create a reference to the resource.
+            ref = new ResourceRef(resource.getType(), resource.getDescription(),
+                    resource.getScope(), resource.getAuth(), resource.getSingleton());
+            // Adding the additional parameters, if any
+            Iterator<String> params = resource.listProperties();
+            while (params.hasNext()) {
+                String paramName = params.next();
+                String paramValue = (String) resource.getProperty(paramName);
+                StringRefAddr refAddr = new StringRefAddr(paramName, paramValue);
+                ref.add(refAddr);
+            }
         }
+
         try {
-            if (logger.isDebugEnabled()) {
-                logger.debug("  Adding resource ref "
-                             + resource.getName() + "  " + ref);
+            if (log.isDebugEnabled()) {
+                log.debug("  Adding resource ref " + resource.getName() + "  " + ref);
             }
             createSubcontexts(envCtx, resource.getName());
             envCtx.bind(resource.getName(), ref);
@@ -984,25 +1030,30 @@
      */
     public void addResourceEnvRef(ContextResourceEnvRef resourceEnvRef) {
 
-        // Create a reference to the resource env.
-        Reference ref = new ResourceEnvRef(resourceEnvRef.getType());
-        // Adding the additional parameters, if any
-        Iterator<String> params = resourceEnvRef.listProperties();
-        while (params.hasNext()) {
-            String paramName = params.next();
-            String paramValue = (String) resourceEnvRef.getProperty(paramName);
-            StringRefAddr refAddr = new StringRefAddr(paramName, paramValue);
-            ref.add(refAddr);
+        Reference ref = lookForLookupRef(resourceEnvRef);
+
+        if (ref == null) {
+            // Create a reference to the resource env.
+            ref = new ResourceEnvRef(resourceEnvRef.getType());
+            // Adding the additional parameters, if any
+            Iterator<String> params = resourceEnvRef.listProperties();
+            while (params.hasNext()) {
+                String paramName = params.next();
+                String paramValue = (String) resourceEnvRef.getProperty(paramName);
+                StringRefAddr refAddr = new StringRefAddr(paramName, paramValue);
+                ref.add(refAddr);
+            }
         }
+
         try {
-            if (logger.isDebugEnabled())
-                log.debug("  Adding resource env ref " + resourceEnvRef.getName());
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("naming.addResourceEnvRef", resourceEnvRef.getName()));
+            }
             createSubcontexts(envCtx, resourceEnvRef.getName());
             envCtx.bind(resourceEnvRef.getName(), ref);
         } catch (NamingException e) {
             logger.error(sm.getString("naming.bindFailed", e));
         }
-
     }
 
 
@@ -1050,7 +1101,7 @@
 
 
     /**
-     * Set the specified EJBs in the naming context.
+     * Remove the specified EJB from the naming context.
      */
     public void removeEjb(String name) {
 
@@ -1064,7 +1115,7 @@
 
 
     /**
-     * Set the specified environment entries in the naming context.
+     * Remove the specified environment entry from the naming context.
      */
     public void removeEnvironment(String name) {
 
@@ -1078,7 +1129,7 @@
 
 
     /**
-     * Set the specified local EJBs in the naming context.
+     * Remove the specified local EJB from the naming context.
      */
     public void removeLocalEjb(String name) {
 
@@ -1092,7 +1143,26 @@
 
 
     /**
-     * Set the specified web services in the naming context.
+     * Remove the specified message destination ref from the naming context.
+     *
+     * @param name the name of the message destination ref which should be
+     *             removed
+     */
+    public void removeMessageDestinationRef(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            log.error(sm.getString("naming.unbindFailed", e));
+        }
+
+    }
+
+
+    /**
+     * Remove the specified web service from the naming context.
+     *
+     * @param name the name of the web service which should be removed
      */
     public void removeService(String name) {
 
@@ -1106,7 +1176,7 @@
 
 
     /**
-     * Set the specified resources in the naming context.
+     * Remove the specified resource from the naming context.
      */
     public void removeResource(String name) {
 
@@ -1125,7 +1195,11 @@
 
 
     /**
-     * Set the specified resources in the naming context.
+     * Remove the specified resource environment reference from the naming
+     * context.
+     *
+     * @param name the name of the resource environment reference which should
+     *             be removed
      */
     public void removeResourceEnvRef(String name) {
 
@@ -1139,7 +1213,7 @@
 
 
     /**
-     * Set the specified resources in the naming context.
+     * Remove the specified resource link from the naming context.
      */
     public void removeResourceLink(String name) {
 
@@ -1176,4 +1250,17 @@
     }
 
 
+    /**
+     * Gets look up reference from resource if exist.
+     *
+     * @param resourceBase resource base object
+     * @return lookup ref
+     */
+    private LookupRef lookForLookupRef(ResourceBase resourceBase) {
+        String lookupName = resourceBase.getLookupName();
+        if ((lookupName != null && !lookupName.equals(""))) {
+            return new LookupRef(resourceBase.getType(), lookupName);
+        }
+        return null;
+    }
 }
diff --git a/java/org/apache/catalina/deploy/LocalStrings.properties b/java/org/apache/catalina/deploy/LocalStrings.properties
index ddcdf76..11e6268 100644
--- a/java/org/apache/catalina/deploy/LocalStrings.properties
+++ b/java/org/apache/catalina/deploy/LocalStrings.properties
@@ -18,6 +18,8 @@
 namingResources.cleanupNoClose=Resource [{0}] in container [{1}] does not have a [{2}] method so no cleanup was performed for that resource
 namingResources.cleanupNoContext=Failed to retrieve JNDI naming context for container [{0}] so no cleanup was performed for that container
 namingResources.cleanupNoResource=Failed to retrieve JNDI resource [{0}] for container [{1}] so no cleanup was performed for that resource
+namingResources.ejbLookupLink=The EJB reference [{0}] specifies both a ejb-link and a lookup-name
+namingResources.envEntryLookupValue=The environment entry [{0}] specifies both a lookup-name and a value
 namingResources.mbeanCreateFail=Failed to create MBean for naming resource [{0}]
 namingResources.mbeanDestroyFail=Failed to destroy MBean for naming resource [{0}]
 namingResources.resourceTypeFail=The JNDI resource named [{0}] is of type [{1}] but the type is inconsistent with the type(s) of the injection target(s) configured for that resource
diff --git a/java/org/apache/catalina/deploy/NamingResourcesImpl.java b/java/org/apache/catalina/deploy/NamingResourcesImpl.java
index ab90515..b74e639 100644
--- a/java/org/apache/catalina/deploy/NamingResourcesImpl.java
+++ b/java/org/apache/catalina/deploy/NamingResourcesImpl.java
@@ -24,6 +24,7 @@
 import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 import javax.naming.NamingException;
@@ -212,6 +213,15 @@
      */
     public void addEjb(ContextEjb ejb) {
 
+        // Entries with lookup-name and ejb-link are an error (EE.5.5.2 / EE.5.5.3)
+        String ejbLink = ejb.getLink();
+        String lookupName = ejb.getLookupName();
+
+        if (ejbLink != null && ejbLink.length() > 0 && lookupName != null && lookupName.length() > 0) {
+            throw new IllegalArgumentException(
+                    sm.getString("namingResources.ejbLookupLink", ejb.getName()));
+        }
+
         if (entries.contains(ejb.getName())) {
             return;
         } else {
@@ -260,12 +270,22 @@
             }
         }
 
+        List<InjectionTarget> injectionTargets = environment.getInjectionTargets();
+        String value = environment.getValue();
+        String lookupName = environment.getLookupName();
+
         // Entries with injection targets but no value are effectively ignored
-        if (environment.getInjectionTargets() != null && environment.getInjectionTargets().size() > 0 &&
-                (environment.getValue() == null || environment.getValue().length() == 0)) {
+        if (injectionTargets != null && injectionTargets.size() > 0 &&
+                (value == null || value.length() == 0)) {
             return;
         }
 
+        // Entries with lookup-name and value are an error (EE.5.4.1.3)
+        if (value != null && value.length() > 0 && lookupName != null && lookupName.length() > 0) {
+            throw new IllegalArgumentException(
+                    sm.getString("namingResources.envEntryLookupValue", environment.getName()));
+        }
+
         if (!checkResourceType(environment)) {
             throw new IllegalArgumentException(sm.getString(
                     "namingResources.resourceTypeFail", environment.getName(),
diff --git a/test/org/apache/naming/TestEnvEntry.java b/test/org/apache/naming/TestEnvEntry.java
new file mode 100644
index 0000000..6eebd1f
--- /dev/null
+++ b/test/org/apache/naming/TestEnvEntry.java
@@ -0,0 +1,136 @@
+/*
+ * 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.naming;
+
+import java.io.File;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+public class TestEnvEntry extends TomcatBaseTest {
+
+    @Test
+    public void testEnvEntryBasic() throws Exception {
+        doTestJndiLookup("env-entry/basic", "basic-value");
+    }
+
+
+    @Test
+    public void testEnvEntryValid() throws Exception {
+        doTestJndiLookup("env-entry/valid", "valid");
+    }
+
+
+    @Test
+    public void testEnvEntryInvalid() throws Exception {
+        doTestJndiLookup("env-entry/invalid", "Not Found");
+    }
+
+
+    @Test
+    public void testEnvEntryInjectField() throws Exception {
+        doTestJndiInjection("property1", "inject-value-1");
+    }
+
+
+    @Test
+    public void testEnvEntryInjectProperty() throws Exception {
+        doTestJndiInjection("property2", "inject-value-2");
+    }
+
+
+    @Test
+    public void testEnvEntryInjectFieldNoType() throws Exception {
+        doTestJndiInjection("property3", "inject-value-3");
+    }
+
+
+    @Test
+    public void testEnvEntryInjectionNoValue() throws Exception {
+        doTestJndiLookup("env-entry/injectNoValue", "Not Found");
+    }
+
+
+    @Test
+    public void testEnvEntryLookup() throws Exception {
+        doTestJndiLookup("env-entry/lookup", "basic-value");
+    }
+
+
+    @Test
+    public void testEnvEntryLookupCircular() throws Exception {
+        doTestJndiLookup("env-entry/circular1", "Naming Error");
+    }
+
+
+    @Test
+    public void testEnvEntryLookupInvalid() throws Exception {
+        doTestJndiLookup("env-entry/lookup-invalid", "Naming Error");
+    }
+
+
+    private void doTestJndiLookup(String jndiName, String expected) throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp-fragments");
+        tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+
+        tomcat.enableNaming();
+        tomcat.start();
+
+        ByteChunk out = new ByteChunk();
+
+        int rc = getUrl("http://localhost:" + getPort() + "/test/jndi.jsp?jndiName=" +
+                jndiName, out, null);
+        Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+
+        // JSP has leading and trailing white-space
+        String result = out.toString().trim();
+        Assert.assertEquals(expected, result);
+    }
+
+
+    private void doTestJndiInjection(String injectionName, String expected) throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp-fragments");
+        Context context = tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+
+        Tomcat.addServlet(context, "InjectionServlet", "org.apache.naming.TesterInjectionServlet");
+        context.addServletMappingDecoded("/injection", "InjectionServlet");
+
+        tomcat.enableNaming();
+        tomcat.start();
+
+        ByteChunk out = new ByteChunk();
+
+        int rc = getUrl("http://localhost:" + getPort() + "/test/injection?injectionName=" +
+                injectionName, out, null);
+        Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+
+        // JSP has leading and trailing white-space
+        String result = out.toString().trim();
+        Assert.assertEquals(expected, result);
+    }
+}
diff --git a/test/org/apache/naming/TesterEnvEntry.java b/test/org/apache/naming/TesterEnvEntry.java
new file mode 100644
index 0000000..62c589d
--- /dev/null
+++ b/test/org/apache/naming/TesterEnvEntry.java
@@ -0,0 +1,33 @@
+/*
+ * 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.naming;
+
+public class TesterEnvEntry {
+
+    private static final String VALID = "valid";
+
+    public TesterEnvEntry(String value) {
+        if (!VALID.equals(value)) {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public String toString() {
+        return VALID;
+    }
+}
diff --git a/test/org/apache/naming/TesterInjectionServlet.java b/test/org/apache/naming/TesterInjectionServlet.java
new file mode 100644
index 0000000..7075f5a
--- /dev/null
+++ b/test/org/apache/naming/TesterInjectionServlet.java
@@ -0,0 +1,68 @@
+/*
+ * 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.naming;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tomcat.util.IntrospectionUtils;
+
+public class TesterInjectionServlet extends HttpServlet {
+
+    private static final long serialVersionUID = 1L;
+
+    private String property1 = null;
+    public String getProperty1() { return property1; }
+
+    // Not used directly.
+    // Here to ensure properties are injected in preference to fields
+    private String property2 = null;
+    public void setProperty2a(String property2) { this.property2 = property2; }
+    public String getProperty2a() { return property2; }
+
+    private String property2a = null;
+    public void setProperty2(String property2) { this.property2a = property2; }
+    public String getProperty2() { return property2a; }
+
+    private String property3 = null;
+    public String getProperty3() { return property3; }
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+
+        resp.setContentType("text/plain");
+        resp.setCharacterEncoding("UTF-8");
+
+        String injectionName = req.getParameter("injectionName");
+
+        PrintWriter pw = resp.getWriter();
+        pw.print(IntrospectionUtils.getProperty(this, injectionName));
+
+        // The property should tyake precedence over the field and this should
+        // be null
+        if (getProperty2a() != null) {
+            pw.println();
+            pw.print(getProperty2a());
+        }
+    }
+}
diff --git a/test/webapp-fragments/WEB-INF/web.xml b/test/webapp-fragments/WEB-INF/web.xml
index 6f96c83..156c906 100644
--- a/test/webapp-fragments/WEB-INF/web.xml
+++ b/test/webapp-fragments/WEB-INF/web.xml
@@ -86,4 +86,94 @@
     <env-entry-type>java.lang.Integer</env-entry-type>
     <env-entry-value>66</env-entry-value>
   </env-entry>
+
+  <servlet>
+    <servlet-name>injection</servlet-name>
+    <servlet-class>org.apache.naming.TesterInjectionServlet</servlet-class>
+  </servlet>
+  <servlet-mapping>
+    <servlet-name>injection</servlet-name>
+    <url-pattern>/injection</url-pattern>
+  </servlet-mapping>
+
+  <env-entry>
+    <env-entry-name>env-entry/basic</env-entry-name>
+    <env-entry-type>java.lang.String</env-entry-type>
+    <env-entry-value>basic-value</env-entry-value>
+  </env-entry>
+
+  <env-entry>
+    <env-entry-name>env-entry/valid</env-entry-name>
+    <env-entry-type>org.apache.naming.TesterEnvEntry</env-entry-type>
+    <env-entry-value>valid</env-entry-value>
+  </env-entry>
+
+  <env-entry>
+    <env-entry-name>env-entry/invalid</env-entry-name>
+    <env-entry-type>org.apache.naming.TesterEnvEntry</env-entry-type>
+    <env-entry-value>invalid</env-entry-value>
+  </env-entry>
+
+  <env-entry>
+    <env-entry-name>env-entry/injectField</env-entry-name>
+    <env-entry-type>java.lang.String</env-entry-type>
+    <env-entry-value>inject-value-1</env-entry-value>
+    <injection-target>
+      <injection-target-class>org.apache.naming.TesterInjectionServlet</injection-target-class>
+      <injection-target-name>property1</injection-target-name>
+    </injection-target>
+  </env-entry>
+
+  <env-entry>
+    <env-entry-name>env-entry/injectProperty</env-entry-name>
+    <env-entry-type>java.lang.String</env-entry-type>
+    <env-entry-value>inject-value-2</env-entry-value>
+    <injection-target>
+      <injection-target-class>org.apache.naming.TesterInjectionServlet</injection-target-class>
+      <injection-target-name>property2</injection-target-name>
+    </injection-target>
+  </env-entry>
+
+  <env-entry>
+    <env-entry-name>env-entry/injectFieldNoType</env-entry-name>
+    <env-entry-value>inject-value-3</env-entry-value>
+    <injection-target>
+      <injection-target-class>org.apache.naming.TesterInjectionServlet</injection-target-class>
+      <injection-target-name>property3</injection-target-name>
+    </injection-target>
+  </env-entry>
+
+  <env-entry>
+    <env-entry-name>env-entry/injectNoValue</env-entry-name>
+    <env-entry-type>java.lang.String</env-entry-type>
+    <injection-target>
+      <injection-target-class>org.apache.naming.TesterInjectionServlet</injection-target-class>
+      <injection-target-name>property4</injection-target-name>
+    </injection-target>
+  </env-entry>
+
+  <env-entry>
+    <env-entry-name>env-entry/lookup</env-entry-name>
+    <env-entry-type>java.lang.String</env-entry-type>
+    <lookup-name>java:comp/env/env-entry/basic</lookup-name>
+  </env-entry>
+
+  <env-entry>
+    <env-entry-name>env-entry/circular1</env-entry-name>
+    <env-entry-type>java.lang.String</env-entry-type>
+    <lookup-name>java:comp/env/env-entry/circular2</lookup-name>
+  </env-entry>
+
+  <env-entry>
+    <env-entry-name>env-entry/circular2</env-entry-name>
+    <env-entry-type>java.lang.String</env-entry-type>
+    <lookup-name>java:comp/env/env-entry/circular1</lookup-name>
+  </env-entry>
+
+  <env-entry>
+    <env-entry-name>env-entry/lookup-invalid</env-entry-name>
+    <env-entry-type>java.lang.Integer</env-entry-type>
+    <lookup-name>java:comp/env/env-entry/basic</lookup-name>
+  </env-entry>
+
 </web-app>
\ No newline at end of file
diff --git a/test/webapp-fragments/jndi.jsp b/test/webapp-fragments/jndi.jsp
new file mode 100644
index 0000000..ff7e506
--- /dev/null
+++ b/test/webapp-fragments/jndi.jsp
@@ -0,0 +1,31 @@
+<%--
+ 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.
+--%>
+<%@page contentType="text/plain" pageEncoding="UTF-8"%><%
+    String jndiName = request.getParameter("jndiName");
+
+    javax.naming.Context initCtx = new javax.naming.InitialContext();
+    javax.naming.Context envCtx = (javax.naming.Context) initCtx.lookup("java:comp/env");
+
+    try {
+        Object obj = envCtx.lookup(jndiName);
+        out.println(obj.toString());
+    } catch (javax.naming.NameNotFoundException e) {
+        out.println("Not Found");
+    } catch (javax.naming.NamingException e) {
+        out.println("Naming Error");
+    }
+%>
\ No newline at end of file
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index ea57dc9..5618e3e 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -81,6 +81,10 @@
         Refactor the <code>org.apache.naming</code> package to reduce duplicate
         code. Duplicate code identified by the Simian tool. (markt)
       </scode>
+      <fix>
+        <bug>50019</bug>: Add support for <code>&lt;lookup-name&gt;</code>.
+        Based on a patch by Gurkan Erdogdu. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Jasper">