I would like to use Apache Shiro with database authentication. But I can't make database design changes. I would like to use my custom SQL command and Java logic to authenticate user. Is this possible? I tried this configuration in shiro.ini:
saltedJdbcRealm=com.crm.web.authentication.JdbcRealm
And custom Java class:
public class JdbcRealm extends AuthorizingRealm
{
#Resource(name = "jdbc/DefaultDB")
private DataSource dataSource;
protected static final String DEFAULT_AUTHENTICATION_QUERY = "select passwd from user where username = ?";
protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select passwd, passwd_salt from user where username = ?";
protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";
protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";
private static final Logger log = LoggerFactory.getLogger(JdbcRealm.class);
public enum SaltStyle
{
NO_SALT, CRYPT, COLUMN, EXTERNAL
};
protected String authenticationQuery = DEFAULT_AUTHENTICATION_QUERY;
protected String userRolesQuery = DEFAULT_USER_ROLES_QUERY;
protected String permissionsQuery = DEFAULT_PERMISSIONS_QUERY;
protected boolean permissionsLookupEnabled = false;
protected SaltStyle saltStyle = SaltStyle.NO_SALT;
public void setDataSource(DataSource dataSource)
{
this.dataSource = dataSource;
}
public void setAuthenticationQuery(String authenticationQuery)
{
this.authenticationQuery = authenticationQuery;
}
public void setUserRolesQuery(String userRolesQuery)
{
this.userRolesQuery = userRolesQuery;
}
public void setPermissionsQuery(String permissionsQuery)
{
this.permissionsQuery = permissionsQuery;
}
public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled)
{
this.permissionsLookupEnabled = permissionsLookupEnabled;
}
public void setSaltStyle(SaltStyle saltStyle)
{
this.saltStyle = saltStyle;
if (saltStyle == SaltStyle.COLUMN && authenticationQuery.equals(DEFAULT_AUTHENTICATION_QUERY))
{
authenticationQuery = DEFAULT_SALTED_AUTHENTICATION_QUERY;
}
}
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
{
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
// Null username is invalid
if (username == null)
{
throw new AccountException("Null usernames are not allowed by this realm.");
}
Connection conn = null;
SimpleAuthenticationInfo info = null;
try
{
conn = dataSource.getConnection();
String password = null;
String salt = null;
switch (saltStyle)
{
case NO_SALT:
password = getPasswordForUser(conn, username)[0];
break;
case CRYPT:
// TODO: separate password and hash from getPasswordForUser[0]
throw new ConfigurationException("Not implemented yet");
//break;
case COLUMN:
String[] queryResults = getPasswordForUser(conn, username);
password = queryResults[0];
salt = queryResults[1];
break;
case EXTERNAL:
password = getPasswordForUser(conn, username)[0];
salt = getSaltForUser(username);
}
if (password == null)
{
throw new UnknownAccountException("No account found for user [" + username + "]");
}
info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName());
if (salt != null)
{
info.setCredentialsSalt(ByteSource.Util.bytes(salt));
}
}
catch (SQLException e)
{
final String message = "There was a SQL error while authenticating user [" + username + "]";
if (log.isErrorEnabled())
{
log.error(message, e);
}
throw new AuthenticationException(message, e);
}
finally
{
JdbcUtils.closeConnection(conn);
}
return info;
}
private String[] getPasswordForUser(Connection conn, String username) throws SQLException
{
String[] result;
boolean returningSeparatedSalt = false;
switch (saltStyle)
{
case NO_SALT:
case CRYPT:
case EXTERNAL:
result = new String[1];
break;
default:
result = new String[2];
returningSeparatedSalt = true;
}
PreparedStatement ps = null;
ResultSet rs = null;
try
{
ps = conn.prepareStatement(authenticationQuery);
ps.setString(1, username);
// Execute query
rs = ps.executeQuery();
// Loop over results - although we are only expecting one result, since usernames should be unique
boolean foundResult = false;
while (rs.next())
{
// Check to ensure only one row is processed
if (foundResult)
{
throw new AuthenticationException("More than one user row found for user [" + username + "]. Usernames must be unique.");
}
result[0] = rs.getString(1);
if (returningSeparatedSalt)
{
result[1] = rs.getString(2);
}
foundResult = true;
}
}
finally
{
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(ps);
}
return result;
}
#Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
{
//null usernames are invalid
if (principals == null)
{
throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
}
String username = (String) getAvailablePrincipal(principals);
Connection conn = null;
Set<String> roleNames = null;
Set<String> permissions = null;
try
{
conn = dataSource.getConnection();
// Retrieve roles and permissions from database
roleNames = getRoleNamesForUser(conn, username);
if (permissionsLookupEnabled)
{
permissions = getPermissions(conn, username, roleNames);
}
}
catch (SQLException e)
{
final String message = "There was a SQL error while authorizing user [" + username + "]";
if (log.isErrorEnabled())
{
log.error(message, e);
}
// Rethrow any SQL errors as an authorization exception
throw new AuthorizationException(message, e);
}
finally
{
JdbcUtils.closeConnection(conn);
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
info.setStringPermissions(permissions);
return info;
}
protected Set<String> getRoleNamesForUser(Connection conn, String username) throws SQLException
{
PreparedStatement ps = null;
ResultSet rs = null;
Set<String> roleNames = new LinkedHashSet<String>();
try
{
ps = conn.prepareStatement(userRolesQuery);
ps.setString(1, username);
// Execute query
rs = ps.executeQuery();
// Loop over results and add each returned role to a set
while (rs.next())
{
String roleName = rs.getString(1);
// Add the role to the list of names if it isn't null
if (roleName != null)
{
roleNames.add(roleName);
}
else
{
if (log.isWarnEnabled())
{
log.warn("Null role name found while retrieving role names for user [" + username + "]");
}
}
}
}
finally
{
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(ps);
}
return roleNames;
}
protected Set<String> getPermissions(Connection conn, String username, Collection<String> roleNames) throws SQLException
{
PreparedStatement ps = null;
Set<String> permissions = new LinkedHashSet<>();
try
{
ps = conn.prepareStatement(permissionsQuery);
for (String roleName : roleNames)
{
ps.setString(1, roleName);
ResultSet rs = null;
try
{
// Execute query
rs = ps.executeQuery();
// Loop over results and add each returned role to a set
while (rs.next())
{
String permissionString = rs.getString(1);
// Add the permission to the set of permissions
permissions.add(permissionString);
}
}
finally
{
JdbcUtils.closeResultSet(rs);
}
}
}
finally
{
JdbcUtils.closeStatement(ps);
}
return permissions;
}
protected String getSaltForUser(String username)
{
return username;
}
}
But when I run the code I get:
org.apache.shiro.authc.AuthenticationException: Authentication token of type [class org.apache.shiro.authc.UsernamePasswordToken] could not be authenticated by any configured realms. Please ensure that at least one realm can authenticate these tokens.
Am I missing some configuration in shiro.ini
This is how we do it in XML(shiro.xml) :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter">
<property name="redirectUrl" value="YOUR_LOGIN_URL" />
</bean>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login"/>
<property name="successUrl" value="YOUR_SUCCESS_URL"/>
<property name="unauthorizedUrl" value="YOUR_ACCESS_DENIED_URL"/>
<property name="filters">
<util:map>
<entry key="logout" value-ref="logout"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/** = authc <!--SPECIFY_OTHERS_FILTERS_CHAINS-->
</value>
</property>
</bean>
<bean id="builtInCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"/>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- Single realm app. If you have multiple realms, use the 'realms' property instead. -->
<property name="realm" ref="myRealm"/>
<property name="cacheManager" ref="builtInCacheManager"/>
<!-- By default the servlet container sessions will be used. Uncomment this line-->
<!-- to use shiro's native sessions (see the JavaDoc for more): -->
<!-- <property name="sessionMode" value="native"/> -->
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- Define the Shiro Realm implementation you want to use to connect to your back-end -->
<!-- security datasource: -->
<bean id="myRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="credentialsMatcher" ref="hashMatcher"/>
<property name="authenticationQuery" value="select password from user_login where user_id = ?"/>
<property name="userRolesQuery" value="YOUR_ROLE_QUERY"/>
<property name="permissionsQuery" value="YOUR_PERMISSION_QUERY" />
<property name="permissionsLookupEnabled" value="true"></property>
<property name="dataSource" ref="YOUR_DATA_SOURCE_NAME"/> <!-- i.e. being used for the DB connection -->
</bean>
<!-- Hash Matcher Bean responsible for matching credentials of logging user -->
<bean id="hashMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- Algorithm name -->
<property name="hashAlgorithmName" value="SHA-512"/>
<!-- No. of Hash Iterations. Note: must match with iterations used to save password in database. -->
<property name="hashIterations" value="10000"/>
<!-- true if Stored Credentials(i.e. password and salt) are in Hexadecimal form. False denotes BASE64 encoding.-->
<property name="storedCredentialsHexEncoded" value="true"/>
</bean>
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after -->
<!-- the lifecycleBeanProcessor has run: -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
You can include it in the application config file (web.xml)
All shiro needs to mark session as authenticated is AuthenticationInfo object. How it's built is up to you.
The realm should be tied to the security manager.
I want to give you 2 suggestions. Hope it will help you.
Suggestion - 1:
Configuration file is not fully configured for Realm.
You should write a class for AuthorizingRealm, then the class will be configured to configuration file.
If you use the spring, then the configuration will look like below:
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="localRealm" />
</bean>
<bean id="localRealm" class="com.xxxx.xxxxx.infra.LocalSecurityRealm">
<constructor-arg index="0" ref="securityApplication" />
</bean>
Add authenticator in shiro.ini configuration file
authenticator = com.realm.MyRealm
Resource Link:
http://www.oschina.net/question/617087_72790#answers
Suggestion - 2:
You need to first make sure that supports() are actually reached and executed.
#Override
public boolean supports(AuthenticationToken authenticationToken) {
return (authenticationToken instanceof UsernamePasswordToken)
}
If you have multiple realms and one throws an error, the others will NOT be processed.
So if you need to work around thrown Exceptions you can do something like this for authz and this for authc.
Resource Link:
http://shiro-user.582556.n2.nabble.com/Still-having-an-issue-with-multiple-realms-td7579698.html
Related
Currently i am developing an application where users are authenticated via LDAP. The criteria for successful login is based on correct username,password and group(TEST-FFSUS-CALBR-USER).
i was able to login successfully with username and password criteria , but not with group criteria.
Following are the code used for this purpose :
#Resource
LdapTemplate ldapTemplate;
public boolean login(String username, String password) {
try {
ContainerCriteria searchCriteria = getLdapFilterCriteria(username);
boolean result = ldapTemplate.authenticate("OU=User,OU=fo-id,DC=feefusde,DC=rootdom,DC=net",
searchCriteria.filter().encode(), password);
return result;
} catch (Exception e) {
return false;
}
}
private ContainerCriteria getLdapFilterCriteria(String usernameOrEmail) {
ContainerCriteria objectClassCriteria = LdapQueryBuilder.query().base("DC=rootdom,DC=net").where("objectClass")
.is("user");
ContainerCriteria mailCriteria = LdapQueryBuilder.query().where("mail").is(usernameOrEmail);
ContainerCriteria cnCriteria = LdapQueryBuilder.query().where("CN").is(usernameOrEmail);
ContainerCriteria roleCriteria = LdapQueryBuilder.query().where("memberOf=CN").is("TEST-FFSUS-CALBR-USER");
ContainerCriteria userByMailOrCnCriteria = mailCriteria.or(cnCriteria);
ContainerCriteria searchCriteria = objectClassCriteria.and(userByMailOrCnCriteria).and(roleCriteria);
return searchCriteria;
}
In application-context:
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<property name="contextSource">
<bean class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg>
<value>ldap://urlofldap:portno</value>
</constructor-arg>
<property name="userDn" value="${ldap.username}" />
<property name="password" value="${ldap.password}" />
</bean>
</property>
</bean>
Is there any way to fix above issue. ?
The memberOf attribute should match the group distinguished name. For example "CN=TEST-FFSUS-CALBR-USER,OU=roles,DC=feefusde,DC=rootdom,DC=net":
ContainerCriteria roleCriteria = LdapQueryBuilder.query().where("memberOf").is(groupDN);
I'm in the process of upgrading the spring framework version used in our webapp from 3.1.4 to 4.1.8. With the new Spring version, A few of our unit tests are failing because #Autowired is no longer working. This is one of the failing tests:
#ContextConfiguration(locations={"/math-application-context.xml"})
public class MathematicaMathServiceTest extends JavaMathServiceTest{
#Autowired
private KernelLinkPool mathematicalKernelPool;
protected static String originalServiceType = System.getProperty("calculation.math.service.type");
#AfterClass
public static void unsetMathServiceType(){
System.clearProperty("calculation.math.service.type");
}
#BeforeClass
public static void setMathServiceType(){
System.setProperty("calculation.math.service.type","Mathematica");
}
#Test
public void testMathematicaService() throws Exception{
try {
acquireKernelAndExecute(0);
Assert.assertEquals(0, mathematicalKernelPool.getBorrowingThreadsCount());
} catch(UnsatisfiedLinkError e) {
System.out.println("Mathematica not installed. Skipping test");
}catch(Exception ex){
if (!ExceptionFormatter.hasCause(ex, MathServiceNotConfiguredException.class)){throw ex;}
if (System.getProperty(MathService.SERVICE_CONFIGURED_SYSTEM_VARIABLE) != null){
throw ex;
}
logger.error("Cannot execute test. Math service is not configured");
}
}
}
This is the KernelLinkPool class:
public class KernelLinkPool extends GenericObjectPool implements InitializingBean{
private static final int RETRY_TIMEOUT_MS = 5000;
private static final long STARTUP_WAIT_TIME_MS = 10000;
private boolean mathematicaConfigured = true;
private PoolableObjectFactory factory;
// ensures that multiple requests from the same thread will be given the same KernelLink object
private static ThreadLocal<KernelLink> threadBoundKernel = new ThreadLocal<KernelLink>();
// holds the number of requests issued on each thread
private static ThreadLocal<Integer> callDepth = new ThreadLocal<Integer>();
private long maxBorrowWait;
private Integer maxKernels;
private boolean releaseLicenseOnReturn;
private Logger logger = LoggerFactory.getLogger(this.getClass());
// (used only for unit testing at this point)
private Map<String,Integer> borrowingThreads = new ConcurrentHashMap<String,Integer>();
public KernelLinkPool(PoolableObjectFactory factory) {
super(factory);
this.factory = factory;
this.setMaxWait(maxBorrowWait);
}
#Override
public Object borrowObject() throws Exception{
return borrowObject(this.maxBorrowWait);
}
public Object borrowObject(long waitTime) throws Exception {
long starttime = System.currentTimeMillis();
if (!mathematicaConfigured){
throw new MathServiceNotConfiguredException();
}
try{
if (callDepth.get() == null){
callDepth.set(1);
}else{
callDepth.set(callDepth.get()+1);
}
KernelLink link = null;
if (threadBoundKernel.get() != null){
link = threadBoundKernel.get();
}else{
//obtain kernelLink from object pool
//retry when borrowObject fail until
//maxBorrowWait is reached
while(true){
try{
logger.debug("Borrowing MathKernel from object pool");
link = (KernelLink) super.borrowObject();
break;
}catch(KernelLinkCreationException ex){
long timeElapsed = System.currentTimeMillis() - starttime;
logger.info("Failed to borrow MathKernel. Time elapsed [" + timeElapsed + "] ms", ex);
if(timeElapsed >= waitTime){
logger.info("Retry timeout reached");
throw ex;
}
Thread.sleep(RETRY_TIMEOUT_MS);
}
}
logger.debug("borrowed [" + link + "]");
threadBoundKernel.set(link);
}
borrowingThreads.put(Thread.currentThread().getName(),callDepth.get());
return link;
}catch(Exception ex){
logger.error("Failed to acquire Mathematica kernel. Borrowing threads [" + borrowingThreads + "]");
throw ex;
}
}
public void returnObject(Object obj) throws Exception {
callDepth.set(callDepth.get()-1);
if (callDepth.get() <= 0){
threadBoundKernel.set(null);
borrowingThreads.remove(Thread.currentThread().getName());
if (releaseLicenseOnReturn){
// will destroy obj
super.invalidateObject(obj);
}
else{
// will park obj in the pool of idle objects
super.returnObject(obj);
}
}else{
borrowingThreads.put(Thread.currentThread().getName(),callDepth.get());
}
}
#Override
public void afterPropertiesSet() throws Exception {
try{
if (maxKernels == 0){
List<KernelLink> links = new ArrayList<KernelLink>();
while (true){
try{
links.add((KernelLink)factory.makeObject());
}catch(KernelLinkCreationException ex){
break;
}
}
if(links.isEmpty()){
logger.warn("No available Mathematica license!");
mathematicaConfigured = false;
return;
}
for (KernelLink link : links){
factory.destroyObject(link);
}
logger.info("Detected number of available Mathematica license = [" + links.size() + "]");
setMaxActive(links.size());
setMaxIdle(links.size());
}else{
if(maxKernels < 0){
logger.info("Set number of Mathematica license to no limit");
}else{
logger.info("Set number of Mathematica license to [" + maxKernels + "]");
}
setMaxActive(maxKernels);
setMaxIdle(maxKernels);
}
Object ob = borrowObject(STARTUP_WAIT_TIME_MS);
returnObject(ob);
mathematicaConfigured = true;
}catch(Throwable ex){
logger.warn("Mathematica kernel pool could not be configured: ", ex.getMessage());
mathematicaConfigured = false;
}
}
public int getBorrowingThreadsCount() {
return borrowingThreads.size();
}
public Integer getMaxKernels() {
return maxKernels;
}
public void setMaxKernels(Integer maxKernels) {
this.maxKernels = maxKernels;
}
public boolean isMathematicaConfigured(){
return mathematicaConfigured;
}
public boolean isReleaseLicenseOnReturn() {
return releaseLicenseOnReturn;
}
public void setReleaseLicenseOnReturn(boolean releaseLicenseOnReturn) {
this.releaseLicenseOnReturn = releaseLicenseOnReturn;
}
public long getMaxBorrowWait() {
return maxBorrowWait;
}
public void setMaxBorrowWait(long maxBorrowWait) {
this.maxBorrowWait = maxBorrowWait;
}
}
The tests are failing with this exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.etse.math.wolfram.KernelLinkPool] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
This is the math-application-context file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<beans profile="unitTest,integratedTest,activeServer">
<bean class="org.springframework.jmx.export.MBeanExporter"
lazy-init="false">
<property name="registrationBehaviorName" value="REGISTRATION_IGNORE_EXISTING" />
<property name="beans">
<map>
<entry key="etse.math:name=MathematicalKernelFactory"
value-ref="mathematicalKernelFactory" />
<entry key="etse.math:name=MathematicalKernelPool" value-ref="mathematicalKernelPool" />
</map>
</property>
</bean>
<bean id="mathService" class="com.etse.math.MathServiceFactoryBean">
<property name="mathServiceType" value="${calculation.math.service.type}"/>
<property name="mathematicaService" ref="mathematicaService"/>
</bean>
<bean id="mathematicaService" class="com.etse.math.wolfram.MathematicaService">
<property name="kernelPool" ref="mathematicalKernelPool" />
<property name="minParallelizationSize" value="${calculation.mathematica.kernel.parallel.batch.size}" />
</bean>
<bean id="mathematicalKernelPool" class="com.etse.math.wolfram.KernelLinkPool"
destroy-method="close">
<constructor-arg ref="mathematicalKernelFactory" />
<property name="maxKernels" value="${calculation.mathematica.max.kernels}" />
<property name="maxBorrowWait"
value="${calculation.mathematica.kernel.borrow.max.wait}" />
<property name="releaseLicenseOnReturn"
value="${calculation.mathematica.kernel.release.license.on.return}" />
</bean>
<bean id="mathematicalKernelFactory" class="com.etse.math.wolfram.KernelLinkFactory">
<property name="debugPackets" value="false" />
<property name="linkMode" value="launch" />
<property name="mathematicaKernelLocation" value="${calculation.mathematica.kernel.location}" />
<property name="mathematicaLibraryLocation" value="${calculation.mathematica.library.location}" />
<property name="mathematicaAddOnsDirectory" value="${calculation.mathematica.addons.directory}" />
<property name="linkProtocol" value="sharedMemory" />
</bean>
</beans>
<beans profile="passiveServer,thickClient,tools">
<bean id="mathService" class="com.etse.math.DummyMathService"/>
</beans>
I also tried using the application context to load the bean, but that failed with the following exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'mathematicalKernelPool' is defined
If I remove the autowired field, the test fails with a NoSuchBeanDefinitionException for another bean (mathService) that is loaded via the application context in a super class. So it appears that the application context from math-application-context is not loaded for some reason. Any idea of what could be happening here? Thank you.
UPDATE:
I took a look at the beans defined in the application context and confirmed that none of the beans defined in math-application-context are present. The application context contains only beans defined in another context file loaded by the super class. Why would it fail to load math-application-context?
At this point I would honestly get rid of the XML config and go total annotation/code based. Create a Config class and have it create any beans you need to be autowired.
It was a profile issue. The super class to the test was using:
#ProfileValueSourceConfiguration(TestProfileValueSource.class)
to set the profile, but it was not working. After removing that annotation I added:
#ActiveProfiles(resolver=TestProfileValueSource.class) and now its working again.
I'm using hibernate to add my objects into the database.
When I call sessionCacti.save(Object object) it stores the object to database while the object shouldn't not be stored because I didn't commit the changes and I use a non auto-commit mode.
Here is the file of the configuration of hibernate
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="hibernate.current_session_context_class">thread</property>
<!-- pcac is the database name -->
<property name="hibernate.connection.url">
jdbc:mysql://uaeipvm1:3307/pcac?zeroDateTimeBehavior=convertToNull
</property>
<property name="hibernate.connection.username">
myadmin
</property>
<property name="hibernate.connection.password">
dbaft
</property>
<!-- 1: READ UNCOMMITTED 2: READ COMMITTED 4: REPEATABLE READ 8: SERIALIZABLE -->
<property name="hibernate.connection.isolation">2</property>
<!-- List of XML mapping files -->
<mapping resource="Host.hbm.xml" />
<mapping resource="HostGraph.hbm.xml" />
<mapping resource="HostTemplate.hbm.xml" />
<mapping resource="HostTemplateGraph.hbm.xml" />
<mapping resource="Settings.hbm.xml" />
</session-factory>
</hibernate-configuration>
And this is my program :
public class SynchroCacti {
public static Map<String,Host> hosts = new HashMap<String, Host>();
private static SessionFactory factoryDerbi,factoryCacti;
private static Transaction tx=null;
private static Session sessionDerbi,sessionCacti;
public static List<Equipment> derbiEquipments;
private static List<HostTemplate> hostTemplates;
private static List<Settings> settings;
private static int nbNewHosts = 0;
private static int initialNbCactiHost,initialNbDerbiEqpts;
public static void initializeData ()
{
try{
factoryDerbi = new Configuration().configure("/hibernate_derbi.cfg.xml").buildSessionFactory();
factoryCacti = new Configuration().configure("/hibernate_cacti.cfg.xml").buildSessionFactory();
sessionDerbi = factoryDerbi.openSession();
sessionCacti = factoryCacti.openSession();
tx = sessionCacti.beginTransaction();
}catch (Throwable ex) {
logErrorsInProgram.error("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
//Store tables of the database in these lists
derbiEquipments = sessionDerbi.createCriteria(Equipment.class).list();
List<Host> cactiHosts = sessionCacti.createCriteria(Host.class).list();
hostTemplates = sessionCacti.createCriteria(HostTemplate.class).list();
settings = sessionCacti.createCriteria(Settings.class).list();
//Initialize the hash map of hosts
Iterator<Host> it = cactiHosts.iterator();
while (it.hasNext())
{
Host actual = it.next();
hosts.put(actual.getDescription(), actual);
}
//Initialize values of initialNbCactiHost and initialNbDerbiEqpts from sizes of lists
initialNbCactiHost = hosts.size();
initialNbDerbiEqpts = derbiEquipments.size();
}
public static void addHostToDatabase (Host host)
{
nbNewHosts++;
try{
sessionCacti.save(host);
}catch (ConstraintViolationException e) {
if (tx!=null) tx.rollback();
}finally { //We don't close session
}
}
public static void main(String[] args) throws HibernateException, SQLException, ParseException {
initializeData();
Host host = createHost(derbiEquipments.get(0));
addHostToDatabase(host);
}
}
I don't want to save data until I execute tx.commit();
Thanks.
The following managed operation exists in the project:
#ManagedOperation(description = "Some description")
#ManagedOperationParameters({
#ManagedOperationParameter(name = "key", description = "Some description"),
})
public void foo(String key) {
// some logic
}
Also there is a property which can be used in Spring context by surrounding it with dollar sign and square brackets:
"${some.property.key}"
Is it possible to use the value of aforementioned property key in the managed operation annotation description? Something like:
#ManagedOperationParameter(name = "key",
description = "Some description, please note that the key is ${some.property.key}")
Not out-of-the-box, but it's pretty easy to customize...
public class CustomAttributeSource extends AnnotationJmxAttributeSource implements EmbeddedValueResolverAware {
private StringValueResolver embeddedValueResolver;
#Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
#Override
public ManagedAttribute getManagedAttribute(Method method) throws InvalidMetadataException {
ManagedAttribute managedAttribute = super.getManagedAttribute(method);
if (this.embeddedValueResolver != null) {
managedAttribute
.setDescription(this.embeddedValueResolver.resolveStringValue(managedAttribute.getDescription()));
}
return managedAttribute;
}
#Override
public ManagedOperation getManagedOperation(Method method) throws InvalidMetadataException {
ManagedOperation managedOperation = super.getManagedOperation(method);
if (this.embeddedValueResolver != null) {
managedOperation
.setDescription(this.embeddedValueResolver.resolveStringValue(managedOperation.getDescription()));
}
return managedOperation;
}
}
Then...
<bean class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter">
<property name="assembler">
<bean class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource">
<bean class="foo.CustomAttributeSource" />
</property>
</bean>
</property>
</bean>
I am getting this below exception when I am trying to execute a Stored procedure using HIbernate from DaoImpl Class.
I am not sure of what is wrong ..I tried all the ways to fix it but did not resolve the issue.
Could anyone please help me ,figure out whats wrong with the code or the mapping file.
The more I am trying to fix ,something the more exceptions I am getting.. I am connectuing to Oracle 9i DB.
I am struggling on this from really 2 weeks ending up no where.. can anyone please help me fix this issue.
Mapping file:
<hibernate-mapping>
<sql-query name="proc_drsrr_sel_ValDDSummaryBal">
<!--CALL proc_drsrr_sel_ValDDSummaryBal(:param1)]]>-->
{ call DEFAULT_SCHEMA.proc_name(?,:param1) }
Main-Class:
public static void main(String[] args) {
String procName = "proc_name";// args[0];
String params = "param1:500089" ;
DAO Implementation:
#SuppressWarnings("unchecked")
public void callProc(String procName, Map paramMap) throws SQLException {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSessionFactory().getCurrentSession();
tx = session.beginTransaction();
String dbURL = session.connection().getMetaData().getURL().toString();
System.out.println("Conenction DB URL "+ dbURL );
tx.setTimeout(5);
String[] keys = new String[paramMap.size()];
keys = (String[]) paramMap.keySet().toArray(keys);
Query query = session.getNamedQuery(procName)
.setParameter("param1", "5501010");
}
List result = query.list();
System.out.println(query.getQueryString());
for (int i = 0; i < result.size(); i++) {
// logging the information.
log.info(i);
}
tx.commit();
} catch (RuntimeException exception) {
exception.printStackTrace();
try {
tx.rollback();
} catch (RuntimeException rbe) {
log.error("Couldn’t roll back transaction", rbe);
rbe.printStackTrace();
}
throw exception;
} finally{
if(session !=null){
session.flush();
session.close();
}
cfg.xml
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.url">jdbc:oracle:thin:#ldap://hdsoid.ute.ovi.com:3060/UT1DEV,cn=OracleContext,dc=ute,dc=ovi,dc=com</property>
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.username">nameapp</property>
<property name="connection.password">nameapp</property>
<property name="connection.pool_size">1</property>
<property name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property>
<!-- <property name="connection.release_mode">after_statement</property> -->
<property name="default_schema">DEFAULT_SCHEMA</property>
<property name="current_session_context_class">thread</property>
<property name="hibernate.show_sql">true</property>
<!-- mapping files -->
<mapping resource="com/ovi/domain/hibernate.hbm.xml" />
</session-factory>
</hibernate-configuration>
You are missing to set the ? parameter which is a so called positional parameter. In contrast to named parameters like :foo
When you have some SQL you must also ensure not to have any Question Marks inside comments! That's what I just ran into. Same holds for : in comments, especially if they are followed by a space.