我的解决方案是将所有定义放入一个server-template.xml
文件中,然后使用巧妙的 XSLT 转换server.xml
为每个实例生成最终定义。我正在使用 ant 构建文件来复制 Tomcat 安装的所有文件,并让它server.xml
从模板创建一个。一切都保存在 CVS 中,因此我可以快速恢复安装,而不必担心某些内容可能无法正常工作。这是模板的样子:
<Server port="${tomcat.server.port}" shutdown="SHUTDOWN"
xmlns:x="http://my.company.com/tomcat-template">
<x:define name="Derby-DataSource" username="???" password="???" url="???"
auth="Container" type="javax.sql.DataSource"
maxActive="50" maxIdle="5" maxWait="300"
driverClassName="org.apache.derby.jdbc.ClientDriver"
testWhileIdle="true" timeBetweenEvictionRunsMillis="3600000"
removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true" />
<x:define x:element="Resource" name="Derby/Embedded/TDB" auth="Container" type="javax.sql.DataSource"
maxActive="50" maxIdle="5" maxWait="300"
username="" password="" driverClassName="org.apache.derby.jdbc.EmbeddedDriver"
url="jdbc:derby:D:/tmp/TestDB" />
<x:define x:element="Resource" name="Derby/TDB" auth="Container" type="javax.sql.DataSource"
maxActive="50" maxIdle="5" maxWait="300"
username="junit" password="test" driverClassName="org.apache.derby.jdbc.ClientDriver"
url="jdbc:derby://localhost:1527/TDB" />
<x:define x:element="Resource" name="Derby/P6/TDB" auth="Container" type="javax.sql.DataSource"
maxActive="50" maxIdle="5" maxWait="300"
username="junit" password="test" driverClassName="com.p6spy.engine.spy.P6SpyDriver"
url="jdbc:derby://localhost:1527/TDB" />
... lots of Tomcat stuff ...
<!-- Global JNDI resources -->
<GlobalNamingResources>
<x:if server="local">
<!-- Local with Derby Network Server -->
<x:use name="Derby/TDB"><x:override name="jdbc/DB" maxIdle="1" /></x:use>
<x:use name="Derby/TDB"><x:override name="jdbc/DB_APP" maxIdle="1" /></x:use>
<x:use name="Derby/TDB"><x:override name="jdbc/DB2" maxIdle="1" /></x:use>
</x:if>
<x:if env="test"> ... same for test </x:if>
<x:if env="prod"> ... same for test </x:if>
</GlobalNamingResources>
</Server>
如您所见,我定义了默认值,然后专门设置。在环境中,我会覆盖一些东西(本地系统获得的池比生产和集成测试更小)。
过滤器脚本如下所示:
<!--
This XSLT Stylesheet transforms the file server-template.xml into server-new.xml.
It will perform the following operations:
- All x:define elements are removed
- All x:use elements will be replaces with the content and attributes
of the respective x:define element. The name of the new element is
specified with the attribute "x:element".
- All attributes in the x:override elements will overwrite the respective
attributes from the x:define element.
- x:if allows to suppress certain parts of the file altogether.
Example:
<x:define element="Resource" name="Derby/Embedded/TDB" auth="Container" ... />
<x:use name="Derby/Embedded/TDB"><x:override name="NewTDB" /></x:use>
becomes:
<Resource name="NewTDB" auth="Container" ... />
i.e. the attribute x:element="Resource" in x:define becomes the name of the
new element, name="Derby/Embedded/ABSTDB" in x:use specifies which x:define
to use and name="NewTDB" in x:override replaces the value of the "name"
attribute in x:define.
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://my.company.com/tomcat-template">
<xsl:output method="xml"/>
<!-- Key for fast lookup of x:defines -->
<xsl:key name="def" match="//x:define" use="@name" />
<!-- Parameters which can be used in x:if -->
<xsl:param name="server" />
<xsl:param name="env" />
<xsl:param name="instance" />
<!-- Copy everything by default -->
<xsl:template match="node()|@*">
<xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
</xsl:template>
<!-- filter x:defines -->
<xsl:template match="x:define"></xsl:template>
<!-- x:use is replaced by the matching x:define -->
<xsl:template match="x:use">
<!-- Find the x:define -->
<xsl:variable name="def" select="key('def', @name)" />
<!-- Save the x:use node in a variable -->
<xsl:variable name="node" select="." />
<!-- Start a new element. the name is in the attribute x:element of the x:define -->
<xsl:element name="{$def/@x:element}">
<!-- Process all attributes in the x: namespace -->
<xsl:for-each select="$def/@x:*">
<xsl:choose>
<xsl:when test="name() = 'x:extends'">
<xsl:variable name="extName" select="." />
<xsl:variable name="ext" select="key('def', $extName)" />
<xsl:for-each select="$ext/@*">
<xsl:if test="namespace-uri() != 'http://my.company.com/tomcat-template'">
<xsl:copy />
</xsl:if>
</xsl:for-each>
</xsl:when>
</xsl:choose>
</xsl:for-each>
<!-- Copy all attributes from the x:define (except those in the x: namespace) -->
<xsl:for-each select="$def/@*">
<xsl:if test="namespace-uri() != 'http://my.company.com/tomcat-template'">
<xsl:copy />
</xsl:if>
</xsl:for-each>
<!-- If there is an x:override-Element, copy those attributes. This
will overwrite existing attributes with the same name. -->
<xsl:for-each select="$node/x:override/@*">
<xsl:variable name="name" select="name()" />
<xsl:variable name="value" select="." />
<xsl:variable name="orig" select="$def/attribute::*[name() = $name]" />
<xsl:choose>
<!-- ${orig} allows to acces the attributes from the x:define -->
<xsl:when test="contains($value, '${orig}')">
<xsl:attribute name="{$name}"><xsl:value-of select="substring-before($value, '${orig}')"
/><xsl:value-of select="$orig"
/><xsl:value-of select="substring-after($value, '${orig}')"
/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<!-- Copy all child nodes, too -->
<xsl:apply-templates select="$def/node()"/>
</xsl:element>
</xsl:template>
<!-- x:if, to filter parts of the document -->
<xsl:template match="x:if">
<!-- t will be non-empty if any of the conditions matches -->
<xsl:variable name="t">
<!-- Check for each paramater whether it is used in the x:if. If so,
check the value. If the value is the same as the stylesheet
paramater, the condition is met. Missing conditions count
as met, too.
<xsl:if test="not(@server) or @server = $server">1</xsl:if>
<xsl:if test="not(@env) or @env = $env">1</xsl:if>
<xsl:if test="not(@instance) or @instance = $instance">1</xsl:if>
</xsl:variable>
<xsl:if test="normalize-space($t) = '111'">
<xsl:comment> <xsl:value-of select="$server" />, Env <xsl:value-of select="$env" />, Instance <xsl:value-of select="$instance" /> </xsl:comment>
<xsl:apply-templates select="node()|@*"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>