If you have configuration differences that go beyond attribute values, variables may not be sufficient. For example, suppose you use an in-memory database in dev (like Derby embedded) and a more robust database in production (like DB2).
In your primary server.xml, you can include another config xml file using a variable like this:
<server>
<include location="dbconfig-${env.ENV_LOCATION}.xml"/>
<!-- rest of common config here -->
</server>
Then you can have dev-only config in dbconfig-dev.xml like this:
<server>
<dataSource id="db" jndiName="jdbc/db">
<jdbcDriver libraryRef="DerbyLib"/>
<properties.derby.embedded databaseName="memory:testdb" createDatabase="create"/>
</dataSource>
<library id="DerbyLib">
<fileset dir="/path/to/derby.jar"/>
</library>
</server>
And production-only config in dbconfig-prod.xml like this:
<server>
<dataSource id="db" jndiName="jdbc/db">
<jdbcDriver libraryRef="DB2JCCLib"/>
<properties.db2.jcc databaseName="PRODUCTION_DB"
serverName="serious.business.com"
portNumber="50000"/>
</dataSource>
<library id="DB2JCCLib">
<fileset dir="/path/to/db2.jar"/>
</library>
</server>
Then, based on which value is set for ENV_LOCATION, either dbconfig-dev.xml or dbconfig-prod.xml will be included in your primary server.xml config.