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.mapper;
18
19 import java.util.ArrayList;
20 import java.util.List;
21
22 import org.apache.catalina.Container;
23 import org.apache.catalina.ContainerEvent;
24 import org.apache.catalina.ContainerListener;
25 import org.apache.catalina.Context;
26 import org.apache.catalina.Engine;
27 import org.apache.catalina.Host;
28 import org.apache.catalina.Lifecycle;
29 import org.apache.catalina.LifecycleEvent;
30 import org.apache.catalina.LifecycleException;
31 import org.apache.catalina.LifecycleListener;
32 import org.apache.catalina.LifecycleState;
33 import org.apache.catalina.Service;
34 import org.apache.catalina.WebResourceRoot;
35 import org.apache.catalina.Wrapper;
36 import org.apache.catalina.util.LifecycleMBeanBase;
37 import org.apache.juli.logging.Log;
38 import org.apache.juli.logging.LogFactory;
39 import org.apache.tomcat.util.res.StringManager;
40
41
42 /**
43  * Mapper listener.
44  *
45  * @author Remy Maucherat
46  * @author Costin Manolache
47  */

48 public class MapperListener extends LifecycleMBeanBase
49         implements ContainerListener, LifecycleListener {
50
51
52     private static final Log log = LogFactory.getLog(MapperListener.class);
53
54
55     // ----------------------------------------------------- Instance Variables
56     /**
57      * Associated mapper.
58      */

59     private final Mapper mapper;
60
61     /**
62      * Associated service
63      */

64     private final Service service;
65
66
67     /**
68      * The string manager for this package.
69      */

70     private static final StringManager sm =
71         StringManager.getManager(Constants.Package);
72
73     /**
74      * The domain (effectively the engine) this mapper is associated with
75      */

76     private final String domain = null;
77
78
79     // ----------------------------------------------------------- Constructors
80
81     /**
82      * Create mapper listener.
83      *
84      * @param service The service this listener is associated with
85      */

86     public MapperListener(Service service) {
87         this.service = service;
88         this.mapper = service.getMapper();
89     }
90
91
92     // ------------------------------------------------------- Lifecycle Methods
93
94     @Override
95     public void startInternal() throws LifecycleException {
96
97         setState(LifecycleState.STARTING);
98
99         Engine engine = service.getContainer();
100         if (engine == null) {
101             return;
102         }
103
104         findDefaultHost();
105
106         addListeners(engine);
107
108         Container[] conHosts = engine.findChildren();
109         for (Container conHost : conHosts) {
110             Host host = (Host) conHost;
111             if (!LifecycleState.NEW.equals(host.getState())) {
112                 // Registering the host will register the context and wrappers
113                 registerHost(host);
114             }
115         }
116     }
117
118
119     @Override
120     public void stopInternal() throws LifecycleException {
121         setState(LifecycleState.STOPPING);
122
123         Engine engine = service.getContainer();
124         if (engine == null) {
125             return;
126         }
127         removeListeners(engine);
128     }
129
130
131     @Override
132     protected String getDomainInternal() {
133         if (service instanceof LifecycleMBeanBase) {
134             return service.getDomain();
135         } else {
136             return null;
137         }
138     }
139
140
141     @Override
142     protected String getObjectNameKeyProperties() {
143         // Same as connector but Mapper rather than Connector
144         return "type=Mapper";
145     }
146
147     // --------------------------------------------- Container Listener methods
148
149     @Override
150     public void containerEvent(ContainerEvent event) {
151
152         if (Container.ADD_CHILD_EVENT.equals(event.getType())) {
153             Container child = (Container) event.getData();
154             addListeners(child);
155             // If child is started then it is too late for life-cycle listener
156             // to register the child so register it here
157             if (child.getState().isAvailable()) {
158                 if (child instanceof Host) {
159                     registerHost((Host) child);
160                 } else if (child instanceof Context) {
161                     registerContext((Context) child);
162                 } else if (child instanceof Wrapper) {
163                     // Only if the Context has started. If it has not, then it
164                     // will have its own "after_start" life-cycle event later.
165                     if (child.getParent().getState().isAvailable()) {
166                         registerWrapper((Wrapper) child);
167                     }
168                 }
169             }
170         } else if (Container.REMOVE_CHILD_EVENT.equals(event.getType())) {
171             Container child = (Container) event.getData();
172             removeListeners(child);
173             // No need to unregister - life-cycle listener will handle this when
174             // the child stops
175         } else if (Host.ADD_ALIAS_EVENT.equals(event.getType())) {
176             // Handle dynamically adding host aliases
177             mapper.addHostAlias(((Host) event.getSource()).getName(),
178                     event.getData().toString());
179         } else if (Host.REMOVE_ALIAS_EVENT.equals(event.getType())) {
180             // Handle dynamically removing host aliases
181             mapper.removeHostAlias(event.getData().toString());
182         } else if (Wrapper.ADD_MAPPING_EVENT.equals(event.getType())) {
183             // Handle dynamically adding wrappers
184             Wrapper wrapper = (Wrapper) event.getSource();
185             Context context = (Context) wrapper.getParent();
186             String contextPath = context.getPath();
187             if ("/".equals(contextPath)) {
188                 contextPath = "";
189             }
190             String version = context.getWebappVersion();
191             String hostName = context.getParent().getName();
192             String wrapperName = wrapper.getName();
193             String mapping = (String) event.getData();
194             boolean jspWildCard = ("jsp".equals(wrapperName)
195                     && mapping.endsWith("/*"));
196             mapper.addWrapper(hostName, contextPath, version, mapping, wrapper,
197                     jspWildCard, context.isResourceOnlyServlet(wrapperName));
198         } else if (Wrapper.REMOVE_MAPPING_EVENT.equals(event.getType())) {
199             // Handle dynamically removing wrappers
200             Wrapper wrapper = (Wrapper) event.getSource();
201
202             Context context = (Context) wrapper.getParent();
203             String contextPath = context.getPath();
204             if ("/".equals(contextPath)) {
205                 contextPath = "";
206             }
207             String version = context.getWebappVersion();
208             String hostName = context.getParent().getName();
209
210             String mapping = (String) event.getData();
211
212             mapper.removeWrapper(hostName, contextPath, version, mapping);
213         } else if (Context.ADD_WELCOME_FILE_EVENT.equals(event.getType())) {
214             // Handle dynamically adding welcome files
215             Context context = (Context) event.getSource();
216
217             String hostName = context.getParent().getName();
218
219             String contextPath = context.getPath();
220             if ("/".equals(contextPath)) {
221                 contextPath = "";
222             }
223
224             String welcomeFile = (String) event.getData();
225
226             mapper.addWelcomeFile(hostName, contextPath,
227                     context.getWebappVersion(), welcomeFile);
228         } else if (Context.REMOVE_WELCOME_FILE_EVENT.equals(event.getType())) {
229             // Handle dynamically removing welcome files
230             Context context = (Context) event.getSource();
231
232             String hostName = context.getParent().getName();
233
234             String contextPath = context.getPath();
235             if ("/".equals(contextPath)) {
236                 contextPath = "";
237             }
238
239             String welcomeFile = (String) event.getData();
240
241             mapper.removeWelcomeFile(hostName, contextPath,
242                     context.getWebappVersion(), welcomeFile);
243         } else if (Context.CLEAR_WELCOME_FILES_EVENT.equals(event.getType())) {
244             // Handle dynamically clearing welcome files
245             Context context = (Context) event.getSource();
246
247             String hostName = context.getParent().getName();
248
249             String contextPath = context.getPath();
250             if ("/".equals(contextPath)) {
251                 contextPath = "";
252             }
253
254             mapper.clearWelcomeFiles(hostName, contextPath,
255                     context.getWebappVersion());
256         }
257     }
258
259
260     // ------------------------------------------------------ Protected Methods
261
262     private void findDefaultHost() {
263
264         Engine engine = service.getContainer();
265         String defaultHost = engine.getDefaultHost();
266
267         boolean found = false;
268
269         if (defaultHost != null && defaultHost.length() > 0) {
270             Container[] containers = engine.findChildren();
271
272             for (Container container : containers) {
273                 Host host = (Host) container;
274                 if (defaultHost.equalsIgnoreCase(host.getName())) {
275                     found = true;
276                     break;
277                 }
278
279                 String[] aliases = host.findAliases();
280                 for (String alias : aliases) {
281                     if (defaultHost.equalsIgnoreCase(alias)) {
282                         found = true;
283                         break;
284                     }
285                 }
286             }
287         }
288
289         if (found) {
290             mapper.setDefaultHostName(defaultHost);
291         } else {
292             log.error(sm.getString("mapperListener.unknownDefaultHost", defaultHost, service));
293         }
294     }
295
296
297     /**
298      * Register host.
299      */

300     private void registerHost(Host host) {
301
302         String[] aliases = host.findAliases();
303         mapper.addHost(host.getName(), aliases, host);
304
305         for (Container container : host.findChildren()) {
306             if (container.getState().isAvailable()) {
307                 registerContext((Context) container);
308             }
309         }
310
311         // Default host may have changed
312         findDefaultHost();
313
314         if(log.isDebugEnabled()) {
315             log.debug(sm.getString("mapperListener.registerHost",
316                     host.getName(), domain, service));
317         }
318     }
319
320
321     /**
322      * Unregister host.
323      */

324     private void unregisterHost(Host host) {
325
326         String hostname = host.getName();
327
328         mapper.removeHost(hostname);
329
330         // Default host may have changed
331         findDefaultHost();
332
333         if(log.isDebugEnabled()) {
334             log.debug(sm.getString("mapperListener.unregisterHost", hostname,
335                     domain, service));
336         }
337     }
338
339
340     /**
341      * Unregister wrapper.
342      */

343     private void unregisterWrapper(Wrapper wrapper) {
344
345         Context context = ((Context) wrapper.getParent());
346         String contextPath = context.getPath();
347         String wrapperName = wrapper.getName();
348
349         if ("/".equals(contextPath)) {
350             contextPath = "";
351         }
352         String version = context.getWebappVersion();
353         String hostName = context.getParent().getName();
354
355         String[] mappings = wrapper.findMappings();
356
357         for (String mapping : mappings) {
358             mapper.removeWrapper(hostName, contextPath, version,  mapping);
359         }
360
361         if(log.isDebugEnabled()) {
362             log.debug(sm.getString("mapperListener.unregisterWrapper",
363                     wrapperName, contextPath, service));
364         }
365     }
366
367
368     /**
369      * Register context.
370      */

371     private void registerContext(Context context) {
372
373         String contextPath = context.getPath();
374         if ("/".equals(contextPath)) {
375             contextPath = "";
376         }
377         Host host = (Host)context.getParent();
378
379         WebResourceRoot resources = context.getResources();
380         String[] welcomeFiles = context.findWelcomeFiles();
381         List<WrapperMappingInfo> wrappers = new ArrayList<>();
382
383         for (Container container : context.findChildren()) {
384             prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
385
386             if(log.isDebugEnabled()) {
387                 log.debug(sm.getString("mapperListener.registerWrapper",
388                         container.getName(), contextPath, service));
389             }
390         }
391
392         mapper.addContextVersion(host.getName(), host, contextPath,
393                 context.getWebappVersion(), context, welcomeFiles, resources,
394                 wrappers);
395
396         if(log.isDebugEnabled()) {
397             log.debug(sm.getString("mapperListener.registerContext",
398                     contextPath, service));
399         }
400     }
401
402
403     /**
404      * Unregister context.
405      */

406     private void unregisterContext(Context context) {
407
408         String contextPath = context.getPath();
409         if ("/".equals(contextPath)) {
410             contextPath = "";
411         }
412         String hostName = context.getParent().getName();
413
414         if (context.getPaused()) {
415             if (log.isDebugEnabled()) {
416                 log.debug(sm.getString("mapperListener.pauseContext",
417                         contextPath, service));
418             }
419
420             mapper.pauseContextVersion(context, hostName, contextPath,
421                     context.getWebappVersion());
422         } else {
423             if (log.isDebugEnabled()) {
424                 log.debug(sm.getString("mapperListener.unregisterContext",
425                         contextPath, service));
426             }
427
428             mapper.removeContextVersion(context, hostName, contextPath,
429                     context.getWebappVersion());
430         }
431     }
432
433
434     /**
435      * Register wrapper.
436      */

437     private void registerWrapper(Wrapper wrapper) {
438
439         Context context = (Context) wrapper.getParent();
440         String contextPath = context.getPath();
441         if ("/".equals(contextPath)) {
442             contextPath = "";
443         }
444         String version = context.getWebappVersion();
445         String hostName = context.getParent().getName();
446
447         List<WrapperMappingInfo> wrappers = new ArrayList<>();
448         prepareWrapperMappingInfo(context, wrapper, wrappers);
449         mapper.addWrappers(hostName, contextPath, version, wrappers);
450
451         if(log.isDebugEnabled()) {
452             log.debug(sm.getString("mapperListener.registerWrapper",
453                     wrapper.getName(), contextPath, service));
454         }
455     }
456
457     /*
458      * Populate <code>wrappers</code> list with information for registration of
459      * mappings for this wrapper in this context.
460      */

461     private void prepareWrapperMappingInfo(Context context, Wrapper wrapper,
462             List<WrapperMappingInfo> wrappers) {
463         String wrapperName = wrapper.getName();
464         boolean resourceOnly = context.isResourceOnlyServlet(wrapperName);
465         String[] mappings = wrapper.findMappings();
466         for (String mapping : mappings) {
467             boolean jspWildCard = (wrapperName.equals("jsp")
468                                    && mapping.endsWith("/*"));
469             wrappers.add(new WrapperMappingInfo(mapping, wrapper, jspWildCard,
470                     resourceOnly));
471         }
472     }
473
474     @Override
475     public void lifecycleEvent(LifecycleEvent event) {
476         if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
477             Object obj = event.getSource();
478             if (obj instanceof Wrapper) {
479                 Wrapper w = (Wrapper) obj;
480                 // Only if the Context has started. If it has not, then it will
481                 // have its own "after_start" event later.
482                 if (w.getParent().getState().isAvailable()) {
483                     registerWrapper(w);
484                 }
485             } else if (obj instanceof Context) {
486                 Context c = (Context) obj;
487                 // Only if the Host has started. If it has not, then it will
488                 // have its own "after_start" event later.
489                 if (c.getParent().getState().isAvailable()) {
490                     registerContext(c);
491                 }
492             } else if (obj instanceof Host) {
493                 registerHost((Host) obj);
494             }
495         } else if (event.getType().equals(Lifecycle.BEFORE_STOP_EVENT)) {
496             Object obj = event.getSource();
497             if (obj instanceof Wrapper) {
498                 unregisterWrapper((Wrapper) obj);
499             } else if (obj instanceof Context) {
500                 unregisterContext((Context) obj);
501             } else if (obj instanceof Host) {
502                 unregisterHost((Host) obj);
503             }
504         }
505     }
506
507
508     /**
509      * Add this mapper to the container and all child containers
510      *
511      * @param container
512      */

513     private void addListeners(Container container) {
514         container.addContainerListener(this);
515         container.addLifecycleListener(this);
516         for (Container child : container.findChildren()) {
517             addListeners(child);
518         }
519     }
520
521
522     /**
523      * Remove this mapper from the container and all child containers
524      *
525      * @param container
526      */

527     private void removeListeners(Container container) {
528         container.removeContainerListener(this);
529         container.removeLifecycleListener(this);
530         for (Container child : container.findChildren()) {
531             removeListeners(child);
532         }
533     }
534 }
535