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
18
19 package org.apache.catalina.core;
20
21
22 import java.io.File;
23 import java.util.List;
24
25 import javax.management.MBeanServer;
26 import javax.management.MalformedObjectNameException;
27 import javax.management.ObjectName;
28
29 import org.apache.catalina.Container;
30 import org.apache.catalina.Engine;
31 import org.apache.catalina.Host;
32 import org.apache.catalina.LifecycleException;
33 import org.apache.catalina.Realm;
34 import org.apache.catalina.Service;
35 import org.apache.catalina.realm.JAASRealm;
36 import org.apache.catalina.util.ServerInfo;
37 import org.apache.juli.logging.Log;
38 import org.apache.juli.logging.LogFactory;
39 import org.apache.tomcat.util.modeler.Registry;
40 import org.apache.tomcat.util.modeler.modules.MbeansSource;
41
42 /**
43 * Standard implementation of the <b>Engine</b> interface. Each
44 * child container must be a Host implementation to process the specific
45 * fully qualified host name of that virtual host. <br/>
46 * You can set the jvmRoute direct or with the System.property <b>jvmRoute</b>.
47 *
48 * @author Craig R. McClanahan
49 * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (Tue, 24 Oct 2006) $
50 */
51
52 public class StandardEngine
53 extends ContainerBase
54 implements Engine {
55
56 private static Log log = LogFactory.getLog(StandardEngine.class);
57
58 // ----------------------------------------------------------- Constructors
59
60
61 /**
62 * Create a new StandardEngine component with the default basic Valve.
63 */
64 public StandardEngine() {
65
66 super();
67 pipeline.setBasic(new StandardEngineValve());
68 /* Set the jmvRoute using the system property jvmRoute */
69 try {
70 setJvmRoute(System.getProperty("jvmRoute"));
71 } catch(Exception ex) {
72 }
73 // By default, the engine will hold the reloading thread
74 backgroundProcessorDelay = 10;
75
76 }
77
78
79 // ----------------------------------------------------- Instance Variables
80
81
82 /**
83 * Host name to use when no server host, or an unknown host,
84 * is specified in the request.
85 */
86 private String defaultHost = null;
87
88
89 /**
90 * The descriptive information string for this implementation.
91 */
92 private static final String info =
93 "org.apache.catalina.core.StandardEngine/1.0";
94
95
96 /**
97 * The <code>Service</code> that owns this Engine, if any.
98 */
99 private Service service = null;
100
101 /** Allow the base dir to be specified explicitely for
102 * each engine. In time we should stop using catalina.base property -
103 * otherwise we loose some flexibility.
104 */
105 private String baseDir = null;
106
107 /** Optional mbeans config file. This will replace the "hacks" in
108 * jk and ServerListener. The mbeans file will support (transparent)
109 * persistence - soon. It'll probably replace jk2.properties and could
110 * replace server.xml. Of course - the same beans could be loaded and
111 * managed by an external entity - like the embedding app - which
112 * can use a different persistence mechanism.
113 */
114 private String mbeansFile = null;
115
116 /** Mbeans loaded by the engine.
117 */
118 private List mbeans;
119
120
121 /**
122 * The JVM Route ID for this Tomcat instance. All Route ID's must be unique
123 * across the cluster.
124 */
125 private String jvmRouteId;
126
127
128 // ------------------------------------------------------------- Properties
129
130 /** Provide a default in case no explicit configuration is set
131 *
132 * @return configured realm, or a JAAS realm by default
133 */
134 public Realm getRealm() {
135 Realm configured=super.getRealm();
136 // If no set realm has been called - default to JAAS
137 // This can be overriden at engine, context and host level
138 if( configured==null ) {
139 configured=new JAASRealm();
140 this.setRealm( configured );
141 }
142 return configured;
143 }
144
145
146 /**
147 * Return the default host.
148 */
149 public String getDefaultHost() {
150
151 return (defaultHost);
152
153 }
154
155
156 /**
157 * Set the default host.
158 *
159 * @param host The new default host
160 */
161 public void setDefaultHost(String host) {
162
163 String oldDefaultHost = this.defaultHost;
164 if (host == null) {
165 this.defaultHost = null;
166 } else {
167 this.defaultHost = host.toLowerCase();
168 }
169 support.firePropertyChange("defaultHost", oldDefaultHost,
170 this.defaultHost);
171
172 }
173
174 public void setName(String name ) {
175 if( domain != null ) {
176 // keep name==domain, ignore override
177 // we are already registered
178 super.setName( domain );
179 return;
180 }
181 // The engine name is used as domain
182 domain=name; // XXX should we set it in init() ? It shouldn't matter
183 super.setName( name );
184 }
185
186
187 /**
188 * Set the cluster-wide unique identifier for this Engine.
189 * This value is only useful in a load-balancing scenario.
190 * <p>
191 * This property should not be changed once it is set.
192 */
193 public void setJvmRoute(String routeId) {
194 jvmRouteId = routeId;
195 }
196
197
198 /**
199 * Retrieve the cluster-wide unique identifier for this Engine.
200 * This value is only useful in a load-balancing scenario.
201 */
202 public String getJvmRoute() {
203 return jvmRouteId;
204 }
205
206
207 /**
208 * Return the <code>Service</code> with which we are associated (if any).
209 */
210 public Service getService() {
211
212 return (this.service);
213
214 }
215
216
217 /**
218 * Set the <code>Service</code> with which we are associated (if any).
219 *
220 * @param service The service that owns this Engine
221 */
222 public void setService(Service service) {
223 this.service = service;
224 }
225
226 public String getMbeansFile() {
227 return mbeansFile;
228 }
229
230 public void setMbeansFile(String mbeansFile) {
231 this.mbeansFile = mbeansFile;
232 }
233
234 public String getBaseDir() {
235 if( baseDir==null ) {
236 baseDir=System.getProperty("catalina.base");
237 }
238 if( baseDir==null ) {
239 baseDir=System.getProperty("catalina.home");
240 }
241 return baseDir;
242 }
243
244 public void setBaseDir(String baseDir) {
245 this.baseDir = baseDir;
246 }
247
248 // --------------------------------------------------------- Public Methods
249
250
251 /**
252 * Add a child Container, only if the proposed child is an implementation
253 * of Host.
254 *
255 * @param child Child container to be added
256 */
257 public void addChild(Container child) {
258
259 if (!(child instanceof Host))
260 throw new IllegalArgumentException
261 (sm.getString("standardEngine.notHost"));
262 super.addChild(child);
263
264 }
265
266
267 /**
268 * Return descriptive information about this Container implementation and
269 * the corresponding version number, in the format
270 * <code><description>/<version></code>.
271 */
272 public String getInfo() {
273
274 return (info);
275
276 }
277
278 /**
279 * Disallow any attempt to set a parent for this Container, since an
280 * Engine is supposed to be at the top of the Container hierarchy.
281 *
282 * @param container Proposed parent Container
283 */
284 public void setParent(Container container) {
285
286 throw new IllegalArgumentException
287 (sm.getString("standardEngine.notParent"));
288
289 }
290
291
292 private boolean initialized=false;
293
294 public void init() {
295 if( initialized ) return;
296 initialized=true;
297
298 if( oname==null ) {
299 // not registered in JMX yet - standalone mode
300 try {
301 if (domain==null) {
302 domain=getName();
303 }
304 if(log.isDebugEnabled())
305 log.debug( "Register " + domain );
306 oname=new ObjectName(domain + ":type=Engine");
307 controller=oname;
308 Registry.getRegistry(null, null)
309 .registerComponent(this, oname, null);
310 } catch( Throwable t ) {
311 log.info("Error registering ", t );
312 }
313 }
314
315 if( mbeansFile == null ) {
316 String defaultMBeansFile=getBaseDir() + "/conf/tomcat5-mbeans.xml";
317 File f=new File( defaultMBeansFile );
318 if( f.exists() ) mbeansFile=f.getAbsolutePath();
319 }
320 if( mbeansFile != null ) {
321 readEngineMbeans();
322 }
323 if( mbeans != null ) {
324 try {
325 Registry.getRegistry(null, null).invoke(mbeans, "init", false);
326 } catch (Exception e) {
327 log.error("Error in init() for " + mbeansFile, e);
328 }
329 }
330
331 // not needed since the following if statement does the same thing the right way
332 // remove later after checking
333 //if( service==null ) {
334 // try {
335 // ObjectName serviceName=getParentName();
336 // if( mserver.isRegistered( serviceName )) {
337 // log.info("Registering with the service ");
338 // try {
339 // mserver.invoke( serviceName, "setContainer",
340 // new Object[] { this },
341 // new String[] { "org.apache.catalina.Container" } );
342 // } catch( Exception ex ) {
343 // ex.printStackTrace();
344 // }
345 // }
346 // } catch( Exception ex ) {
347 // log.error("Error registering with service ");
348 // }
349 //}
350
351 if( service==null ) {
352 // for consistency...: we are probably in embeded mode
353 try {
354 service=new StandardService();
355 service.setContainer( this );
356 service.initialize();
357 } catch( Throwable t ) {
358 log.error(t);
359 }
360 }
361
362 }
363
364 public void destroy() throws LifecycleException {
365 if( ! initialized ) return;
366 initialized=false;
367
368 // if we created it, make sure it's also destroyed
369 // this call implizit this.stop()
370 ((StandardService)service).destroy();
371
372 if( mbeans != null ) {
373 try {
374 Registry.getRegistry(null, null)
375 .invoke(mbeans, "destroy", false);
376 } catch (Exception e) {
377 log.error(sm.getString("standardEngine.unregister.mbeans.failed" ,mbeansFile), e);
378 }
379 }
380 //
381 if( mbeans != null ) {
382 try {
383 for( int i=0; i<mbeans.size() ; i++ ) {
384 Registry.getRegistry(null, null)
385 .unregisterComponent((ObjectName)mbeans.get(i));
386 }
387 } catch (Exception e) {
388 log.error(sm.getString("standardEngine.unregister.mbeans.failed", mbeansFile), e);
389 }
390 }
391
392 // force all metadata to be reloaded.
393 // That doesn't affect existing beans. We should make it per
394 // registry - and stop using the static.
395 Registry.getRegistry(null, null).resetMetadata();
396
397 }
398
399 /**
400 * Start this Engine component.
401 *
402 * @exception LifecycleException if a startup error occurs
403 */
404 public void start() throws LifecycleException {
405 if( started ) {
406 return;
407 }
408 if( !initialized ) {
409 init();
410 }
411
412 // Look for a realm - that may have been configured earlier.
413 // If the realm is added after context - it'll set itself.
414 if( realm == null ) {
415 ObjectName realmName=null;
416 try {
417 realmName=new ObjectName( domain + ":type=Realm");
418 if( mserver.isRegistered(realmName ) ) {
419 mserver.invoke(realmName, "init",
420 new Object[] {},
421 new String[] {}
422 );
423 }
424 } catch( Throwable t ) {
425 log.debug("No realm for this engine " + realmName);
426 }
427 }
428
429 // Log our server identification information
430 //System.out.println(ServerInfo.getServerInfo());
431 if(log.isInfoEnabled())
432 log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
433 if( mbeans != null ) {
434 try {
435 Registry.getRegistry(null, null)
436 .invoke(mbeans, "start", false);
437 } catch (Exception e) {
438 log.error("Error in start() for " + mbeansFile, e);
439 }
440 }
441
442 // Standard container startup
443 super.start();
444
445 }
446
447 public void stop() throws LifecycleException {
448 super.stop();
449 if( mbeans != null ) {
450 try {
451 Registry.getRegistry(null, null).invoke(mbeans, "stop", false);
452 } catch (Exception e) {
453 log.error("Error in stop() for " + mbeansFile, e);
454 }
455 }
456 }
457
458
459 /**
460 * Return a String representation of this component.
461 */
462 public String toString() {
463
464 StringBuffer sb = new StringBuffer("StandardEngine[");
465 sb.append(getName());
466 sb.append("]");
467 return (sb.toString());
468
469 }
470
471
472 // ------------------------------------------------------ Protected Methods
473
474
475 // -------------------- JMX registration --------------------
476
477 public ObjectName preRegister(MBeanServer server,
478 ObjectName name) throws Exception
479 {
480 super.preRegister(server,name);
481
482 this.setName( name.getDomain());
483
484 return name;
485 }
486
487 // FIXME Remove -- not used
488 public ObjectName getParentName() throws MalformedObjectNameException {
489 if (getService()==null) {
490 return null;
491 }
492 String name = getService().getName();
493 ObjectName serviceName=new ObjectName(domain +
494 ":type=Service,serviceName="+name);
495 return serviceName;
496 }
497
498 public ObjectName createObjectName(String domain, ObjectName parent)
499 throws Exception
500 {
501 if( log.isDebugEnabled())
502 log.debug("Create ObjectName " + domain + " " + parent );
503 return new ObjectName( domain + ":type=Engine");
504 }
505
506
507 private void readEngineMbeans() {
508 try {
509 MbeansSource mbeansMB=new MbeansSource();
510 File mbeansF=new File( mbeansFile );
511 mbeansMB.setSource(mbeansF);
512
513 Registry.getRegistry(null, null).registerComponent
514 (mbeansMB, domain + ":type=MbeansFile", null);
515 mbeansMB.load();
516 mbeansMB.init();
517 mbeansMB.setRegistry(Registry.getRegistry(null, null));
518 mbeans=mbeansMB.getMBeans();
519
520 } catch( Throwable t ) {
521 log.error( "Error loading " + mbeansFile, t );
522 }
523
524 }
525
526 public String getDomain() {
527 if (domain!=null) {
528 return domain;
529 } else {
530 return getName();
531 }
532 }
533
534 public void setDomain(String domain) {
535 this.domain = domain;
536 }
537
538 }