In this setup we will use the office-wide LDAP authentication server to authenticate the client using HTTP basic authentication (digest will not work).
First of all discover the structure of LDAP directory. You need to know the LDAP server URL, DN prefix for user and group search:
$ ldapsearch -H ldap://ldap.company.org:389/ -D uid=myuid,ou=people,dc=company,dc=org -w <mypassword> -b 'uid=myuid,ou=people,dc=company,dc=org' dn: uid=myuid, ou=people, dc=company, dc=org objectclass: top objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson uid: myuid cn: Dmitry Katsubo sn: Katsubo givenname: Dmitry displayname: Dmitry Katsubo ...
For group (role) search we will use the following filter:
$ ldapsearch -H ldap://ldap.company.org:389/ -D uid=myuid,ou=people,dc=company,dc=org -w <mypassword> -b 'ou=groups,dc=company,dc=org' -s sub '(member=uid=myuid,ou=people,dc=company,dc=org)' dn: cn=subdiv5587, ou=groups, dc=company, dc=org objectclass: top objectclass: groupOfNames cn: subdiv5587 description: Subdevision 55.87 member: uid=myuid, ou=people, dc=company, dc=org ... other members ... dn: cn=div55, ou=groups, dc=company, dc=org objectclass: top objectclass: groupOfNames cn: div55 description: Devision 55 member: uid=myuid, ou=people, dc=company, dc=org ... other members ...
Based on the discovered information we define in Tomcat's server.xml
the following realm1):
<Server ...> ... <Realm className="org.apache.catalina.realm.CombinedRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> <Realm className="org.apache.catalina.realm.JNDIRealm" connectionURL="ldap://ldap.company.org:389/" alternateURL="ldap://ldap-replica.company.org:389/" userPattern="uid={0},ou=people,dc=company,dc=org" roleBase="ou=groups,dc=company,dc=org" roleName="cn" roleSearch="(member={0})" roleSubtree="false" /> </Realm> </Server>
This combined realm uses the local user database (e.g. you want to define local Tomcat admins or application deployers) as well as the LDAP directory to authenticate. The last one applies authentication-by-binding mechanism. If want client to check the password, use userSearch
+ userSubtree
JNDIRealm
properties.
Now you can say in application's WEB-INF/web.xml
:
<web-app version="2.4" ...> ... <security-constraint> <web-resource-collection> <web-resource-name>All Pages</web-resource-name> <url-pattern>/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>dir2711</role-name> </auth-constraint> </security-constraint> <security-role id="SecurityRole_0"> <description>The role that is assigned to all members of Subdevision 55.87.</description> <role-name>subdiv5587</role-name> </security-role> <login-config> <auth-method>BASIC</auth-method> <realm-name>MyApplication</realm-name> </login-config> </web-app>
You can define several <url-pattern>
s and several <http-method>
s in <web-resource-collection>
. The <role-name>
from <auth-constraint>
should be also described by <security-role>
.
<auth-method>
represents basic HTTP/1.1 authentication methods:
<form-login-config>
element2).In the code one can use HttpServletRequest#getRemoteUser() and HttpServletRequest#isUserInRole() methods:
package org.company.application.web; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class MyWebController { private static final Log logger = LogFactory.getLog(MyWebController.class); @RequestMapping(value = "/processFile", method = RequestMethod.POST) public void processFile(HttpServletRequest request, HttpServletResponse response) { logger.debug("User:" + request.getRemoteUser() + ", is in directorate:" + request.isUserInRole("subdiv5587")); ... } }
Let's test the above declared POST request handler:
$ wget -S -nv -t 1 --post-file myfile.txt -O - http://myuid:mypasword@service-id.company.org:8080/application/processFile HTTP/1.1 401 Unauthorized Server: Apache-Coyote/1.1 WWW-Authenticate: Basic realm="MyApplication" Content-Type: text/html;charset=utf-8 Content-Length: 954 Date: Thu, 05 Aug 2010 16:00:40 GMT Connection: keep-alive HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: text/plain;charset=US-ASCII Content-Length: 3999 Date: Thu, 05 Aug 2010 16:01:09 GMT Connection: keep-alive
In a log file:
DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] Security checking request POST /application/processFile DEBUG [org.apache.catalina.realm.RealmBase] Checking constraint 'SecurityConstraint[All Pages]' against POST /processFile --> true DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] Calling hasUserDataPermission() DEBUG [org.apache.catalina.realm.RealmBase] User data constraint has no restrictions DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] Calling authenticate() DEBUG [org.apache.catalina.realm.CombinedRealm] Attempting to authenticate user "myuid" with realm "org.apache.catalina.realm.UserDatabaseRealm/1.0" DEBUG [org.apache.catalina.realm.CombinedRealm] Cannot find message associated with key combinedRealm.authFail DEBUG [org.apache.catalina.realm.CombinedRealm] Attempting to authenticate user "myuid" with realm "org.apache.catalina.realm.JNDIRealm/1.0" DEBUG [org.apache.catalina.core.ContainerBase.[Catalina]] Found role: div55 DEBUG [org.apache.catalina.core.ContainerBase.[Catalina]] Found role: subdiv5587 DEBUG [org.apache.catalina.realm.CombinedRealm] Authenticated user "myuid" with realm "org.apache.catalina.realm.JNDIRealm/1.0" DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] Authenticated 'myuid' with type 'BASIC' DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] Calling accessControl() DEBUG [org.apache.catalina.realm.RealmBase] Checking roles GenericPrincipal[myuid(div55,subdiv5587)] DEBUG [org.apache.catalina.realm.RealmBase] Different realm org.apache.catalina.realm.CombinedRealm@1d32e45 org.apache.catalina.realm.JNDIRealm@12f1bf0 DEBUG [org.apache.catalina.realm.RealmBase] Username myuid has role subdiv5587 DEBUG [org.apache.catalina.realm.RealmBase] Role found: subdiv5587 DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] Successfully passed all security constraints DEBUG [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping] Mapping [/processFile] to handler 'org.company.application.web.MyWebController@24f4eb' DEBUG [org.company.application.web.MyWebController] User:myuid, is in directorate:true
For more information see Realm Configuration HOWTO.
General installation steps:
/etc/krb5.conf
(or use the system-wide one, defined by Linux administrator): [libdefaults] default_realm = REALM.COMPANY.ORG default_tkt_enctypes = des-cbc-md5 des-cbc-crc default_tgs_enctypes = des-cbc-md5 des-cbc-crc kdc_timesync = 0 kdc_default_options = 0x40000010 clockskew = 300 check_delegate = 0 ccache_type = 3 kdc_timeout = 60 [domain_realm] company.org = REALM.COMPANY.ORG .company.org = REALM.COMPANY.ORG [realms] REALM.COMPANY.ORG = { admin_server = server1.company.org kdc = server1.company.org kdc = server2.company.org }
Installation of SF SPNEGO module
spnego.jar
library and put it to /usr/share/tomcat6/lib
/etc/tomcat6/web.xml
(or to your application web.xml
): <web-app ...> ... <filter> <filter-name>SpnegoHttpFilter</filter-name> <filter-class>net.sourceforge.spnego.SpnegoHttpFilter</filter-class> <init-param> <param-name>spnego.allow.basic</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>spnego.allow.localhost</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>spnego.allow.unsecure.basic</param-name> <param-value>true</param-value> </init-param> <!-- The same can be configured via -Djava.security.krb5.conf=... system property. If not defined, system-wide /etc/krb5.conf is used. --> <init-param> <param-name>spnego.krb5.conf</param-name> <param-value>/etc/krb5.conf</param-value> </init-param> <!-- The same can be configured via java.security.auth.login.config=... system property. If not defined, user's ~/.java.login.config is used. --> <init-param> <param-name>spnego.login.conf</param-name> <param-value>/etc/tomcat6/login.config</param-value> </init-param> <init-param> <param-name>spnego.login.client.module</param-name> <param-value>spnego-client</param-value> </init-param> <init-param> <param-name>spnego.login.server.module</param-name> <param-value>spnego-server</param-value> </init-param> <init-param> <param-name>spnego.prompt.ntlm</param-name> <param-value>true</param-value> </init-param> <!-- 1 = FINEST .. 7 = SEVERE --> <init-param> <param-name>spnego.logger.level</param-name> <param-value>1</param-value> </init-param> </filter> <filter-mapping> <filter-name>SpnegoHttpFilter</filter-name> <url-pattern>*</url-pattern> </filter-mapping> </web-app>
/etc/tomcat6/login.config
, which consists of two sections: spnego-client
is used to authenticate service client (browser), spnego-server
is used to authenticate service (Tomcat) to KDC. The service principal should be present in a given /etc/tomcat6/service.keytab
and should be provided to you by KDC administrator: spnego-client { com.sun.security.auth.module.Krb5LoginModule required; }; spnego-server { com.sun.security.auth.module.Krb5LoginModule required storeKey=true useKeyTab=true keyTab="file:/etc/tomcat6/service.keytab" principal="HTTP/service-id.company.org@REALM.COMPANY.ORG"; };
/var/lib/tomcat6/webapps/ROOT
and test by navigating to http://service-id.company.org:8080/hello_spnego.jsp
.net.sourceforge.spnego.SpnegoHttpFilter
uses JUL to log debug information. See Questions Answered section for the hints about how to setup the logging environment.
net.sourceforge.spnego.SpnegoHttpFilter
for Jetty, refer Configuring SSO on Jetty 6. The only thing to add to above settings is the following:<Call name="addUserRealm"> <Arg> <New class="org.mortbay.jetty.plus.jaas.JAASUserRealm"> <Set name="name">REALM.COMPANY.ORG</Set> <Set name="LoginModuleName">spnego-client</Set> </New> </Arg> </Call>
For Jetty see How to configure Jetty to use SPNEGO/NEGOTIATE authentication.
For local services development, follow SPNEGO Tomcat instructions:
Installation of in-house SPNEGO
xxx-spnego-1.1.20.jar
3), xxx-spnego-tomcat-1.1.20.jar
, xxx_common_Jaas-0.3.3.jar
, xxx-spnego-pac-1.1.20.jar
4), bcprov-jdk16-1.40.jar
5), commons-logging-1.1.1.jar
libraries to /usr/share/tomcat6/lib
./etc/tomcat6/login.config
, which consists of two sections: app.spnego.client
is used to authenticate service client (browser), app.spnego.server
is used to authenticate service (Tomcat) to KDC. The service principal should be present in a given /etc/tomcat6/service.keytab
and should be provided to you by KDC administrator: // SPNEGO setup to authenticate the client. Should match context.xml/Context/Realm[className='org.company.security.tomcat.SpnegoRealm']/@AppName: app.spnego.client { org.company.security.sso.spnego.SpnegoLoginModule required; // org.company.security.sso.spnego.XmiRoleMappingLoginModule optional // additionalRoles="additionalRoleA,additionalRoleB,Authenticated"; }; // SPNEGO setup to authenticate the sevice to KDC. // The given keytab should correspond to principal name and should be registered at KDC. app.spnego.server { com.sun.security.auth.module.Krb5LoginModule required principal="HTTP/service-id.company.org@REALM.COMPANY.ORG" useKeyTab=true keyTab="/etc/tomcat6/service.keytab" storeKey=true debug=true; };
/etc/tomcat6/spnego.properties
that should contain the references to login.config
and krb5.conf
. This file is read by xxx-spnego-1.1.20.jar
: java.security.auth.login.config=/etc/tomcat6/login.config java.security.krb5.conf=/etc/krb5.conf jaas.kerberos.config.name=app.spnego.server # Settings below are optional and required for users-to-roles mappings. You need to enable XmiRoleMappingLoginModule in login.config. #xxx.ldap.url=ldap://server1.company.org:389/dc=company,dc=org #bindings.file.name=/etc/tomcat6/application-bnd.xmi #roles.file.name=conf/etc/tomcat6/application.xml
/etc/tomcat6/spnego.properties
into server JAVA_OPTS
: # Enable SPNEGO JAVA_OPTS="${JAVA_OPTS} -Dspnego.config=/etc/tomcat6/spnego.properties"
<auth-method>
of application's WEB-INF/web.xml
: <web-app version="2.4" ...> ... <security-constraint> <web-resource-collection> ... </web-resource-collection> <auth-constraint> <role-name>*</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>NEGOTIATE</auth-method> <realm-name>MyApplication</realm-name> </login-config> </web-app>
META-INF/context.xml
or modify server-wide
: <Context> <!-- login.config should contain the section "app.spnego.client" --> <Realm className="org.company.security.tomcat.SpnegoRealm" appName="app.spnego.client" userClassNames="org.company.security.jaas.principal.SimplePrincipal" roleClassNames="org.company.security.jaas.principal.SimplePrincipal" callbackHandler="org.company.security.tomcat.SpnegoCallbackHandler" allRolesMode="authOnly" /> <Valve className="org.company.security.tomcat.SpnegoAuthenticator" /> </Context>
/etc/tomcat6/application-bnd.xmi
and /etc/tomcat6/application.xml
to customize the mapping between LDAP groups and Web Container roles: <applicationbnd:ApplicationBinding xmlns:applicationbnd="applicationbnd.xmi" xmlns:xmi="http://www.omg.org/XMI" xmi:version="2.0"> <authorizationTable> <authorizations xmi:id="authorizations-0"> <role href="META-INF/application.xml#SecurityRole_1" /> <groups xmi:id="groups-0" name="cn=SysAdmins,cn=service" /> </authorizations> <authorizations xmi:id="authorizations-11"> <role href="META-INF/application.xml#SecurityRole_2" /> <groups xmi:id="groups-0" name="cn=Operator,cn=service" /> </authorizations> <authorizations xmi:id="authorizations-12"> <role href="META-INF/application.xml#SecurityRole_3" /> <groups xmi:id="groups-0" name="specialSubject:AllAuthenticatedUsers" /> </authorizations> </authorizationTable> </applicationbnd:ApplicationBinding>
<?xml version="1.0" encoding="UTF-8"?> <application xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/application_1_4.xsd" version="1.4"> <description>My Enterprise Application</description> <display-name>My Service</display-name> <module> <web> <web-uri>myservice.war</web-uri> <context-root>production/myservice</context-root> </web> </module> <security-role id="SecurityRole_1"> <description>System Administrator</description> <role-name>SYSADMIN</role-name> </security-role> <security-role id="SecurityRole_2"> <description>Online Operator</description> <role-name>OPERATOR</role-name> </security-role> <security-role id="SecurityRole_3"> <description>All Authenticated Users</description> <role-name>AUTHENTICATED_USER</role-name> </security-role> </application>
java.net.SocketException
SEVERE: StandardServer.await: create[8005]: java.net.SocketException: Invalid argument at java.net.PlainSocketImpl.socketBind(Native Method) at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:365) at java.net.ServerSocket.bind(ServerSocket.java:319) at java.net.ServerSocket.<init>(ServerSocket.java:185) at org.apache.catalina.core.StandardServer.await(StandardServer.java:373) at org.apache.catalina.startup.Catalina.await(Catalina.java:662) at org.apache.catalina.startup.Catalina.start(Catalina.java:614) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289) at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
-Djava.net.preferIPv4Stack=true
(see Java Networking Properties) or set net.ipv6.bindv6only
kernel system variable to zero (see this conversion):# This sysctl sets the default value of the IPV6_V6ONLY socket option. # # When disabled, IPv6 sockets will also be able to send and receive IPv4 # traffic with addresses in the form ::ffff:192.0.2.1 and daemons listening # on IPv6 sockets will also accept IPv4 connections. # # When IPV6_V6ONLY is enabled, daemons interested in both IPv4 and IPv6 # connections must open two listening sockets. # This is the default behaviour of all modern operating systems. net.ipv6.bindv6only = 0
For correct UTF-8 URL handling set <Connector ... URIEncoding="UTF-8" />
option in server.xml
or use <Connector ... useBodyEncodingForURI="true" />
.
The contents (request body) is not correctly represented because by default the character encoding of HttpServletRequest
is set to ISO-8859-1 (unless is provided in browser request). Spring has a special CharacterEncodingFilter
filter that fixes this (see sample here).
Unable to add the resource at [/…] to the cache for web application [/app] because there was insufficient free space available after evicting expired cache entries - consider increasing the maximum size of the cache
<Context> ... <Resources cachingAllowed="true" cacheMaxSize="100000" />
META-INF/context.xml
<Context path="/myapp"> </Context>
however this behaviour has changed in Tomcat 6 (see Context Container, bug#46713 and Tomcat's context path for a webapp not working):
path
attribute must only be used when statically defining a Context inserver.xml
. In all other circumstances, the path will be inferred from the filenames used for either the .xml context file or thedocBase
.
For deployment to Tomcat 6 you should use tomcat-maven-plugin
(that deploys via Tomcat Manager):
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <configuration> <!-- Server credentials are stored in settings.xml (should match with ones in /etc/tomcat6/tomcat-users.xml): --> <server>tomcat-server-id</server> <url>${tomcat.server.url}/manager</url> <!-- Deployment context: --> <path>${context.path}</path> </configuration> </plugin>
Tomcat has build-in memory leak protections, one of which does not allow to automatically image providers from web application in JVM-wide ImageIO registry. This is done by JreMemoryLeakPreventionListener which initializes ImageIO (call to any static method forces static registry to be initialized).
As a workaround appContextProtection
has to be disabled for JreMemoryLeakPreventionListener
:
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" appContextProtection="false"/>
however this is not a complete solution as if application is redeployed again and again, the references to image providers will still be kept together with classloader and all classes it has loaded. This will cause PermGen to run out of memory and under certain conditions may cause ClassCastException when using ImageIO.
One solution is provided in IIOProviderContextListener.java
(see How to resolve OutOfMemoryError with ImageIO plugins as the cause), and basically consists of the following steps:
ImageIO.scanForPlugins()
before using any ImageIO API (servlet initialization).
Another solution is to put jai_imageio.jar
+ jai_codec.jar
into server-wide lib/
folder.
References:
log4j
logging for Tomcat? /usr/share/tomcat6/lib/log4j.properties
log4j.rootLogger=INFO, R log4j.appender.R=org.apache.log4j.RollingFileAppender log4j.appender.R.File=${catalina.base}/logs/tomcat.log log4j.appender.R.MaxFileSize=50MB log4j.appender.R.MaxBackupIndex=5 log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%d{dd.MM HH:mm:ss} %-5p [%c] %m%n # For a more detailed Catalina localhost log, uncomment below: #log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost]=DEBUG #log4j.logger.org.apache.catalina.core=DEBUG #log4j.logger.org.apache.catalina.session=DEBUG
/etc/tomcat6/logging.properties
file you will not be able to see the debug output done via java.util.logging
. So either keep it, or remove/rename and continue with steps below.
Also you might be interested in redirecting the java.util.logging
to Log4j completely so you don't need to dig two log files for errors as everything will be in one file. For that you need to do further steps to one mentioned above:
apache-jul-log4j-bridge.jar
, log4j-1.2.15.jar
and log4j.properties
directory location into Tomcat boot classpath: /usr/share/tomcat6/bin/catalina.sh
--- catalina.sh.orig 2010-06-14 21:21:36.000000000 +0200 +++ catalina.sh 2010-08-09 13:06:38.000000000 +0200 @@ -164,12 +164,17 @@ # Add tomcat-juli.jar and bootstrap.jar to classpath # tomcat-juli.jar can be over-ridden per instance if [ ! -z "$CLASSPATH" ] ; then - CLASSPATH="$CLASSPATH": + CLASSPATH="$CLASSPATH:" fi -if [ "$CATALINA_BASE" != "$CATALINA_HOME" ] && [ -r "$CATALINA_BASE/bin/tomcat-juli.jar" ] ; then - CLASSPATH="$CLASSPATH""$CATALINA_BASE"/bin/tomcat-juli.jar:"$CATALINA_HOME"/bin/bootstrap.jar -else - CLASSPATH="$CLASSPATH""$CATALINA_HOME"/bin/bootstrap.jar + +CLASSPATH="$CLASSPATH""$CATALINA_HOME"/bin/bootstrap.jar + +if [ "$CATALINA_BASE" != "$CATALINA_HOME" ] && [ -r "$CATALINA_HOME/bin/tomcat-juli.jar" ] ; then + CLASSPATH="$CLASSPATH:$CATALINA_HOME/bin/tomcat-juli.jar" +fi + +if [ -r "$CATALINA_HOME/lib/apache-jul-log4j-bridge.jar" -a -r "$CATALINA_HOME/lib/log4j-1.2.15.jar" ] ; then + CLASSPATH="$CLASSPATH:$CATALINA_HOME/lib/apache-jul-log4j-bridge.jar:$CATALINA_HOME/lib/log4j-1.2.15.jar:$CATALINA_HOME/lib" fi if [ -z "$CATALINA_OUT" ] ; then
java.util.logging.manager
property either via LOGGING_MANAGER
or JAVA_OPTS
(check your startup scripts and catalina.sh
about how to do it correctly): LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.logging.julbridge.JULBridgeLogManager"
catalina.sh
you need to fix /etc/init.d/tomcat6
(or /usr/sbin/dtomcat6
): --- /usr/bin/dtomcat6.orig 2011-02-22 23:20:16.000000000 +0100 +++ /usr/bin/dtomcat6 2011-12-12 10:29:12.000000000 +0100 @@ -24,6 +25,9 @@ fi CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/bin/bootstrap.jar" CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/bin/tomcat-juli.jar" +CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/lib/apache-jul-log4j-bridge.jar" +CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/lib/log4j-1.2.15.jar" +CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/lib" CLASSPATH="${CLASSPATH}:$(build-classpath commons-daemon 2>/dev/null)" if [ "$1" = "start" ]; then @@ -34,7 +38,7 @@ -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \ -Djava.io.tmpdir="$CATALINA_TMPDIR" \ -Djava.util.logging.config.file="${CATALINA_BASE}/conf/logging.properties" \ - -Djava.util.logging.manager="org.apache.juli.ClassLoaderLogManager" \ + -Djava.util.logging.manager="org.apache.logging.julbridge.JULBridgeLogManager" \ org.apache.catalina.startup.Bootstrap start \ >> ${CATALINA_BASE}/logs/catalina.out 2>&1 & if [ ! -z "$CATALINA_PID" ]; then
Finally, checklist:
/usr/share/tomcat6/bin
contains:catalina.sh
(patched for classpath)tomcat-juli.jar
(from extras)/usr/share/tomcat6/lib
contains:tomcat-juli-adapters.jar
(from extras)log4j.properties
(newly created)log4j-1.2.15.jar
apache-jul-log4j-bridge.jar
/etc/default/tomcat6
is fixed for LOGGING_MANAGER
.and you are ready to restart Tomcat and enjoy the result.
For slf4j logging, check here.
mod_jk
could not be multiplexed into one HTTP/2 stream, see this post. To enable multiplexing JBoss 5.x / Apache Tomcat 9 is required. There could be different options where to offload TLS:
ProxyPass "/path" "h2://jboss.server"
+ <UpgradeProtocol className=“org.apache.coyote.http2.Http2Protocol” />
inside TLS enabled <Connector …>
element).ProxyPass "/path" "h2c://jboss.server"
+ <UpgradeProtocol className=“org.apache.coyote.http2.Http2Protocol” />
inside <Connector …>
element).See also:
XmiRoleMappingLoginModule
module, that is responsible for mapping users to security roles using the mapping file application-bnd.xmi
"Девица не хочет лезть в Окно" – device not compatible with Windows.