1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.catalina.deploy;
18
19 import java.beans.PropertyChangeListener;
20 import java.beans.PropertyChangeSupport;
21 import java.io.Serializable;
22 import java.lang.reflect.Field;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30
31 import javax.naming.NamingException;
32
33 import org.apache.catalina.Container;
34 import org.apache.catalina.Context;
35 import org.apache.catalina.Engine;
36 import org.apache.catalina.JmxEnabled;
37 import org.apache.catalina.LifecycleException;
38 import org.apache.catalina.LifecycleState;
39 import org.apache.catalina.Server;
40 import org.apache.catalina.mbeans.MBeanUtils;
41 import org.apache.catalina.util.Introspection;
42 import org.apache.catalina.util.LifecycleMBeanBase;
43 import org.apache.juli.logging.Log;
44 import org.apache.juli.logging.LogFactory;
45 import org.apache.naming.ContextBindings;
46 import org.apache.tomcat.util.ExceptionUtils;
47 import org.apache.tomcat.util.descriptor.web.ContextEjb;
48 import org.apache.tomcat.util.descriptor.web.ContextEnvironment;
49 import org.apache.tomcat.util.descriptor.web.ContextLocalEjb;
50 import org.apache.tomcat.util.descriptor.web.ContextResource;
51 import org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef;
52 import org.apache.tomcat.util.descriptor.web.ContextResourceLink;
53 import org.apache.tomcat.util.descriptor.web.ContextService;
54 import org.apache.tomcat.util.descriptor.web.ContextTransaction;
55 import org.apache.tomcat.util.descriptor.web.InjectionTarget;
56 import org.apache.tomcat.util.descriptor.web.MessageDestinationRef;
57 import org.apache.tomcat.util.descriptor.web.NamingResources;
58 import org.apache.tomcat.util.descriptor.web.ResourceBase;
59 import org.apache.tomcat.util.res.StringManager;
60
61
62 /**
63  * Holds and manages the naming resources defined in the J2EE Enterprise
64  * Naming Context and their associated JNDI context.
65  *
66  * @author Remy Maucherat
67  */

68 public class NamingResourcesImpl extends LifecycleMBeanBase
69         implements Serializable, NamingResources {
70
71     private static final long serialVersionUID = 1L;
72
73     private static final Log log = LogFactory.getLog(NamingResourcesImpl.class);
74
75     private static final StringManager sm = StringManager.getManager(NamingResourcesImpl.class);
76
77     private volatile boolean resourceRequireExplicitRegistration = false;
78
79     // ----------------------------------------------------------- Constructors
80
81
82     /**
83      * Create a new NamingResources instance.
84      */

85     public NamingResourcesImpl() {
86         // NOOP
87     }
88
89
90     // ----------------------------------------------------- Instance Variables
91
92
93     /**
94      * Associated container object.
95      */

96     private Object container = null;
97
98
99     /**
100      * Set of naming entries, keyed by name.
101      */

102     private final Set<String> entries = new HashSet<>();
103
104
105     /**
106      * The EJB resource references for this web application, keyed by name.
107      */

108     private final Map<String, ContextEjb> ejbs = new HashMap<>();
109
110
111     /**
112      * The environment entries for this web application, keyed by name.
113      */

114     private final Map<String, ContextEnvironment> envs = new HashMap<>();
115
116
117     /**
118      * The local  EJB resource references for this web application, keyed by
119      * name.
120      */

121     private final Map<String, ContextLocalEjb> localEjbs = new HashMap<>();
122
123
124     /**
125      * The message destination references for this web application,
126      * keyed by name.
127      */

128     private final Map<String, MessageDestinationRef> mdrs = new HashMap<>();
129
130
131     /**
132      * The resource environment references for this web application,
133      * keyed by name.
134      */

135     private final HashMap<String, ContextResourceEnvRef> resourceEnvRefs =
136         new HashMap<>();
137
138
139     /**
140      * The resource references for this web application, keyed by name.
141      */

142     private final HashMap<String, ContextResource> resources =
143         new HashMap<>();
144
145
146     /**
147      * The resource links for this web application, keyed by name.
148      */

149     private final HashMap<String, ContextResourceLink> resourceLinks =
150         new HashMap<>();
151
152
153     /**
154      * The web service references for this web application, keyed by name.
155      */

156     private final HashMap<String, ContextService> services =
157         new HashMap<>();
158
159
160     /**
161      * The transaction for this webapp.
162      */

163     private ContextTransaction transaction = null;
164
165
166     /**
167      * The property change support for this component.
168      */

169     protected final PropertyChangeSupport support =
170             new PropertyChangeSupport(this);
171
172
173     // ------------------------------------------------------------- Properties
174
175
176     /**
177      * @return the container with which the naming resources are associated.
178      */

179     @Override
180     public Object getContainer() {
181         return container;
182     }
183
184
185     /**
186      * Set the container with which the naming resources are associated.
187      * @param container the associated with the resources
188      */

189     public void setContainer(Object container) {
190         this.container = container;
191     }
192
193
194     /**
195      * Set the transaction object.
196      * @param transaction the transaction descriptor
197      */

198     public void setTransaction(ContextTransaction transaction) {
199         this.transaction = transaction;
200     }
201
202
203     /**
204      * @return the transaction object.
205      */

206     public ContextTransaction getTransaction() {
207         return transaction;
208     }
209
210
211     /**
212      * Add an EJB resource reference for this web application.
213      *
214      * @param ejb New EJB resource reference
215      */

216     public void addEjb(ContextEjb ejb) {
217
218         // Entries with lookup-name and ejb-link are an error (EE.5.5.2 / EE.5.5.3)
219         String ejbLink = ejb.getLink();
220         String lookupName = ejb.getLookupName();
221
222         if (ejbLink != null && ejbLink.length() > 0 && lookupName != null && lookupName.length() > 0) {
223             throw new IllegalArgumentException(
224                     sm.getString("namingResources.ejbLookupLink", ejb.getName()));
225         }
226
227         if (entries.contains(ejb.getName())) {
228             return;
229         } else {
230             entries.add(ejb.getName());
231         }
232
233         synchronized (ejbs) {
234             ejb.setNamingResources(this);
235             ejbs.put(ejb.getName(), ejb);
236         }
237         support.firePropertyChange("ejb"null, ejb);
238
239     }
240
241
242     /**
243      * Add an environment entry for this web application.
244      *
245      * @param environment New environment entry
246      */

247     @Override
248     public void addEnvironment(ContextEnvironment environment) {
249
250         if (entries.contains(environment.getName())) {
251             ContextEnvironment ce = findEnvironment(environment.getName());
252             ContextResourceLink rl = findResourceLink(environment.getName());
253             if (ce != null) {
254                 if (ce.getOverride()) {
255                     removeEnvironment(environment.getName());
256                 } else {
257                     return;
258                 }
259             } else if (rl != null) {
260                 // Link. Need to look at the global resources
261                 NamingResourcesImpl global = getServer().getGlobalNamingResources();
262                 if (global.findEnvironment(rl.getGlobal()) != null) {
263                     if (global.findEnvironment(rl.getGlobal()).getOverride()) {
264                         removeResourceLink(environment.getName());
265                     } else {
266                         return;
267                     }
268                 }
269             } else {
270                 // It exists but it isn't an env or a res link...
271                 return;
272             }
273         }
274
275         List<InjectionTarget> injectionTargets = environment.getInjectionTargets();
276         String value = environment.getValue();
277         String lookupName = environment.getLookupName();
278
279         // Entries with injection targets but no value are effectively ignored
280         if (injectionTargets != null && injectionTargets.size() > 0 &&
281                 (value == null || value.length() == 0)) {
282             return;
283         }
284
285         // Entries with lookup-name and value are an error (EE.5.4.1.3)
286         if (value != null && value.length() > 0 && lookupName != null && lookupName.length() > 0) {
287             throw new IllegalArgumentException(
288                     sm.getString("namingResources.envEntryLookupValue", environment.getName()));
289         }
290
291         if (!checkResourceType(environment)) {
292             throw new IllegalArgumentException(sm.getString(
293                     "namingResources.resourceTypeFail", environment.getName(),
294                     environment.getType()));
295         }
296
297         entries.add(environment.getName());
298
299         synchronized (envs) {
300             environment.setNamingResources(this);
301             envs.put(environment.getName(), environment);
302         }
303         support.firePropertyChange("environment"null, environment);
304
305         // Register with JMX
306         if (resourceRequireExplicitRegistration) {
307             try {
308                 MBeanUtils.createMBean(environment);
309             } catch (Exception e) {
310                 log.warn(sm.getString("namingResources.mbeanCreateFail",
311                         environment.getName()), e);
312             }
313         }
314     }
315
316     // Container should be an instance of Server or Context. If it is anything
317     // elsereturn null which will trigger a NPE.
318     private Server getServer() {
319         if (container instanceof Server) {
320             return (Server) container;
321         }
322         if (container instanceof Context) {
323             // Could do this in one go. Lots of casts so split out for clarity
324             Engine engine =
325                 (Engine) ((Context) container).getParent().getParent();
326             return engine.getService().getServer();
327         }
328         return null;
329     }
330
331     /**
332      * Add a local EJB resource reference for this web application.
333      *
334      * @param ejb New EJB resource reference
335      */

336     public void addLocalEjb(ContextLocalEjb ejb) {
337
338         if (entries.contains(ejb.getName())) {
339             return;
340         } else {
341             entries.add(ejb.getName());
342         }
343
344         synchronized (localEjbs) {
345             ejb.setNamingResources(this);
346             localEjbs.put(ejb.getName(), ejb);
347         }
348         support.firePropertyChange("localEjb"null, ejb);
349
350     }
351
352
353     /**
354      * Add a message destination reference for this web application.
355      *
356      * @param mdr New message destination reference
357      */

358     public void addMessageDestinationRef(MessageDestinationRef mdr) {
359
360         if (entries.contains(mdr.getName())) {
361             return;
362         } else {
363             if (!checkResourceType(mdr)) {
364                 throw new IllegalArgumentException(sm.getString(
365                         "namingResources.resourceTypeFail", mdr.getName(),
366                         mdr.getType()));
367             }
368             entries.add(mdr.getName());
369         }
370
371         synchronized (mdrs) {
372             mdr.setNamingResources(this);
373             mdrs.put(mdr.getName(), mdr);
374         }
375         support.firePropertyChange("messageDestinationRef"null, mdr);
376
377     }
378
379
380     /**
381      * Add a property change listener to this component.
382      *
383      * @param listener The listener to add
384      */

385     public void addPropertyChangeListener(PropertyChangeListener listener) {
386
387         support.addPropertyChangeListener(listener);
388
389     }
390
391
392     /**
393      * Add a resource reference for this web application.
394      *
395      * @param resource New resource reference
396      */

397     @Override
398     public void addResource(ContextResource resource) {
399
400         if (entries.contains(resource.getName())) {
401             return;
402         } else {
403             if (!checkResourceType(resource)) {
404                 throw new IllegalArgumentException(sm.getString(
405                         "namingResources.resourceTypeFail", resource.getName(),
406                         resource.getType()));
407             }
408             entries.add(resource.getName());
409         }
410
411         synchronized (resources) {
412             resource.setNamingResources(this);
413             resources.put(resource.getName(), resource);
414         }
415         support.firePropertyChange("resource"null, resource);
416
417         // Register with JMX
418         if (resourceRequireExplicitRegistration) {
419             try {
420                 MBeanUtils.createMBean(resource);
421             } catch (Exception e) {
422                 log.warn(sm.getString("namingResources.mbeanCreateFail",
423                         resource.getName()), e);
424             }
425         }
426     }
427
428
429     /**
430      * Add a resource environment reference for this web application.
431      *
432      * @param resource The resource
433      */

434     public void addResourceEnvRef(ContextResourceEnvRef resource) {
435
436         if (entries.contains(resource.getName())) {
437             return;
438         } else {
439             if (!checkResourceType(resource)) {
440                 throw new IllegalArgumentException(sm.getString(
441                         "namingResources.resourceTypeFail", resource.getName(),
442                         resource.getType()));
443             }
444             entries.add(resource.getName());
445         }
446
447         synchronized (resourceEnvRefs) {
448             resource.setNamingResources(this);
449             resourceEnvRefs.put(resource.getName(), resource);
450         }
451         support.firePropertyChange("resourceEnvRef"null, resource);
452
453     }
454
455
456     /**
457      * Add a resource link for this web application.
458      *
459      * @param resourceLink New resource link
460      */

461     @Override
462     public void addResourceLink(ContextResourceLink resourceLink) {
463
464         if (entries.contains(resourceLink.getName())) {
465             return;
466         } else {
467             entries.add(resourceLink.getName());
468         }
469
470         synchronized (resourceLinks) {
471             resourceLink.setNamingResources(this);
472             resourceLinks.put(resourceLink.getName(), resourceLink);
473         }
474         support.firePropertyChange("resourceLink"null, resourceLink);
475
476         // Register with JMX
477         if (resourceRequireExplicitRegistration) {
478             try {
479                 MBeanUtils.createMBean(resourceLink);
480             } catch (Exception e) {
481                 log.warn(sm.getString("namingResources.mbeanCreateFail",
482                         resourceLink.getName()), e);
483             }
484         }
485     }
486
487
488     /**
489      * Add a web service reference for this web application.
490      *
491      * @param service New web service reference
492      */

493     public void addService(ContextService service) {
494
495         if (entries.contains(service.getName())) {
496             return;
497         } else {
498             entries.add(service.getName());
499         }
500
501         synchronized (services) {
502             service.setNamingResources(this);
503             services.put(service.getName(), service);
504         }
505         support.firePropertyChange("service"null, service);
506
507     }
508
509
510     /**
511      * @return the EJB resource reference with the specified name, if any;
512      * otherwise, return <code>null</code>.
513      *
514      * @param name Name of the desired EJB resource reference
515      */

516     public ContextEjb findEjb(String name) {
517
518         synchronized (ejbs) {
519             return ejbs.get(name);
520         }
521
522     }
523
524
525     /**
526      * @return the defined EJB resource references for this application.
527      * If there are none, a zero-length array is returned.
528      */

529     public ContextEjb[] findEjbs() {
530
531         synchronized (ejbs) {
532             ContextEjb results[] = new ContextEjb[ejbs.size()];
533             return ejbs.values().toArray(results);
534         }
535
536     }
537
538
539     /**
540      * @return the environment entry with the specified name, if any;
541      * otherwise, return <code>null</code>.
542      *
543      * @param name Name of the desired environment entry
544      */

545     public ContextEnvironment findEnvironment(String name) {
546
547         synchronized (envs) {
548             return envs.get(name);
549         }
550
551     }
552
553
554     /**
555      * @return the set of defined environment entries for this web
556      * application.  If none have been defined, a zero-length array
557      * is returned.
558      */

559     public ContextEnvironment[] findEnvironments() {
560
561         synchronized (envs) {
562             ContextEnvironment results[] = new ContextEnvironment[envs.size()];
563             return envs.values().toArray(results);
564         }
565
566     }
567
568
569     /**
570      * @return the local EJB resource reference with the specified name, if any;
571      * otherwise, return <code>null</code>.
572      *
573      * @param name Name of the desired EJB resource reference
574      */

575     public ContextLocalEjb findLocalEjb(String name) {
576
577         synchronized (localEjbs) {
578             return localEjbs.get(name);
579         }
580
581     }
582
583
584     /**
585      * @return the defined local EJB resource references for this application.
586      * If there are none, a zero-length array is returned.
587      */

588     public ContextLocalEjb[] findLocalEjbs() {
589
590         synchronized (localEjbs) {
591             ContextLocalEjb results[] = new ContextLocalEjb[localEjbs.size()];
592             return localEjbs.values().toArray(results);
593         }
594
595     }
596
597
598     /**
599      * @return the message destination reference with the specified name,
600      * if any; otherwise, return <code>null</code>.
601      *
602      * @param name Name of the desired message destination reference
603      */

604     public MessageDestinationRef findMessageDestinationRef(String name) {
605
606         synchronized (mdrs) {
607             return mdrs.get(name);
608         }
609
610     }
611
612
613     /**
614      * @return the defined message destination references for this application.
615      * If there are none, a zero-length array is returned.
616      */

617     public MessageDestinationRef[] findMessageDestinationRefs() {
618
619         synchronized (mdrs) {
620             MessageDestinationRef results[] =
621                 new MessageDestinationRef[mdrs.size()];
622             return mdrs.values().toArray(results);
623         }
624
625     }
626
627
628     /**
629      * @return the resource reference with the specified name, if any;
630      * otherwise return <code>null</code>.
631      *
632      * @param name Name of the desired resource reference
633      */

634     public ContextResource findResource(String name) {
635
636         synchronized (resources) {
637             return resources.get(name);
638         }
639
640     }
641
642
643     /**
644      * @return the resource link with the specified name, if any;
645      * otherwise return <code>null</code>.
646      *
647      * @param name Name of the desired resource link
648      */

649     public ContextResourceLink findResourceLink(String name) {
650
651         synchronized (resourceLinks) {
652             return resourceLinks.get(name);
653         }
654
655     }
656
657
658     /**
659      * @return the defined resource links for this application.  If
660      * none have been defined, a zero-length array is returned.
661      */

662     public ContextResourceLink[] findResourceLinks() {
663
664         synchronized (resourceLinks) {
665             ContextResourceLink results[] =
666                 new ContextResourceLink[resourceLinks.size()];
667             return resourceLinks.values().toArray(results);
668         }
669
670     }
671
672
673     /**
674      * @return the defined resource references for this application.  If
675      * none have been defined, a zero-length array is returned.
676      */

677     public ContextResource[] findResources() {
678
679         synchronized (resources) {
680             ContextResource results[] = new ContextResource[resources.size()];
681             return resources.values().toArray(results);
682         }
683
684     }
685
686
687     /**
688      * @return the resource environment reference type for the specified
689      * name, if any; otherwise return <code>null</code>.
690      *
691      * @param name Name of the desired resource environment reference
692      */

693     public ContextResourceEnvRef findResourceEnvRef(String name) {
694
695         synchronized (resourceEnvRefs) {
696             return resourceEnvRefs.get(name);
697         }
698
699     }
700
701
702     /**
703      * @return the set of resource environment reference names for this
704      * web application.  If none have been specified, a zero-length
705      * array is returned.
706      */

707     public ContextResourceEnvRef[] findResourceEnvRefs() {
708
709         synchronized (resourceEnvRefs) {
710             ContextResourceEnvRef results[] = new ContextResourceEnvRef[resourceEnvRefs.size()];
711             return resourceEnvRefs.values().toArray(results);
712         }
713
714     }
715
716
717     /**
718      * @return the web service reference for the specified
719      * name, if any; otherwise return <code>null</code>.
720      *
721      * @param name Name of the desired web service
722      */

723     public ContextService findService(String name) {
724
725         synchronized (services) {
726             return services.get(name);
727         }
728
729     }
730
731
732     /**
733      * @return the defined web service references for this application.  If
734      * none have been defined, a zero-length array is returned.
735      */

736     public ContextService[] findServices() {
737
738         synchronized (services) {
739             ContextService results[] = new ContextService[services.size()];
740             return services.values().toArray(results);
741         }
742
743     }
744
745
746     /**
747      * Remove any EJB resource reference with the specified name.
748      *
749      * @param name Name of the EJB resource reference to remove
750      */

751     public void removeEjb(String name) {
752
753         entries.remove(name);
754
755         ContextEjb ejb = null;
756         synchronized (ejbs) {
757             ejb = ejbs.remove(name);
758         }
759         if (ejb != null) {
760             support.firePropertyChange("ejb", ejb, null);
761             ejb.setNamingResources(null);
762         }
763
764     }
765
766
767     /**
768      * Remove any environment entry with the specified name.
769      *
770      * @param name Name of the environment entry to remove
771      */

772     @Override
773     public void removeEnvironment(String name) {
774
775         entries.remove(name);
776
777         ContextEnvironment environment = null;
778         synchronized (envs) {
779             environment = envs.remove(name);
780         }
781         if (environment != null) {
782             support.firePropertyChange("environment", environment, null);
783             // De-register with JMX
784             if (resourceRequireExplicitRegistration) {
785                 try {
786                     MBeanUtils.destroyMBean(environment);
787                 } catch (Exception e) {
788                     log.warn(sm.getString("namingResources.mbeanDestroyFail",
789                             environment.getName()), e);
790                 }
791             }
792             environment.setNamingResources(null);
793         }
794     }
795
796
797     /**
798      * Remove any local EJB resource reference with the specified name.
799      *
800      * @param name Name of the EJB resource reference to remove
801      */

802     public void removeLocalEjb(String name) {
803
804         entries.remove(name);
805
806         ContextLocalEjb localEjb = null;
807         synchronized (localEjbs) {
808             localEjb = localEjbs.remove(name);
809         }
810         if (localEjb != null) {
811             support.firePropertyChange("localEjb", localEjb, null);
812             localEjb.setNamingResources(null);
813         }
814
815     }
816
817
818     /**
819      * Remove any message destination reference with the specified name.
820      *
821      * @param name Name of the message destination resource reference to remove
822      */

823     public void removeMessageDestinationRef(String name) {
824
825         entries.remove(name);
826
827         MessageDestinationRef mdr = null;
828         synchronized (mdrs) {
829             mdr = mdrs.remove(name);
830         }
831         if (mdr != null) {
832             support.firePropertyChange("messageDestinationRef",
833                                        mdr, null);
834             mdr.setNamingResources(null);
835         }
836
837     }
838
839
840     /**
841      * Remove a property change listener from this component.
842      *
843      * @param listener The listener to remove
844      */

845     public void removePropertyChangeListener(PropertyChangeListener listener) {
846
847         support.removePropertyChangeListener(listener);
848
849     }
850
851
852     /**
853      * Remove any resource reference with the specified name.
854      *
855      * @param name Name of the resource reference to remove
856      */

857     @Override
858     public void removeResource(String name) {
859
860         entries.remove(name);
861
862         ContextResource resource = null;
863         synchronized (resources) {
864             resource = resources.remove(name);
865         }
866         if (resource != null) {
867             support.firePropertyChange("resource", resource, null);
868             // De-register with JMX
869             if (resourceRequireExplicitRegistration) {
870                 try {
871                     MBeanUtils.destroyMBean(resource);
872                 } catch (Exception e) {
873                     log.warn(sm.getString("namingResources.mbeanDestroyFail",
874                             resource.getName()), e);
875                 }
876             }
877             resource.setNamingResources(null);
878         }
879     }
880
881
882     /**
883      * Remove any resource environment reference with the specified name.
884      *
885      * @param name Name of the resource environment reference to remove
886      */

887     public void removeResourceEnvRef(String name) {
888
889         entries.remove(name);
890
891         ContextResourceEnvRef resourceEnvRef = null;
892         synchronized (resourceEnvRefs) {
893             resourceEnvRef =
894                 resourceEnvRefs.remove(name);
895         }
896         if (resourceEnvRef != null) {
897             support.firePropertyChange("resourceEnvRef", resourceEnvRef, null);
898             resourceEnvRef.setNamingResources(null);
899         }
900
901     }
902
903
904     /**
905      * Remove any resource link with the specified name.
906      *
907      * @param name Name of the resource link to remove
908      */

909     @Override
910     public void removeResourceLink(String name) {
911
912         entries.remove(name);
913
914         ContextResourceLink resourceLink = null;
915         synchronized (resourceLinks) {
916             resourceLink = resourceLinks.remove(name);
917         }
918         if (resourceLink != null) {
919             support.firePropertyChange("resourceLink", resourceLink, null);
920             // De-register with JMX
921             if (resourceRequireExplicitRegistration) {
922                 try {
923                     MBeanUtils.destroyMBean(resourceLink);
924                 } catch (Exception e) {
925                     log.warn(sm.getString("namingResources.mbeanDestroyFail",
926                             resourceLink.getName()), e);
927                 }
928             }
929             resourceLink.setNamingResources(null);
930         }
931     }
932
933
934     /**
935      * Remove any web service reference with the specified name.
936      *
937      * @param name Name of the web service reference to remove
938      */

939     public void removeService(String name) {
940
941         entries.remove(name);
942
943         ContextService service = null;
944         synchronized (services) {
945             service = services.remove(name);
946         }
947         if (service != null) {
948             support.firePropertyChange("service", service, null);
949             service.setNamingResources(null);
950         }
951
952     }
953
954
955     // ------------------------------------------------------- Lifecycle methods
956
957     @Override
958     protected void initInternal() throws LifecycleException {
959         super.initInternal();
960
961         // Set this before we register currently known naming resources to avoid
962         // timing issues. Duplication registration is not an issue.
963         resourceRequireExplicitRegistration = true;
964
965         for (ContextResource cr : resources.values()) {
966             try {
967                 MBeanUtils.createMBean(cr);
968             } catch (Exception e) {
969                 log.warn(sm.getString(
970                         "namingResources.mbeanCreateFail", cr.getName()), e);
971             }
972         }
973
974         for (ContextEnvironment ce : envs.values()) {
975             try {
976                 MBeanUtils.createMBean(ce);
977             } catch (Exception e) {
978                 log.warn(sm.getString(
979                         "namingResources.mbeanCreateFail", ce.getName()), e);
980             }
981         }
982
983         for (ContextResourceLink crl : resourceLinks.values()) {
984             try {
985                 MBeanUtils.createMBean(crl);
986             } catch (Exception e) {
987                 log.warn(sm.getString(
988                         "namingResources.mbeanCreateFail", crl.getName()), e);
989             }
990         }
991     }
992
993
994     @Override
995     protected void startInternal() throws LifecycleException {
996         fireLifecycleEvent(CONFIGURE_START_EVENT, null);
997         setState(LifecycleState.STARTING);
998     }
999
1000
1001     @Override
1002     protected void stopInternal() throws LifecycleException {
1003         cleanUp();
1004         setState(LifecycleState.STOPPING);
1005         fireLifecycleEvent(CONFIGURE_STOP_EVENT, null);
1006     }
1007
1008     /**
1009      * Close those resources that an explicit close may help clean-up faster.
1010      */

1011     private void cleanUp() {
1012         if (resources.size() == 0) {
1013             return;
1014         }
1015         javax.naming.Context ctxt;
1016         try {
1017             if (container instanceof Server) {
1018                 ctxt = ((Server) container).getGlobalNamingContext();
1019             } else {
1020                 ctxt = ContextBindings.getClassLoader();
1021                 ctxt = (javax.naming.Context) ctxt.lookup("comp/env");
1022             }
1023         } catch (NamingException e) {
1024             log.warn(sm.getString("namingResources.cleanupNoContext",
1025                     container), e);
1026             return;
1027         }
1028         for (ContextResource cr: resources.values()) {
1029             if (cr.getSingleton()) {
1030                 String closeMethod = cr.getCloseMethod();
1031                 if (closeMethod != null && closeMethod.length() > 0) {
1032                     String name = cr.getName();
1033                     Object resource;
1034                     try {
1035                          resource = ctxt.lookup(name);
1036                     } catch (NamingException e) {
1037                         log.warn(sm.getString(
1038                                 "namingResources.cleanupNoResource",
1039                                 cr.getName(), container), e);
1040                         continue;
1041                     }
1042                     cleanUp(resource, name, closeMethod);
1043                 }
1044             }
1045         }
1046     }
1047
1048
1049     /**
1050      * Clean up a resource by calling the defined close method. For example,
1051      * closing a database connection pool will close it's open connections. This
1052      * will happen on GC but that leaves db connections open that may cause
1053      * issues.
1054      *
1055      * @param resource  The resource to close.
1056      */

1057     private void cleanUp(Object resource, String name, String closeMethod) {
1058         // Look for a zero-arg close() method
1059         Method m = null;
1060         try {
1061             m = resource.getClass().getMethod(closeMethod, (Class<?>[]) null);
1062         } catch (SecurityException e) {
1063             log.debug(sm.getString("namingResources.cleanupCloseSecurity",
1064                     closeMethod, name, container));
1065             return;
1066         } catch (NoSuchMethodException e) {
1067             log.debug(sm.getString("namingResources.cleanupNoClose",
1068                     name, container, closeMethod));
1069             return;
1070         }
1071         try {
1072             m.invoke(resource, (Object[]) null);
1073         } catch (IllegalArgumentException | IllegalAccessException e) {
1074             log.warn(sm.getString("namingResources.cleanupCloseFailed",
1075                     closeMethod, name, container), e);
1076         } catch (InvocationTargetException e) {
1077             Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
1078             ExceptionUtils.handleThrowable(t);
1079             log.warn(sm.getString("namingResources.cleanupCloseFailed",
1080                     closeMethod, name, container), t);
1081         }
1082     }
1083
1084     @Override
1085     protected void destroyInternal() throws LifecycleException {
1086
1087         // Set this before we de-register currently known naming resources to
1088         // avoid timing issues. Duplication de-registration is not an issue.
1089         resourceRequireExplicitRegistration = false;
1090
1091         // Destroy in reverse order to create, although it should not matter
1092         for (ContextResourceLink crl : resourceLinks.values()) {
1093             try {
1094                 MBeanUtils.destroyMBean(crl);
1095             } catch (Exception e) {
1096                 log.warn(sm.getString(
1097                         "namingResources.mbeanDestroyFail", crl.getName()), e);
1098             }
1099         }
1100
1101         for (ContextEnvironment ce : envs.values()) {
1102             try {
1103                 MBeanUtils.destroyMBean(ce);
1104             } catch (Exception e) {
1105                 log.warn(sm.getString(
1106                         "namingResources.mbeanDestroyFail", ce.getName()), e);
1107             }
1108         }
1109
1110         for (ContextResource cr : resources.values()) {
1111             try {
1112                 MBeanUtils.destroyMBean(cr);
1113             } catch (Exception e) {
1114                 log.warn(sm.getString(
1115                         "namingResources.mbeanDestroyFail", cr.getName()), e);
1116             }
1117         }
1118
1119         super.destroyInternal();
1120     }
1121
1122
1123     @Override
1124     protected String getDomainInternal() {
1125         // Use the same domain as our associated container if we have one
1126         Object c = getContainer();
1127
1128         if (c instanceof JmxEnabled) {
1129             return ((JmxEnabled) c).getDomain();
1130         }
1131
1132         return null;
1133     }
1134
1135
1136     @Override
1137     protected String getObjectNameKeyProperties() {
1138         Object c = getContainer();
1139         if (c instanceof Container) {
1140             return "type=NamingResources" +
1141                     ((Container) c).getMBeanKeyProperties();
1142         }
1143         // Server or just unknown
1144         return "type=NamingResources";
1145     }
1146
1147     /**
1148      * Checks that the configuration of the type for the specified resource is
1149      * consistent with any injection targets and if the type is not specified,
1150      * tries to configure the type based on the injection targets
1151      *
1152      * @param resource  The resource to check
1153      *
1154      * @return  <code>true</code> if the type for the resource is now valid (if
1155      *          previously <code>null</code> this means it is now set) or
1156      *          <code>false</code> if the current resource type is inconsistent
1157      *          with the injection targets and/or cannot be determined
1158      */

1159     private boolean checkResourceType(ResourceBase resource) {
1160         if (!(container instanceof Context)) {
1161             // Only Context's will have injection targets
1162             return true;
1163         }
1164
1165         if (resource.getInjectionTargets() == null ||
1166                 resource.getInjectionTargets().size() == 0) {
1167             // No injection targets so use the defined type for the resource
1168             return true;
1169         }
1170
1171         Context context = (Context) container;
1172
1173         String typeName = resource.getType();
1174         Class<?> typeClass = null;
1175         if (typeName != null) {
1176             typeClass = Introspection.loadClass(context, typeName);
1177             if (typeClass == null) {
1178                 // Can't load the type - will trigger a failure later so don't
1179                 // fail here
1180                 return true;
1181             }
1182         }
1183
1184         Class<?> compatibleClass =
1185                 getCompatibleType(context, resource, typeClass);
1186         if (compatibleClass == null) {
1187             // Indicates that a compatible type could not be identified that
1188             // worked for all injection targets
1189             return false;
1190         }
1191
1192         resource.setType(compatibleClass.getCanonicalName());
1193         return true;
1194     }
1195
1196     private Class<?> getCompatibleType(Context context,
1197             ResourceBase resource, Class<?> typeClass) {
1198
1199         Class<?> result = null;
1200
1201         for (InjectionTarget injectionTarget : resource.getInjectionTargets()) {
1202             Class<?> clazz = Introspection.loadClass(
1203                     context, injectionTarget.getTargetClass());
1204             if (clazz == null) {
1205                 // Can't load class - therefore ignore this target
1206                 continue;
1207             }
1208
1209             // Look for a match
1210             String targetName = injectionTarget.getTargetName();
1211             // Look for a setter match first
1212             Class<?> targetType = getSetterType(clazz, targetName);
1213             if (targetType == null) {
1214                 // Try a field match if no setter match
1215                 targetType = getFieldType(clazz,targetName);
1216             }
1217             if (targetType == null) {
1218                 // No match - ignore this injection target
1219                 continue;
1220             }
1221             targetType = Introspection.convertPrimitiveType(targetType);
1222
1223             if (typeClass == null) {
1224                 // Need to find a common type amongst the injection targets
1225                 if (result == null) {
1226                     result = targetType;
1227                 } else if (targetType.isAssignableFrom(result)) {
1228                     // NO-OP - This will work
1229                 } else if (result.isAssignableFrom(targetType)) {
1230                     // Need to use more specific type
1231                     result = targetType;
1232                 } else {
1233                     // Incompatible types
1234                     return null;
1235                 }
1236             } else {
1237                 // Each injection target needs to be consistent with the defined
1238                 // type
1239                 if (targetType.isAssignableFrom(typeClass)) {
1240                     result = typeClass;
1241                 } else {
1242                     // Incompatible types
1243                     return null;
1244                 }
1245             }
1246         }
1247         return result;
1248     }
1249
1250     private Class<?> getSetterType(Class<?> clazz, String name) {
1251         Method[] methods = Introspection.getDeclaredMethods(clazz);
1252         if (methods != null && methods.length > 0) {
1253             for (Method method : methods) {
1254                 if (Introspection.isValidSetter(method) &&
1255                         Introspection.getPropertyName(method).equals(name)) {
1256                     return method.getParameterTypes()[0];
1257                 }
1258             }
1259         }
1260         return null;
1261     }
1262
1263     private Class<?> getFieldType(Class<?> clazz, String name) {
1264         Field[] fields = Introspection.getDeclaredFields(clazz);
1265         if (fields != null && fields.length > 0) {
1266             for (Field field : fields) {
1267                 if (field.getName().equals(name)) {
1268                     return field.getType();
1269                 }
1270             }
1271         }
1272         return null;
1273     }
1274 }
1275