I'm trying to implement Postgres Row Level Security on my app that uses R2DBC.
I found this AWS post that implements this but uses a non-reactive approach.
I'm having problems converting this to a reactive approach since I can't find a class equivalent to the AbstractRoutingDataSource:
public class TenantAwareDataSource extends AbstractRoutingDataSource {
private static final Logger LOGGER = LoggerFactory.getLogger(TenantAwareDataSource.class);
#Override
protected Object determineCurrentLookupKey() {
Object key = null;
// Pull the currently authenticated tenant from the security context
// of the HTTP request and use it as the key in the map that points
// to the connection pool (data source) for each tenant.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
try {
if (!(authentication instanceof AnonymousAuthenticationToken)) {
Tenant currentTenant = (Tenant) authentication.getPrincipal();
key = currentTenant.getId();
}
} catch (Exception e) {
LOGGER.error("Failed to get current tenant for data source lookup", e);
throw new RuntimeException(e);
}
return key;
}
#Override
public Connection getConnection() throws SQLException {
// Every time the app asks the data source for a connection
// set the PostgreSQL session variable to the current tenant
// to enforce data isolation.
Connection connection = super.getConnection();
try (Statement sql = connection.createStatement()) {
LOGGER.info("Setting PostgreSQL session variable app.current_tenant = '{}' on {}", determineCurrentLookupKey().toString(), this);
sql.execute("SET SESSION app.current_tenant = '" + determineCurrentLookupKey().toString() + "'");
} catch (Exception e) {
LOGGER.error("Failed to execute: SET SESSION app.current_tenant = '{}'", determineCurrentLookupKey().toString(), e);
}
return connection;
}
#Override
public String toString() {
return determineTargetDataSource().toString();
}
}
What would be the equivalent on R2DBC to AbstractRoutingDataSource?
Thanks
Full source code here.
#Transactional annotation is not working for me in a springboot-hibernate project. I am using the annotation configuration for which i have done the following configuration. I have tried using the #Transactional on method and class name in the service layer and also in the dao layer, but no luck. I think there is some issue with the transaction manager configuration but I am unable to figure out how to configure the transaction manager in my application.
application.properties
#spring configuration
spring.jpa.show-sql = true
#spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
#spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
dao
#Autowired
private EntityManagerFactory entityManagerFactory;
#Override
public void deleteSMS(String id) {
logger.info("Delete sms details with id :: \"" + id + "\"");
Session session = null;
try {
session = entityManagerFactory.unwrap(SessionFactory.class).openSession();
SMSDetails smsDetails = session.get(SMSDetails.class, Long.parseLong(id));
if (smsDetails != null)
session.delete(smsDetails);
} catch (Exception e) {
logger.error("Error occured while deleting the sms with id :: \"" + id + "\" :: " + e.getMessage());
throw e;
} finally {
if (session != null)
session.close();
}
}
service
#Override
#Transactional
public void deleteSMS(String id) {
smsDao.deleteSMS(id);
}
I am using spring boot 2.1.3 and hibernate. I have configured the entitymanagerfactory as above and used the following to obtain session
session = entityManagerFactory.unwrap(SessionFactory.class).openSession();
but the #Transactional is not working
You are opening a Session inside a #Transactional method. This is wrong, because when you annotate method as transactional, it is invoked inside a single session, you don't need to open another one.
I had the same problem
I added this annotation on my class application
#EnableTransactionManagement (proxyTargetClass = true)
And this on the method on my class service
#Transactional (rollbackFor = Exception.class)
I am trying to use PowerMockito to mock by DBUtil. Unlike typical testcase, I don't want to mock the db calls completely. Whenever Dbutil.getConnection() is called. I want to return the connection object to my local Database.
The simple jdbc connection code below is not working when i call from #BeforeClass method. But it works when I call from the java class.
public static Connection getConnection() throws Exception {
System.out.println("-------- Connecting to " + Constants.CONNECTION_STR + " ------");
try {
Class.forName(Constants.ORACLE_DRIVER_NAME);
}
catch (ClassNotFoundException e) {
throw new Exception("JDBC Driver not found... " + e);
}
catch (Exception e) {
// TODO: handle exception
System.out.println("getConnection :: exp :: "+ e);
}
System.out.println("Oracle JDBC Driver Registered Sucessfully!");
Connection connection = null;
try {
connection = DriverManager.getConnection(Constants.CONNECTION_STR, Constants.USERNAME, Constants.PASSWORD);
}
catch (SQLException e) {
throw new Exception("Connection Failed!",e);
}
if (connection != null) {
System.out.println("Connected to Database Sucessfully, take control your database now!");
return connection;
}
System.out.println("Failed to make connection!");
return null;
}
My Testclass
#RunWith (PowerMockRunner.class)
#PrepareForTest(DbUtil.class)
public class MyUtilTest {
#Mock
private DbUtil dbUtil;
#InjectMocks
private MyUtil myUtil;
private static Connection myDBConn;
#BeforeClass
public static void beforeClass() throws Exception {
myDBConn = OracleJDBCConnetion.getConnection(); // This always throws invalid username/password exception.
}
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testIsAdminUser() throws Throwable{
PowerMockito.mockStatic(DbUtil.class);
PowerMockito.when(DbUtil.getConnection()).thenReturn(myDBConn);
String accId= "TH123" ;
boolean isAdmin = MyUtil.isAdminUser(cloudAccGuid);
System.out.println("isAdmin : " + isAdmin);
//then
PowerMockito.verifyStatic(Mockito.times(1));
DbUtil.getConnection();
assertTrue(isAdmin);
//Finally I am closing my connection.
if(myDBConn!=null && !myDBConn.isClosed())
OracleJDBCConnetion.closeConnection(myDBConn);
}
}
The beforeClass method always throws below expection.
Connection Failed! java.sql.SQLException: ORA-01017: invalid username/password; logon denied
But the same code works, when i try from normal Java class.
Can anyone help in understanding whats wrong here?
I am using ojdbc6.jar and powermokito-1.5.6 and my Oracle database version is 11.2.0.4.0
Thanks.
Edit :
I found that #PrepareForTest annotation is causing the error. without the annotation connection is successful but mock does not work. can anyone help me in understanding what is happening? I am very new to these mocking stuff.
The problem with #PrepareForTest annotation is, it recursively creates stubs for all dependent classes. Since DBUtil class uses java.sql.Connection class , a stub is created for Connection class also.
So, When i try to create connection, it refers to stub class and throws expection.
Add #PowerMockIgnore annotation to the class,to avoid it. #PowerMockIgnore annotation tells the powermock not to create for the classes that falls under the given package.
#RunWith (PowerMockRunner.class)
#PrepareForTest({DbUtil.class})
#PowerMockIgnore({"java.sql.*"})
public class MyUtilTest {
...
}
This worked for me.
I am trying to call RES server (v 7.1) from EAR deployed on WAS (8.5) instance. I was able to invoke rule server from standalone program and its working without any problems.
However my main problem is to invoke EJB deployed on RES server remotely from another EAR deployed on some other WAS instance. In this case we are not able to look-up the EJB remotely.
As per below thread we should bypass the EJB3 IlrSessionFactory API and should use Java EJB API to look up rule sessions directly.
http://www-01.ibm.com/support/docview.wss?uid=swg21586621
Recommendation from IBM is to use standard java api for ejb lookup or to upgrade to Rule Server 7.5 (latest 8.x).
Code snippet
// Initialization
Map<String, Object> outputParms = null;
IlrStatelessSession session=null;
IlrSessionResponse response=null;
// IlrSessionFactory factory = getFactory();
try {
sessionFactory = JRulesInvoker.getFactory();
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.ibm.websphere.naming.WsnInitialContextFactory");
env.put(Context.PROVIDER_URL,"corbaloc:iiop:localhost:28004");
Context ctx = new InitialContext(env);
Object lookupResult = ctx.lookup("ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote");
PortableRemoteObject aPortableRemoteObject = new PortableRemoteObject();
session = (IlrStatelessSession) aPortableRemoteObject.narrow(lookupResult, IlrStatelessSession.class);
IlrPath path = new IlrPath(ruleApp, ruleSet);
IlrSessionRequest request = sessionFactory.createRequest();
request.setRulesetPath(path);
request.setInputParameters(inputParms);
request.getTraceFilter().setInfoTotalRulesFired(true);
request.getTraceFilter().setInfoExecutionEvents(true);
request.setTraceEnabled(true);
// session = sessionFactory.createStatelessSession();
System.out.println("created session " + IlrJNDIConstants.STATELESS_SESSION_EJB3_NAME);
response = session.execute(request);
System.out.println(response.getRulesetExecutionTrace().getTotalRulesFired() + " rule(s) fired.");
System.out.println("Execution output=" + response.getRulesetExecutionOutput());
// Return the result(s)
outputParms = response.getOutputParameters();
if (logger.isEnabledFor(Level.DEBUG)) {
if (response.getRulesetExecutionOutput() != null) {
logger.debug("RuleSet execution output: \n" + response.getRulesetExecutionOutput());
}
}
}catch (IlrSessionCreationException cx) {
if (logger.isEnabledFor(Level.ERROR)) {
logger.error(cx.getMessage(), cx);
}
} catch (IlrSessionException e) {
if (logger.isEnabledFor(Level.ERROR)) {
logger.error(e.getMessage(), e);
}
} catch (NamingException e) {
if (logger.isEnabledFor(Level.ERROR)) {
logger.error(e.getMessage(), e);
}
}
Error
Context: idewas/nodes/ide/servers/server1, name: ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote: First component in name ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote not found.
javax.naming.NameNotFoundException: Context: idewas/nodes/ide/servers/server1, name: ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote: First component in name ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote not found. [Root exception is org.omg.CosNaming.NamingContextPackage.NotFound: IDL:omg.org/CosNaming/NamingContext/NotFound:1.0]
at com.ibm.ws.naming.jndicos.CNContextImpl.mapNotFoundException(CNContextImpl.java:4563)
at com.ibm.ws.naming.jndicos.CNContextImpl.doLookup(CNContextImpl.java:1821)
at com.ibm.ws.naming.jndicos.CNContextImpl.doLookup(CNContextImpl.java:1776)
at com.ibm.ws.naming.jndicos.CNContextImpl.lookupExt(CNContextImpl.java:1433)
at com.ibm.ws.naming.jndicos.CNContextImpl.lookup(CNContextImpl.java:615)
at com.ibm.ws.naming.util.WsnInitCtx.lookup(WsnInitCtx.java:165)
at com.ibm.ws.naming.util.WsnInitCtx.lookup(WsnInitCtx.java:179)
at org.apache.aries.jndi.DelegateContext.lookup(DelegateContext.java:161)
at javax.naming.InitialContext.lookup(InitialContext.java:436)
Check in the SystemOut.log of the RES server what are the binding names for EJBs as it looks like there is no ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote there. Also if you have two servers on the same host under the same name e.g. server1 you may have interoberability issues and need to set JVM property com.ibm.websphere.orb.uniqueServerName to true. For more details check the following page Application access problems
We have a JPA application (using hibernate) and we need to pass a call to a legacy reporting tool that needs a JDBC database connection as a parameter. Is there a simple way to get access to the JDBC connection hibernate has setup?
As per the hibernate docs here,
Connection connection()
Deprecated. (scheduled for removal in 4.x). Replacement depends on need; for doing direct JDBC stuff use
doWork(org.hibernate.jdbc.Work) ...
Use Hibernate Work API instead:
Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
// do whatever you need to do with the connection
}
});
Where you want to get that connection is unclear. One possibility would be to get it from the underlying Hibernate Session used by the EntityManager. With JPA 1.0, you'll have to do something like this:
Session session = (Session)em.getDelegate();
Connection conn = session.connection();
Note that the getDelegate() is not portable, the result of this method is implementation specific: the above code works in JBoss, for GlassFish you'd have to adapt it - have a look at Be careful while using EntityManager.getDelegate().
In JPA 2.0, things are a bit better and you can do the following:
Connection conn = em.unwrap(Session.class).connection();
If you are running inside a container, you could also perform a lookup on the configured DataSource.
If you are using JAVA EE 5.0, the best way to do this is to use the #Resource annotation to inject the datasource in an attribute of a class (for instance an EJB) to hold the datasource resource (for instance an Oracle datasource) for the legacy reporting tool, this way:
#Resource(mappedName="jdbc:/OracleDefaultDS") DataSource datasource;
Later you can obtain the connection, and pass it to the legacy reporting tool in this way:
Connection conn = dataSource.getConnection();
if you use EclipseLink:
You should be in a JPA transaction to access the Connection
entityManager.getTransaction().begin();
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class);
...
entityManager.getTransaction().commit();
Hibernate 4 / 5:
Session session = entityManager.unwrap(Session.class);
session.doWork(connection -> doSomeStuffWith(connection));
Since the code suggested by #Pascal is deprecated as mentioned by #Jacob, I found this another way that works for me.
import org.hibernate.classic.Session;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.engine.SessionFactoryImplementor;
Session session = (Session) em.getDelegate();
SessionFactoryImplementor sfi = (SessionFactoryImplementor) session.getSessionFactory();
ConnectionProvider cp = sfi.getConnectionProvider();
Connection connection = cp.getConnection();
The word pure doesn't match to the word hibernate.
EclipseLink
Getting a JDBC Connection from an EntityManager
It's somewhat straightforward as described in above link.
Note that the EntityManager must be joined to a Transaction or the unwrap method will return null. (Not a good move at all.)
I'm not sure the responsibility of closing the connection.
// --------------------------------------------------------- EclipseLink
try {
final Connection connection = manager.unwrap(Connection.class);
if (connection != null) { // manage is not in any transaction
return function.apply(connection);
}
} catch (final PersistenceException pe) {
logger.log(FINE, pe, () -> "failed to unwrap as a connection");
}
Hibernate
It should be, basically, done with following codes.
// using vendor specific APIs
final Session session = (Session) manager.unwrap(Session.class);
//return session.doReturningWork<R>(function::apply);
return session.doReturningWork(new ReturningWork<R>() {
#Override public R execute(final Connection connection) {
return function.apply(connection);
}
});
Well, we (at least I) might don't want any vendor-specific dependencies. Proxy comes in rescue.
try {
// See? You shouldn't fire me, ass hole!!!
final Class<?> sessionClass
= Class.forName("org.hibernate.Session");
final Object session = manager.unwrap(sessionClass);
final Class<?> returningWorkClass
= Class.forName("org.hibernate.jdbc.ReturningWork");
final Method executeMethod
= returningWorkClass.getMethod("execute", Connection.class);
final Object workProxy = Proxy.newProxyInstance(
lookup().lookupClass().getClassLoader(),
new Class[]{returningWorkClass},
(proxy, method, args) -> {
if (method.equals(executeMethod)) {
final Connection connection = (Connection) args[0];
return function.apply(connection);
}
return null;
});
final Method doReturningWorkMethod = sessionClass.getMethod(
"doReturningWork", returningWorkClass);
return (R) doReturningWorkMethod.invoke(session, workProxy);
} catch (final ReflectiveOperationException roe) {
logger.log(Level.FINE, roe, () -> "failed to work with hibernate");
}
OpenJPA
Runtime Access to DataSource
OPENJPA-1803 Unwrap EntityManager to Connection
I'm not sure OpenJPA already serves a way using unwrap(Connection.class) but can be done with the way described in one of above links.
It's not clear the responsibility of closing the connection. The document (one of above links) seems saying clearly but I'm not good at English.
try {
final Class<?> k = Class.forName(
"org.apache.openjpa.persistence.OpenJPAEntityManager");
if (k.isInstance(manager)) {
final Method m = k.getMethod("getConnection");
try {
try (Connection c = (Connection) m.invoke(manager)) {
return function.apply(c);
}
} catch (final SQLException sqle) {
logger.log(FINE, sqle, () -> "failed to work with openjpa");
}
}
} catch (final ReflectiveOperationException roe) {
logger.log(Level.FINE, roe, () -> "failed to work with openjpa");
}
Hibernate uses a ConnectionProvider internally to obtain connections. From the hibernate javadoc:
The ConnectionProvider interface is not intended to be exposed to the application. Instead it is used internally by Hibernate to obtain connections.
The more elegant way of solving this would be to create a database connection pool yourself and hand connections to hibernate and your legacy tool from there.
I ran into this problem today and this was the trick I did, which worked for me:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("DAOMANAGER");
EntityManagerem = emf.createEntityManager();
org.hibernate.Session session = ((EntityManagerImpl) em).getSession();
java.sql.Connection connectionObj = session.connection();
Though not the best way but does the job.
Below is the code that worked for me. We use jpa 1.0, Apache openjpa implementation.
import java.sql.Connection;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAPersistence;
public final class MsSqlDaoFactory {
public static final Connection getConnection(final EntityManager entityManager) {
OpenJPAEntityManager openJPAEntityManager = OpenJPAPersistence.cast(entityManager);
Connection connection = (Connection) openJPAEntityManager.getConnection();
return connection;
}
}
I'm using a old version of Hibernate (3.3.0) with a newest version of OpenEJB (4.6.0). My solution was:
EntityManagerImpl entityManager = (EntityManagerImpl)em.getDelegate();
Session session = entityManager.getSession();
Connection connection = session.connection();
Statement statement = null;
try {
statement = connection.createStatement();
statement.execute(sql);
connection.commit();
} catch (SQLException e) {
throw new RuntimeException(e);
}
I had an error after that:
Commit can not be set while enrolled in a transaction
Because this code above was inside a EJB Controller (you can't commit inside a transaction). I annotated the method with #TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED) and the problem was gone.
Here is a code snippet that works with Hibernate 4 based on Dominik's answer
Connection getConnection() {
Session session = entityManager.unwrap(Session.class);
MyWork myWork = new MyWork();
session.doWork(myWork);
return myWork.getConnection();
}
private static class MyWork implements Work {
Connection conn;
#Override
public void execute(Connection arg0) throws SQLException {
this.conn = arg0;
}
Connection getConnection() {
return conn;
}
}
I am a little bit new to Spring Boot, I have needing the Connection object to send it to Jasperreport also, after trying the different answers in this post, this was only useful for me and, I hope it helps someone who is stuck at this point.
#Repository
public class GenericRepository {
private final EntityManager entityManager;
#Autowired
public GenericRepository(EntityManager entityManager, DataSource dataSource) {
this.entityManager = entityManager;
}
public Connection getConnection() throws SQLException {
Map<String, Object> properties = entityManager.getEntityManagerFactory().getProperties();
HikariDataSource dataSource = (HikariDataSource) properties.get("javax.persistence.nonJtaDataSource");
return dataSource.getConnection();
}
}