在前面的文章中,如果我們要啟動tomcat容器,我們需要使用Bootstrap類來實例化連接器、servlet容器、Wrapper實例和其他組件,然后調用各個對象的set方法將它們關聯起來;這種配置應用程序的方法有一個明顯的缺陷,即所有的配置都必須硬編碼。調整組件配置和屬性值都必須要重新編譯Bootstrap類。幸運的是,Tomcat的設計者使用了一種更加優雅的配置方式,即使用一個名為server.xml的XML文件來對應用程序進行配置。server.xml文件中的每個元素都會轉換為一個java對象,元素的屬性會用于設置java對象的屬性,這樣,就可以通過簡單的編輯server.xml文件來修改tomcat的配置。
Tomcat使用了開源庫Digester來將xml文件中的元素轉換成java對象。
由于一個Context實例表示一個Web應用程序,因此配置Web應用程序是通過對已經實例化的Context實例進行配置完成的。用來配置Web應用程序的XML文件的名稱是web.xml,該文件位于Web應用程序的WEB-INF目錄下。
下面來介紹Digester庫,Digester庫是Apache軟件基金會的Jatarta項目下的子Commons項目下的一個開源項目,它的主頁地址是http://commons.apache.org/proper/commons-digester/
org.apache.commons.digester3.Digester類是Digester庫中的主類,該類可用于解析XML文件,對于XML文件中的每個元素,Digester對象都會檢查它是否要做事先預定義的事件,在調用Digester對象的parse()方法之前,程序員要先定義好Digester對象執行哪些動作。
因此,程序員要先定義好模式,然后將每個模式與一條或多條規則相關聯。
模式通常是xml文件里面元素的路徑,類似于xpath的語法路徑
規則指明了當Digester對象遇到了某個特殊的模式時要執行的一個或多個動作,規則是org.apache.commons.digester3.Rule類的實例,Digester類開源包含0個或多個Rule對象,在Digester實例中,這些規則和其相關聯的模式都存儲在由org.apache.commons.digester3.Rules接口表示的一類存儲器中,每當把一條規則添加到Digester實例中時,Rule對象都會被添加到Rules對象中。
另外,Rule類有begin()方法和end()方法,在解析xml文件時,當Digester實例遇到匹配某個模式的元素的開始標簽時,它會調用相應的Rule對象的begin()方法,而當Digester實例遇到相應元素的結束標簽時,它會調用Rule對象的end()方法。
在使用Digester庫時,我們需要先導入相關依賴jar
<
dependency
>
<
groupId
>
org.apache.commons
</
groupId
>
<
artifactId
>
commons-digester3
</
artifactId
>
<
version
>
3.2
</
version
>
<
classifier
>
with-deps
</
classifier
>
</
dependency
>
第一個示例應用程序演示如何使用Digester庫動態的創建對象,并設置相應的屬性值。
employee1.xml文件內容如下
<?
xml version="1.0" encoding="ISO-8859-1"
?>
<
employee
firstName
="Brian"
lastName
="May"
>
</
employee
>
我們需要根據上面的xml文件創建Employee對象,并設置相應屬性,Employee類代碼如下:
public
class
Employee {
private
String firstName;
private
String lastName;
private
ArrayList offices =
new
ArrayList();
public
Employee() {
System.out.println(
"Creating Employee"
);
}
public
String getFirstName() {
return
firstName;
}
public
void
setFirstName(String firstName) {
System.out.println(
"Setting firstName : " +
firstName);
this
.firstName =
firstName;
}
public
String getLastName() {
return
lastName;
}
public
void
setLastName(String lastName) {
System.out.println(
"Setting lastName : " +
lastName);
this
.lastName =
lastName;
}
public
void
addOffice(Office office) {
System.out.println(
"Adding Office to this employee"
);
offices.add(office);
}
public
ArrayList getOffices() {
return
offices;
}
public
void
printName() {
System.out.println(
"My name is " + firstName + " " +
lastName);
}
}
現在寫一個測試類Test01,它使用Digester類,并為其添加創建Employee對象和設置其屬性的規則。
public
class
Test01 {
public
static
void
main(String[] args) {
InputStream inputStream
=
null
;
Digester digester
=
new
Digester();
//
add rules
digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee"
);
digester.addSetProperties(
"employee"
);
digester.addCallMethod(
"employee", "printName"
);
try
{
inputStream
= Thread.currentThread().getContextClassLoader().getResourceAsStream("employee1.xml");
Employee employee =
(Employee) digester.parse(inputStream);
System.out.println(
"First name : " +
employee.getFirstName());
System.out.println(
"Last name : " +
employee.getLastName());
}
catch
(Exception e) {
e.printStackTrace();
}
finally
{
if
(inputStream !=
null
) {
try
{
inputStream.close();
}
catch
(IOException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
第二個示例演示如何利用Digester庫創建兩個對象,并建立他們之間的關系
employee2.xml 文件內容如下
<?
xml version="1.0" encoding="ISO-8859-1"
?>
<
employee
firstName
="Freddie"
lastName
="Mercury"
>
<
office
description
="Headquarters"
>
<
address
streetName
="Wellington Avenue"
streetNumber
="223"
/>
</
office
>
<
office
description
="Client site"
>
<
address
streetName
="Downing Street"
streetNumber
="10"
/>
</
office
>
</
employee
>
然后我們還需要創建Office類和Address類
Office類代碼如下:
public
class
Office {
private
Address address;
private
String description;
public
Office() {
System.out.println(
"..Creating Office"
);
}
public
String getDescription() {
return
description;
}
public
void
setDescription(String description) {
System.out.println(
"..Setting office description : " +
description);
this
.description =
description;
}
public
Address getAddress() {
return
address;
}
public
void
setAddress(Address address) {
System.out.println(
"..Setting office address : " +
address);
this
.address =
address;
}
}
Address類代碼如下:
public
class
Address {
private
String streetName;
private
String streetNumber;
public
Address() {
System.out.println(
"....Creating Address"
);
}
public
String getStreetName() {
return
streetName;
}
public
void
setStreetName(String streetName) {
System.out.println(
"....Setting streetName : " +
streetName);
this
.streetName =
streetName;
}
public
String getStreetNumber() {
return
streetNumber;
}
public
void
setStreetNumber(String streetNumber) {
System.out.println(
"....Setting streetNumber : " +
streetNumber);
this
.streetNumber =
streetNumber;
}
public
String toString() {
return
"...." + streetNumber + " " +
streetName;
}
}
下面是Test02類的定義,該類使用一個Digester對象,并為其添加規則
public
class
Test02 {
public
static
void
main(String[] args) {
InputStream inputStream =
null
;
Digester digester
=
new
Digester();
//
add rules
digester.addObjectCreate("employee"
,
"ex15.pyrmont.digestertest.Employee"
);
digester.addSetProperties(
"employee"
);
digester.addObjectCreate(
"employee/office"
,
"ex15.pyrmont.digestertest.Office"
);
digester.addSetProperties(
"employee/office"
);
digester.addSetNext(
"employee/office", "addOffice"
);
digester.addObjectCreate(
"employee/office/address"
,
"ex15.pyrmont.digestertest.Address"
);
digester.addSetProperties(
"employee/office/address"
);
digester.addSetNext(
"employee/office/address", "setAddress"
);
try
{
inputStream
=
Thread.currentThread().getContextClassLoader()
.getResourceAsStream(
"employee2.xml");
Employee employee =
(Employee) digester.parse(inputStream);
ArrayList offices
=
employee.getOffices();
Iterator iterator
=
offices.iterator();
System.out
.println(
"-------------------------------------------------"
);
while
(iterator.hasNext()) {
Office office
=
(Office) iterator.next();
Address address
=
office.getAddress();
System.out.println(office.getDescription());
System.out.println(
"Address : " +
address.getStreetNumber()
+ " " +
address.getStreetName());
System.out.println(
"--------------------------------"
);
}
}
catch
(Exception e) {
e.printStackTrace();
}
finally
{
if
(inputStream !=
null
) {
try
{
inputStream.close();
}
catch
(IOException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
Rule類包含了一些方法,其中最重要的兩個方法是begin()方法和end()方法,當Digester實例遇到某個XML元素的開始標簽時,它會調用它所包含的匹配Rule對象的begin()方法,方法簽名如下:
public void begin( String namespace, String name, Attributes attributes ) throws Exception
當Digester實例遇到某個XML元素的結束標簽時,它會調用它所包含的匹配Rule對象的end()方法,方法簽名如下:
public void end( String namespace, String name ) throws Exception
Digester對象是如何完成這些工作的呢?當調用Digester對象的addObjectCreate()方法、addCallMethod()方法、addSetNext()方法或其他方法時,都會間接地調用Digester類的addRule()方法;該方法將一個Rule對象和它所匹配的模式添加到Digester對象的Rules集合中。
addRule()方法實現如下:
public
void
addRule( String pattern, Rule rule )
{
rule.setDigester(
this
);
getRules().add( pattern, rule );
}
查看Digester類的addObjectCreate()方法的重載實現如下:
public
void
addObjectCreate( String pattern, String className )
{
addRule( pattern,
new
ObjectCreateRule( className ) );
}
public
void
addObjectCreate( String pattern, Class<?>
clazz )
{
addRule( pattern,
new
ObjectCreateRule( clazz ) );
}
public
void
addObjectCreate( String pattern, String className, String attributeName )
{
addRule( pattern,
new
ObjectCreateRule( className, attributeName ) );
}
public
void
addObjectCreate( String pattern, String attributeName, Class<?>
clazz )
{
addRule( pattern,
new
ObjectCreateRule( attributeName, clazz ) );
}
這四個重載方法都調用了addRule()方法,ObjectCreateRule類是Rule類的子類,該類的實例可作為addRule()方法的第二個參數使用。
下面是ObjectCreateRule類的begin()方法和end()方法的實現
@Override
public
void
begin( String namespace, String name, Attributes attributes )
throws
Exception
{
Class
<?> clazz =
this
.clazz;
if
( clazz ==
null
)
{
//
Identify the name of the class to instantiate
String realClassName =
className;
if
( attributeName !=
null
)
{
String value
=
attributes.getValue( attributeName );
if
( value !=
null
)
{
realClassName
=
value;
}
}
if
( getDigester().getLogger().isDebugEnabled() )
{
getDigester().getLogger().debug( format(
"[ObjectCreateRule]{%s} New '%s'"
,
getDigester().getMatch(),
realClassName ) );
}
//
Instantiate the new object and push it on the context stack
clazz =
getDigester().getClassLoader().loadClass( realClassName );
}
Object instance;
if
( constructorArgumentTypes ==
null
|| constructorArgumentTypes.length == 0
)
{
if
( getDigester().getLogger().isDebugEnabled() )
{
getDigester()
.getLogger()
.debug( format(
"[ObjectCreateRule]{%s} New '%s' using default empty constructor"
,
getDigester().getMatch(),
clazz.getName() ) );
}
instance
=
clazz.newInstance();
}
else
{
if
( proxyManager ==
null
)
{
Constructor
<?> constructor =
getAccessibleConstructor( clazz, constructorArgumentTypes );
if
( constructor ==
null
)
{
throw
new
SAXException(
format(
"[ObjectCreateRule]{%s} Class '%s' does not have a construcor with types %s"
,
getDigester().getMatch(),
clazz.getName(),
Arrays.toString( constructorArgumentTypes ) ) );
}
proxyManager
=
new
ProxyManager( clazz, constructor, defaultConstructorArguments, getDigester() );
}
instance
=
proxyManager.createProxy();
}
getDigester().push( instance );
}
/**
* {
@inheritDoc
}
*/
@Override
public
void
end( String namespace, String name )
throws
Exception
{
Object top
=
getDigester().pop();
if
( proxyManager !=
null
)
{
proxyManager.finalize( top );
}
if
( getDigester().getLogger().isDebugEnabled() )
{
getDigester().getLogger().debug( format(
"[ObjectCreateRule]{%s} Pop '%s'"
,
getDigester().getMatch(),
top.getClass().getName() ) );
}
}
begin()方法用于創建一個對象實例,并將其壓入到Digester對象的內部棧中;end()方法會將內部棧的棧頂元素彈出棧
要向Digester實例中添加Rule對象,還可以調用其addRuleSet()方法,方法實現如下:
public
void
addRuleSet( RuleSet ruleSet )
{
String oldNamespaceURI
=
getRuleNamespaceURI();
String newNamespaceURI
=
ruleSet.getNamespaceURI();
if
( log.isDebugEnabled() )
{
if
( newNamespaceURI ==
null
)
{
log.debug(
"addRuleSet() with no namespace URI"
);
}
else
{
log.debug(
"addRuleSet() with namespace URI " +
newNamespaceURI );
}
}
setRuleNamespaceURI( newNamespaceURI );
ruleSet.addRuleInstances(
this
);
setRuleNamespaceURI( oldNamespaceURI );
}
org.apache.commons.digester3.RuleSet接口表示Rule對象的集合,該接口定義了兩個方法,分別為addRuleInstance()和getNamespaceURI(),addRuleInstance()方法簽名如下:
public void addRuleInstance(Digester digester)
addRuleInstance()方法用于添加定義在當前RuleSet對象中的Rule對象集合到作為該方法參數傳輸的Digester實例中
getNamespaceUR()方法返回將要應用在所有Rule對象(在當前Ruleset中創建的)的命名空間的URI,該方法簽名如下
public java.lang.String getNamespaceURI()
因此,在創建了Digester對象之后,可以創建一個RuleSet對象,并將其傳輸給Digester對象的addRuleSet()方法
為了便于使用,實現RuleSet接口有一個基類RuleSetBase,RuleSetBase類為抽象類,提供了getNamespaceURI()方法的實現,我們只需要提供addRuleInstances()方法的實現就可以了
下面是我們創建的EmployeeRuleSet類的源碼(繼承自RuleSetBase類)
public
class
EmployeeRuleSet
extends
RuleSetBase {
public
void
addRuleInstances(Digester digester) {
//
add rules
digester.addObjectCreate("employee", "ex15.pyrmont.digestertest.Employee"
);
digester.addSetProperties(
"employee"
);
digester.addObjectCreate(
"employee/office", "ex15.pyrmont.digestertest.Office"
);
digester.addSetProperties(
"employee/office"
);
digester.addSetNext(
"employee/office", "addOffice"
);
digester.addObjectCreate(
"employee/office/address"
,
"ex15.pyrmont.digestertest.Address"
);
digester.addSetProperties(
"employee/office/address"
);
digester.addSetNext(
"employee/office/address", "setAddress"
);
}
}
我們注意到,EmployeeRuleSet類中的addRuleInstances()方法的實現的功能類似Test02類,將相同的Rule對象添加到Digester對象中
下面是Test03的代碼,里面會創建EmployeeRuleSet類的實例,然后將其添加到之前創建的Digester對象中
public
class
Test03 {
public
static
void
main(String[] args) {
InputStream inputStream =
null
;
Digester digester
=
new
Digester();
digester.addRuleSet(
new
EmployeeRuleSet());
try
{
inputStream
= Thread.currentThread().getContextClassLoader().getResourceAsStream("employee2.xml"
);
Employee employee
=
(Employee) digester.parse(inputStream);
ArrayList offices
=
employee.getOffices();
Iterator iterator
=
offices.iterator();
System.out.println(
"-------------------------------------------------"
);
while
(iterator.hasNext()) {
Office office
=
(Office) iterator.next();
Address address
=
office.getAddress();
System.out.println(office.getDescription());
System.out.println(
"Address : " +
address.getStreetNumber()
+ " " +
address.getStreetName());
System.out.println(
"--------------------------------"
);
}
}
catch
(Exception e) {
e.printStackTrace();
}
finally
{
if
(inputStream !=
null
) {
try
{
inputStream.close();
}
catch
(IOException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
與其他類型的容器不同,StandardContext實例必須有一個監聽器,該監聽器會負責配置StandardContext實例,設置成功后會將StandardContext實例的變量configued值設置為tue。
StandardContext類的標準監聽器是org.apache.catalina.startup.ContextConfig類的實例,它會執行很對StandardContext實例來說必不可少的任務,例如安裝驗證器閥到StandardContext實例的管道對象中,此外還會添加許可器閥(類型為org.apache.catalina.valves.CertificateValve)到管道對象中。
但更重要的是,ContextConfig類的實例還會讀取和解析默認的web.xml文件和應用程序自定義的web.xml文件,并將xml元素轉換為java對象。
默認的web.xml文件位于CATALINE_HOME目錄下的conf目錄中,其中定義并映射了很多默認的servlet,配置了很多MIME類型文件的映射,定義了默認的session超時時間,以及定義了歡迎文件的列表。
應用程序的web.xml文件是應用程序自定義的配置文件,位于應用程序目錄下的WEB-INF目錄中。
ContextConfig實例會為每一個servlet元素創建StandardWrapper實例,因此,正如你在本章應用程序中看到的,配置變簡單了,你不在需要實例化Wrapper實例了
因此,我們需要在Bootstrap類中實例化一個ContextConfig類,并調用org.apache.catalina.Lifecycle接口的addLifecycleListener()方法將其添加到StandardContext對象中
LifecycleListener listener =
new
ContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
在啟動和停止StandardContext實例時,會觸發相應事件,ContextConfig類會對兩種事件做出響應,分別為START_EVENT 和STOP_EVENT
每當StandardContext實例觸發事件時,會調用ContextConfig實例的lifecycleEvent()方法
public
void
lifecycleEvent(LifecycleEvent event) {
//
Identify the context we are associated with
try
{
context
=
(Context) event.getLifecycle();
if
(context
instanceof
StandardContext) {
int
contextDebug =
((StandardContext) context).getDebug();
if
(contextDebug >
this
.debug)
this
.debug =
contextDebug;
}
}
catch
(ClassCastException e) {
log(sm.getString(
"contextConfig.cce"
, event.getLifecycle()), e);
return
;
}
//
Process the event that has occurred
if
(event.getType().equals(Lifecycle.START_EVENT))
start();
else
if
(event.getType().equals(Lifecycle.STOP_EVENT))
stop();
}
在上面方法中,會繼續調用start()方法和stop()方法
private
synchronized
void
start() {
if
(debug > 0
)
log(sm.getString(
"contextConfig.start"
));
context.setConfigured(
false
);
ok
=
true
;
//
Set properties based on DefaultContext
Container container =
context.getParent();
if
( !
context.getOverride() ) {
if
( container
instanceof
Host ) {
((Host)container).importDefaultContext(context);
container
=
container.getParent();
}
if
( container
instanceof
Engine ) {
((Engine)container).importDefaultContext(context);
}
}
//
Process the default and application web.xml files
defaultConfig();
applicationConfig();
if
(ok) {
validateSecurityRoles();
}
//
Scan tag library descriptor files for additional listener classes
if
(ok) {
try
{
tldScan();
}
catch
(Exception e) {
log(e.getMessage(), e);
ok
=
false
;
}
}
//
Configure a certificates exposer valve, if required
if
(ok)
certificatesConfig();
//
Configure an authenticator if we need one
if
(ok)
authenticatorConfig();
//
Dump the contents of this pipeline if requested
if
((debug >= 1) && (context
instanceof
ContainerBase)) {
log(
"Pipline Configuration:"
);
Pipeline pipeline
=
((ContainerBase) context).getPipeline();
Valve valves[]
=
null
;
if
(pipeline !=
null
)
valves
=
pipeline.getValves();
if
(valves !=
null
) {
for
(
int
i = 0; i < valves.length; i++
) {
log(
" " +
valves[i].getInfo());
}
}
log(
"======================"
);
}
//
Make our application available if no problems were encountered
if
(ok)
context.setConfigured(
true
);
else
{
log(sm.getString(
"contextConfig.unavailable"
));
context.setConfigured(
false
);
}
}
start()方法會進一步調用defaultConfig()方法和applicationConfig()方法
defaultConfig()方法負責讀取并解析位于%CATALINA_HOME%/conf目錄下的默認的web.xml文件
private
void
defaultConfig() {
//
Open the default web.xml file, if it exists
File file =
new
File(Constants.DefaultWebXml);
if
(!
file.isAbsolute())
file
=
new
File(System.getProperty("catalina.base"
),
Constants.DefaultWebXml);
FileInputStream stream
=
null
;
try
{
stream
=
new
FileInputStream(file.getCanonicalPath());
stream.close();
stream
=
null
;
}
catch
(FileNotFoundException e) {
log(sm.getString(
"contextConfig.defaultMissing"
));
return
;
}
catch
(IOException e) {
log(sm.getString(
"contextConfig.defaultMissing"
), e);
return
;
}
//
Process the default web.xml file
synchronized
(webDigester) {
try
{
InputSource is
=
new
InputSource("file://" +
file.getAbsolutePath());
stream
=
new
FileInputStream(file);
is.setByteStream(stream);
webDigester.setDebug(getDebug());
if
(context
instanceof
StandardContext)
((StandardContext) context).setReplaceWelcomeFiles(
true
);
webDigester.clear();
webDigester.push(context);
webDigester.parse(is);
}
catch
(SAXParseException e) {
log(sm.getString(
"contextConfig.defaultParse"
), e);
log(sm.getString(
"contextConfig.defaultPosition"
,
"" +
e.getLineNumber(),
"" +
e.getColumnNumber()));
ok
=
false
;
}
catch
(Exception e) {
log(sm.getString(
"contextConfig.defaultParse"
), e);
ok
=
false
;
}
finally
{
try
{
if
(stream !=
null
) {
stream.close();
}
}
catch
(IOException e) {
log(sm.getString(
"contextConfig.defaultClose"
), e);
}
}
}
}
applicationConfig()方法與defaultConfig()方法類似,只不過它處理的是應用程序自定義的部署描述符,該部署描述符位于應用目錄下的WEB-INF目錄中
private
void
applicationConfig() {
//
Open the application web.xml file, if it exists
InputStream stream =
null
;
ServletContext servletContext
=
context.getServletContext();
if
(servletContext !=
null
)
stream
=
servletContext.getResourceAsStream
(Constants.ApplicationWebXml);
if
(stream ==
null
) {
log(sm.getString(
"contextConfig.applicationMissing"
));
return
;
}
//
Process the application web.xml file
synchronized
(webDigester) {
try
{
URL url
=
servletContext.getResource(Constants.ApplicationWebXml);
InputSource is
=
new
InputSource(url.toExternalForm());
is.setByteStream(stream);
webDigester.setDebug(getDebug());
if
(context
instanceof
StandardContext) {
((StandardContext) context).setReplaceWelcomeFiles(
true
);
}
webDigester.clear();
webDigester.push(context);
webDigester.parse(is);
}
catch
(SAXParseException e) {
log(sm.getString(
"contextConfig.applicationParse"
), e);
log(sm.getString(
"contextConfig.applicationPosition"
,
"" +
e.getLineNumber(),
"" +
e.getColumnNumber()));
ok
=
false
;
}
catch
(Exception e) {
log(sm.getString(
"contextConfig.applicationParse"
), e);
ok
=
false
;
}
finally
{
try
{
if
(stream !=
null
) {
stream.close();
}
}
catch
(IOException e) {
log(sm.getString(
"contextConfig.applicationClose"
), e);
}
}
}
}
在ContextConfig類中,使用變量webDigester來引用一個Digester類型的對象
private static Digester webDigester = createWebDigester();
該Digester對象用于解析默認的web.xml文件和應用程序自定義的web.xml文件,在調用createWebDigester()方法時會添加用來處理web.xml文件的規則
/**
* Create (if necessary) and return a Digester configured to process the
* web application deployment descriptor (web.xml).
*/
private
static
Digester createWebDigester() {
URL url
=
null
;
Digester webDigester
=
new
Digester();
webDigester.setValidating(
true
);
url
= ContextConfig.
class
.getResource(Constants.WebDtdResourcePath_22);
webDigester.register(Constants.WebDtdPublicId_22,
url.toString());
url
= ContextConfig.
class
.getResource(Constants.WebDtdResourcePath_23);
webDigester.register(Constants.WebDtdPublicId_23,
url.toString());
webDigester.addRuleSet(
new WebRuleSet());
return
(webDigester);
}
我們注意到,上面方法中調用了變量webDigester的addRuleSet()方法,傳入一個org.apache.catalina.startup.WebRuleSet類型的對象作為參數;WebRuleSet類是org.apache.commons.digester.RuleSetBase的子類。
下面是WebRuleSet類的addRuleInstances()方法實現:
public
void
addRuleInstances(Digester digester) {
digester.addRule(prefix
+ "web-app"
,
new
SetPublicIdRule(digester, "setPublicId"
));
digester.addCallMethod(prefix
+ "web-app/context-param"
,
"addParameter", 2
);
digester.addCallParam(prefix
+ "web-app/context-param/param-name", 0
);
digester.addCallParam(prefix
+ "web-app/context-param/param-value", 1
);
digester.addCallMethod(prefix
+ "web-app/display-name"
,
"setDisplayName", 0
);
digester.addRule(prefix
+ "web-app/distributable"
,
new
SetDistributableRule(digester));
digester.addObjectCreate(prefix
+ "web-app/ejb-local-ref"
,
"org.apache.catalina.deploy.ContextLocalEjb"
);
digester.addSetNext(prefix
+ "web-app/ejb-local-ref"
,
"addLocalEjb"
,
"org.apache.catalina.deploy.ContextLocalEjb"
);
//
代碼太長,后面部分略
}
---------------------------------------------------------------------------?
本系列How Tomcat Works系本人原創?
轉載請注明出處 博客園 刺猬的溫馴?
本人郵箱: ? chenying998179 # 163.com ( #改為@ )
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

