Показаны сообщения с ярлыком hibernate. Показать все сообщения
Показаны сообщения с ярлыком hibernate. Показать все сообщения

суббота, 14 апреля 2012 г.

Force reindex single entity with hibernate search.

Hibernate search documentations is weak area. In case if you catch the  "IllegalStateException: Could not get property value"  try to unproxy entity.

FullTextSession fullTextSession = Search.getFullTextSession(sessionFactory.getCurrentSession());
fullTextSession.index(HibernateHelper.unproxy(entity));

пятница, 3 февраля 2012 г.

Hibernate search migration to new version.

    Have you got "SearchException: Unable to perform work. Entity Class is not @Indexed nor hosts @ContainedIn: class java.lang.String" ? I guess  yes, so try to use FieldBridge to sovle you problems with embeded indexes.

    I am migrate from 3.1.0.GA to 3.4.1.Final and collect a lot of errors. I am pretty sure, that hibernate search team dont event think about backward compatibility. Hibernate search unit test cover only simple cases , documentation is also weak.


четверг, 12 мая 2011 г.

Verbose hibernate logging

Hi there ! Do you have oververbose hibernate logging and cannot configure it via log4j.properties? If yes - try to remove slf4j-simple and your life will be more easy ... according to logging

пятница, 23 апреля 2010 г.

"Statement is closed" SQLExeption or OutOfMemory exception during dbunit testing.

Do you have a problem with "Statement is closed" SQLExeption during dbunit testing ?
I had, because wrong dataSource configuration, so check the maxOpenPreparedStatements parameter. Your dataSource should looks like described below:
  1 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
2 <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
3 <property name="url" value="jdbc:hsqldb:mem:testnpadb"/>
4 <property name="username" value="sa"/>
5 <property name="password" value=""/>
6 <property name="initialSize" value="5"/>
7 <property name="maxActive" value="100"/>
8 <property name="poolPreparedStatements" value="true"/>
9 <property name="maxOpenPreparedStatements" value="100"/>
10 <property name="defaultAutoCommit" value="true"/>
11 </bean>


OutOfMemory ? Check the tear down method, guess you have to shutdown spring context.

среда, 17 марта 2010 г.

End to end identity propagation with jboss 4, hibernate and oracle. Part 3

Usual way to get the database connection - use data source. I saw a toons of applications, that use the DataSource#getConnection() method to get the data base connection via configured in application server data source. And usual situation, data source configured with priveledged user (root for mysql, schema owner in oracle case, sa for mssql, etc.) name and his password. LOL data source tell you - here the access key, and there the place where you can get everything and even more. To prevent unauthorized access need to use DataSource#getConnection(String userName, String userPassord) instead of DataSource#getConnection(). At this point need to solve following issues:
  • how to configure hibernate to get the JDBC connection with user name and password ?
  • how to configure jboss, application and data source ?
  • passing the password ?
  • is created connection will be in connection pool ?

Here the answers:

If you are look to hibernate configuration documentation you can find an amazing configuration parameter - hibernate.connection.provider_class - That has classname of a custom org.hibernate.connection.ConnectionProvider which provides JDBC connections to Hibernate WOW ! This in definitely that need to use !
The source code see below.



/**
* User: iazarny
* Date: 18.08.2008
* Time: 17:44:21
*
* This custom connection provider will work via JBoss datasource with
* application-managment-security configuration
* @see@ hibernate.connection.provider_class configation parameter for Hibernate JPA provider
*/

public class OracleConnectionProvider implements ConnectionProvider {

private static Logger logger = Logger.getLogger(OracleConnectionProvider.class);

private static InitialContext ctx;
private static DataSource ds;
static {
try {
ctx = new InitialContext();
ds = (DataSource) ctx.lookup("java:/CrTimeDS");
} catch (NamingException e) {
logger.fatal("Error ",e);
}
}

private Properties properties;


public void configure(Properties properties) throws HibernateException {
this.properties = properties;
}

public Connection getConnection() throws SQLException {
String userName = null;
String password = null;
if(SecurityAssociation.getPrincipal()!=null) {
userName = SecurityAssociation.getPrincipal().getName();
password = SecurityAssociation.getCredential().toString();
logger.info("SecurityAssociation.getPrincipal = " + userName);
logger.info("SecurityAssociation.getCredential() = " + password!=null );
}
if (password==null) {
userName = properties.getProperty(Environment.USER);
password = properties.getProperty(Environment.PASS);
logger.info("Environment.USER = " + userName);
logger.info("Environment.PASS = " + password!=null );
}


return ds.getConnection(userName,password);

}

public void closeConnection(Connection connection) throws SQLException {
logger.info("OracleConnectionProvider connection return to pool " + connection );
connection.close();
}

public void close() throws HibernateException {
logger.info("OracleConnectionProvider detach from DataSouce");
ds = null;
}

public boolean supportsAggressiveRelease() {
return false;
}
}


Guess you remember from End to end identity propagation with jboss 4, hibernate and oracle. Part 2 how to configure tamcat/jboss-web valves to get the current user password from SecurityAssociation.

As you can see from connection provider example need to configure CrTimeDS datasource.


<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>CrTimeDS</jndi-name>
<connection-url>jdbc:oracle:thin:@127.0.0.1:1521:az3</connection-url>
<driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
<!-- username and password not necessary, because of application-managed-security -->
<user-name></user-name>
<password></password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter
</exception-sorter-class-name>
<min-pool-size>10</min-pool-size>
<max-pool-size>200</max-pool-size>
<blocking-timeout-millis>1000</blocking-timeout-millis>
<idle-timeout-minutes>1</idle-timeout-minutes>
<application-managed-security/>
<metadata>
<type-mapping>Oracle9i</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>

In the persistence.xml connection provider wired with hibernate. In case if hibernate will try to check data base schema against domain model on application start and you find a lot of stack traces you need to add copule of lines.
<property name="hibernate.connection.username" value="readSchemaOnlyUser"/>
<property name="hibernate.connection.password" value="readSchemaOnlyPassword"/>

The readSchemaOnlyUser user in oracle, that can read schema meta data, but not actuall data. It easy with oracle



<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd
" version="1.0">
<persistence-unit name="crtimedbUnit" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/CrTimeDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect"/>
<property name="hibernate.connection.charset" value="UTF-8"/>
<property name="hibernate.cache.use_second_level_cache" value="false"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="hibernate.default_schema" value="CRTIME"/>
<property name="hibernate.connection.provider_class" value="com.crtime.connection.provider.OracleConnectionProvider"/>
</properties>
</persistence-unit>
</persistence>



In web application configuration jboss-web.xml you have to configure security domain



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.3V2//EN"
"http://www.jboss.org/j2ee/dtd/jboss-web_3_2.dtd">
<jboss-web>
<security-domain>java:/jaas/crtime-webapp</security-domain>
</jboss-web>




And last question is created connection for user willl be pooled ? Simple answer - yes it is. If you not sure - dig the jboss source code :)

Hmmm... 3 articles, guess nedd to provide overview how it works from end to end.
When not authorized user hit the page he will be promted by login to input his name and password. Provided login name and password will be checked via CrTimeAuthDS datasource by standard java login module (see part 1) in case if result is successful, user become authorized. Database connection will be obtained configured CrTimeDS and hibernate connection provider by using DataSource#getConnection(user, password) method. So user not impersonalized at data base level as well as web and middleware levels.

пятница, 5 марта 2010 г.

Second level cache in hibernate

Do you use hibernate ? I guess - yes, if you are here.
Do you use second level cache and query cache with hibernate? Are you happy with performance? I dont know... probably. Actually I am not happy with hibernate performance with query cache and 2nd level cache. Lets look inside ;)
First of all you have to understand, that 2nd level cache used when you perform lookup entity by id. Sound good, but by default entity will be cache without collections. The first solution - enable cache for collection in configurations, it will works for bags, lists, collections, sets, maps.
Second - query cache. It cache only the ids and must be used in conjunction second level cache.
Everything not bad at surface, but when you create hibernate and ehcache configuration and run you solution you will be surprised. Hibernate still hit you db :( Root cause - collection cache will not works for map, that has a map-key, and you db will be hitted a lot of times per single entity.
How to solve this ? Throw away the hibernate cache and use cache aspect in your application, it save a lot of you times, IO operations and CPU. It more predictable way that hibernate cache and allow to cache that you really need :)

пятница, 26 февраля 2010 г.

End to end identity propagation with jboss 4, hibernate and oracle. Part 2

Complete web application to test oracle login module.



First of all we have to understand where username/password stored after successful authentication to reuse it for our future needs.
By default jboss and tomcat not allow you to get password via SecurityAssociation class, so need additional configuration in context.xml.
Tomcat (and derived jboss web server) has a very useful valve elements for configuration
and additional request/response processing
see http://tomcat.apache.org/tomcat-5.5-doc/config/valve.html for more details.



Following changes in context.xml allow to get user password via SecurityAssociationValve class in jboss and tomcat


<Context cookies="true" crossContext="true">


<Valve className="org.jboss.web.tomcat.security.ExtendedFormAuthenticator"


includePassword="true" />


</Context>









As far as you remember from part 1 im try to minimize administrative overhead, but unfortunately i have to duplicate roles in web and db.

In any case lets create a simple web app with 3 pages and login form. Each role has access to one page only. See security-constraint nodes for page names and roles. web.xml shall looks like this:



web.xml


<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4"

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/web-app_2_4.xsd">




<servlet>

<servlet-name>logon</servlet-name>

<servlet-class>com.crtime.web.Logon</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>logon</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>







<security-role>

<role-name>CRTIME_ADMIN</role-name>

</security-role>




<security-role>

<role-name>CRTIME_MANAGER</role-name>

</security-role>




<security-role>

<role-name>anonymous</role-name>

</security-role>




<security-role>

<role-name>CRTIME_SOMEOTHERROLE</role-name>

</security-role>




<security-constraint>

<display-name>CRTIME_ADMIN</display-name>

<web-resource-collection>

<web-resource-name>Protected Area</web-resource-name>

<url-pattern>/crtime_admin.jsp</url-pattern>

</web-resource-collection>

<auth-constraint>

<role-name>CRTIME_ADMIN</role-name>

</auth-constraint>

<user-data-constraint>

<description>SSL required</description>

<transport-guarantee>CONFIDENTIAL</transport-guarantee>

</user-data-constraint>

</security-constraint>




<security-constraint>

<display-name>CRTIME_MANAGER</display-name>

<web-resource-collection>

<web-resource-name>Protected Area</web-resource-name>

<url-pattern>/crtime_manager.jsp</url-pattern>

</web-resource-collection>

<auth-constraint>

<role-name>CRTIME_MANAGER</role-name>

</auth-constraint>

<user-data-constraint>

<description>SSL required</description>

<transport-guarantee>CONFIDENTIAL</transport-guarantee>

</user-data-constraint>

</security-constraint>




<security-constraint>

<display-name>CRTIME_SOMEOTHERROLE</display-name>

<web-resource-collection>

<web-resource-name>Protected Area</web-resource-name>

<url-pattern>/crtime_someotherrole.jsp</url-pattern>

</web-resource-collection>

<auth-constraint>

<role-name>CRTIME_SOMEOTHERROLE</role-name>

</auth-constraint>

<user-data-constraint>

<description>SSL required</description>

<transport-guarantee>CONFIDENTIAL</transport-guarantee>

</user-data-constraint>

</security-constraint>




<login-config>

<auth-method>FORM</auth-method>

<realm-name>CrTime</realm-name>

<form-login-config>

<form-login-page>/login.jsp</form-login-page>

<form-error-page>/loginError.jsp</form-error-page>

</form-login-config>

</login-config>







<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>




<error-page>

<error-code>403</error-code>

<location>/login.jsp</location>

</error-page>




</web-app>




Login form:





<form name="logonForm" action="j_security_check" method=post>

<input type="text" name="j_username" maxlength=20>

<input type="password" name="j_password" maxlength=20>

<input type="submit" value="Login">

</form>




Login servlet



In case of successful authentication servlet will redirect to referred page.



package com.crtime.web;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletException;
import java.io.IOException;
import org.jboss.web.tomcat.security.login.WebAuthentication;
import org.jboss.web.tomcat.security.SecurityAssociationValve;
import org.apache.catalina.connector.Request;
import org.apache.catalina.Session;
import org.apache.catalina.authenticator.Constants;

/**
* User: iazarny
* Date: 10.06.2008
* Time: 11:21:38
*
* see following link for more details
* http://roneiv.wordpress.com/2008/03/
* http://forum.java.sun.com/thread.jspa?threadID=5293266&tstart=0
* http://www.javaworld.com/javaforums/printthread.php?Board=JavaSecurity&main=2500&type=post
*
*
*/
public class Logon extends HttpServlet {


public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
try {

WebAuthentication webAuthentication = new WebAuthentication();


boolean stat = webAuthentication.login(request.getParameter("j_username"), request.getParameter("j_password"));

if (stat) {

Request activeRequest = (Request) SecurityAssociationValve.activeRequest.get();
Session session = activeRequest.getSessionInternal(false);
String userNameFromTomCatsession = (String)session.getNote(Constants.SESS_USERNAME_NOTE);
String userPasswordFromTomCatsession = (String)session.getNote(Constants.SESS_PASSWORD_NOTE);
System.out.println("\nuserNameFromTomCatsession =" + userNameFromTomCatsession);
System.out.println("\nuserPasswordFromTomCatsession =" + userPasswordFromTomCatsession);

String referer = request.getHeader("Referer");
System.out.println("\nreferer = " + referer);
response.sendRedirect(referer);
} else {
response.sendRedirect(request.getContextPath() + "/loginError.jsp");
}


} catch (Exception e) {
e.printStackTrace();
response.sendRedirect("loginError.jsp");
}


}


public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

doGet(request, response);

}

}



To be continued ....

четверг, 25 февраля 2010 г.

End to end identity propagation with jboss 4, hibernate and oracle. Part 1

Several times i was asked by customers for create high security solutions with following requirements:
  • End to end identity propagation across all application levels - web/middle tier and database.
  • No password must be stored in jboss data source configuration.
  • Secure solution must work on public enviroment.
  • etc.
Is it possible ? - Yes ! But how to implement this and what does it mean?
  • It means, that Egor Lupan, for example, with account elupan can not have anonymous/impersonalized resourses or connections.
  • Also it means, that elupan must have accounts on web app/middle tier/db.
  • It means, that administrative overhead for manage accounts not allowed.
Do you think, that need to use LDAP as central point of authorization and authentication ? Probably, but i found another way. Lets look how it can be solved for typical CRUD web application with hibernate on jboss 4 with oracle rdbms as backend.

To minimaze administrative overhead oracle will be used as authorization, authentication and role provider for JAAS login module without any additional user/roles tables, just reuse the oracle all_users dictionary.


OracleDatabaseServerLoginModule source code:


package org.jboss.security.auth.spi;

import org.jboss.tm.TransactionDemarcationSupport;
import org.jboss.security.SimpleGroup;

import javax.security.auth.login.LoginException;
import javax.security.auth.login.FailedLoginException;
import javax.naming.NamingException;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.transaction.Transaction;
import java.sql.SQLException;
import java.sql.ResultSet;
import java.sql.PreparedStatement;
import java.sql.Connection;
import java.security.acl.Group;
import java.security.Principal;
import java.util.HashMap;


import java.security.Principal;
import java.security.acl.Group;


import javax.resource.spi.security.PasswordCredential;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;


import org.jboss.security.SimplePrincipal;


/**
* User: iazarny
* Date: 04.06.2008
* Time: 17:15:08
*/
public class OracleDatabaseServerLoginModule extends DatabaseServerLoginModule {



/**
* Oracle already validate the user
*
* @param inputPassword
* @param expectedPassword
* @return true
*/
protected boolean validatePassword(String inputPassword, String expectedPassword) {
return expectedPassword != null;
}

/**
* Get the expected password for the current username available via
* the getUsername() method. This is called from within the login()
* method after the CallbackHandler has returned the username and
* candidate password.
*
* @return the valid password String
*/
protected String getUsersPassword() throws LoginException {

String[] info = getUsernameAndPassword();
String usernameInput = info[0];
String passwordInput = info[1];


String username = getUsername();
String password = null;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;

Transaction tx = null;
if (suspendResume) {
tx = TransactionDemarcationSupport.suspendAnyTransaction();
}

try {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(dsJndiName);
// This is main point if connection obtained succesfuly - than pwd is valid
conn = ds.getConnection(username, passwordInput);
// Get the password

// log.trace("Excuting query: "+principalsQuery+", with username: "+username);
ps = conn.prepareStatement(principalsQuery);
ps.setString(1, username.toUpperCase());
rs = ps.executeQuery();
if (rs.next() == false) {
throw new FailedLoginException("No matching username found in Principals");
}
password = rs.getString(1);
password = convertRawPassword(password);
}
catch (NamingException ex) {
LoginException le = new LoginException("Error looking up DataSource from: " + dsJndiName);
le.initCause(ex);
throw le;
}
catch (SQLException ex) {
LoginException le = new LoginException("Query failed");
le.initCause(ex);
throw le;
}
finally {
if (rs != null) {
try {
rs.close();
}
catch (SQLException e) {
}
}
if (ps != null) {
try {
ps.close();
}
catch (SQLException e) {
}
}
if (conn != null) {
try {
conn.close();
}
catch (SQLException ex) {
}
}
if (suspendResume) {
TransactionDemarcationSupport.resumeAnyTransaction(tx);
}
}
return password;
}


/**
* Execute the rolesQuery against the dsJndiName to obtain the roles for
* the authenticated user.
*
* @return Group[] containing the sets of roles
*/
protected Group[] getRoleSets() throws LoginException {
String username = getUsername();
Group[] roleSets = getRoleSets(username, dsJndiName, rolesQuery, this,
suspendResume);
return roleSets;
}


/**
* Execute the rolesQuery against the dsJndiName to obtain the roles for
* the authenticated user.
*
* @return Group[] containing the sets of roles
*/
private Group[] getRoleSets(String username, String dsJndiName,
String rolesQuery, AbstractServerLoginModule aslm, boolean suspendResume)
throws LoginException {
Connection conn = null;
HashMap setsMap = new HashMap();
PreparedStatement ps = null;
ResultSet rs = null;

Transaction tx = null;
if (suspendResume) {
tx = TransactionDemarcationSupport.suspendAnyTransaction();
}

try {

String[] info = getUsernameAndPassword();
String usernameInput = info[0];
String passwordInput = info[1];

InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(dsJndiName);
conn = ds.getConnection(usernameInput,passwordInput);
// Get the user role names
ps = conn.prepareStatement(rolesQuery);
try {
ps.setString(1, username);
}
catch (ArrayIndexOutOfBoundsException ignore) {
// The query may not have any parameters so just try it
}
rs = ps.executeQuery();
if (rs.next() == false) {
if (aslm.getUnauthenticatedIdentity() == null)
throw new FailedLoginException("No matching username found in Roles");
/* We are running with an unauthenticatedIdentity so create an
empty Roles set and return.
*/
Group[] roleSets = {new SimpleGroup("Roles")};
return roleSets;
}

do {
String name = rs.getString(1);
String groupName = rs.getString(2);
if (groupName == null || groupName.length() == 0)
groupName = "Roles";
Group group = (Group) setsMap.get(groupName);
if (group == null) {
group = new SimpleGroup(groupName);
setsMap.put(groupName, group);
}

try {
Principal p = aslm.createIdentity(name);
group.addMember(p);
}
catch (Exception e) {
//log.debug("Failed to create principal: " + name, e);
}
} while (rs.next());
}
catch (NamingException ex) {
LoginException le = new LoginException("Error looking up DataSource from: " + dsJndiName);
le.initCause(ex);
throw le;
}
catch (SQLException ex) {
LoginException le = new LoginException("Query failed");
le.initCause(ex);
throw le;
}
finally {
if (rs != null) {
try {
rs.close();
}
catch (SQLException e) {
}
}
if (ps != null) {
try {
ps.close();
}
catch (SQLException e) {
}
}
if (conn != null) {
try {
conn.close();
}
catch (Exception ex) {
}
}
if (suspendResume) {
TransactionDemarcationSupport.resumeAnyTransaction(tx);
//if (trace)
// log.trace("resumeAnyTransaction");
}
}
Group[] roleSets = new Group[setsMap.size()];
setsMap.values().toArray(roleSets);
return roleSets;
}



}

How it works ?
Login module get the provided login user and password and try to create the real connection to oracle via data source, that configured as application managed security, for this kind of data sources not need to provide login and password in data source configuration. In getUsersPassword() method need to verify provided username/password in oracle via getting connection. In case if connection succesful login module get configured roles for username.

Data source configuration for login module will be following:


<?xml version="1.0" encoding="UTF-8"?>

<datasources>

<local-tx-datasource>

<jndi-name>CrTimeAuthDS</jndi-name>

<connection-url>jdbc:oracle:thin:@127.0.0.1:1521:az3</connection-url>

<driver-class>oracle.jdbc.driver.OracleDriver</driver-class>

<!-- username and password not necessary, because of application-managed-security -->

<user-name></user-name>

<password></password>

<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter

</exception-sorter-class-name>

<min-pool-size>10</min-pool-size>

<max-pool-size>20</max-pool-size>

<blocking-timeout-millis>1000</blocking-timeout-millis>

<idle-timeout-minutes>1</idle-timeout-minutes>

<application-managed-security/>

<metadata>

<type-mapping>Oracle9i</type-mapping>

</metadata>

</local-tx-datasource>

</datasources>


JBoss login configuration:


<!-- part of login-config.xml -->

<application-policy name="crtime-webapp">

<authentication>

<login-module code="org.jboss.security.auth.spi.OracleDatabaseServerLoginModule" flag="required">

<module-option name="unauthenticatedIdentity">anonymous</module-option>

<module-option name = "dsJndiName">java:/CrTimeAuthDS</module-option>

<module-option name = "principalsQuery">SELECT USER_ID FROM all_users WHERE username=?</module-option>

<module-option name = "rolesQuery">SELECT granted_role, 'Roles' FROM user_role_privs WHERE upper(username)=upper(?)</module-option>

</login-module>

</authentication>

</application-policy>




For advanced programmers thats enough to catch the main idea. Only just small hint - we will use two datasources one for login module, second for application. In next part - Jboss/Tomcat context configuration, second datasource configuration, hibernate triks.