Tomee dynamic Resources/Datasources - java

After searching for a while, I'm exhausted and hope you can help me. I've been trying to write a Java EE application that comes with a datasource configurator (like WordPress at first startup). The datasources are not known on deploy time. With the router example of TomEE I found a method to dynamically replace datasources at application lifetime. I'm currently generating my datasources with the following code:
GeronimoTransactionManager transactionManager = (GeronimoTransactionManager) OpenEJB.getTransactionManager();
Properties props = new Properties();
props.setProperty("driverClassName", "com.mysql.cj.jdbc.Driver");
props.setProperty("url", "jdbc:mysql://localhost/test");
props.setProperty("password", "test");
props.setProperty("passwordCipher", "PlainText");
props.setProperty("username", "test");
try {
DataSource underlying = new org.apache.tomcat.jdbc.pool.DataSourceFactory().createDataSource(props);
dataSource = new ManagedDataSource(underlying, transactionManager, transactionManager);
} catch (Exception e) {
e.printStackTrace();
}
My problem is that at this point no ddl-generation is executed, so that entitymanager.persist(object); throws an exception that the table was not generated.
The following code produces an java.lang.AbstractMethodError:
Map properties = new HashMap<>();
StringWriter create = new StringWriter();
properties.put("javax.persistence.schema-generation.scripts.action", "create");
properties.put("javax.persistence.schema-generation.scripts.create-target", create);
Persistence.generateSchema("router", properties);
Is there a not "hacky" Java EE way to dynamically load resources, especially datasources. If not, is there a TomEE way? Thanks in advance.

Related

JNDI configuration for Springboot standalone JAR application

I created with Springboot a JAR application.
I launch this JAR application under Tomcat server.
One asks me to use JNDI to retrieve datasource stored in the Tomcat context.xml, saying it's easy.
I try to do this with this following code.
Context initContext;
try {
initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:comp/env");
DataSource ds = (DataSource) envContext.lookup("jdbc/mydatabase");
} catch (NamingException e) {
System.out.println("ERROR!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
e.printStackTrace();
System.out.println("ERROR!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
But it does not work.
I obtain this error :
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
Is it a simple mean to overcome this problem ?
Or with my JAR standalone application, it is still absolutely impossible to overcome this problem, like the precedent post Tomcat JNDI + Standalone Java informs ?
I see many posts, many docs but it remains unclear for me.
Thanks for your help, your sharing.
Thomas
Kedar brings me an answer.
I make a summary of the Kedar answer :
when running your JAR application with the jar command line, i am using embedded Tomcat
when i use embedded Tomcat, it's possible that i have not access to my JNDI datasource
That's the reason why my JNDI does not work.
I will try to see this post to configure my application in order to try tu use JNDI :
How to create JNDI context in Spring Boot with Embedded Tomcat Container
But, now, thanks to Kedar, i understand why my JNDI code does not work.
I will see the other post to see how configure to use anyway JNDI if possible.
Thanks Kedar.
Sorry for the delay to give the answer.
Thanks to Kedar that indicates me that JNDI does not work with a JAR, i try to :
read on the Tomcat server the context.xml file
extract from this context.xml file the XML element containing all the properties concerning the database
construct a DataSource object from this database properties
Thanks also to MKyong with this tutorial :
https://mkyong.com/java/how-to-read-xml-file-in-java-dom-parser/
Here is my working code under SpringBoot :
#Configuration
public class ApplicationConfiguration {
/**
* Absolute path of the configuration file named context.xml on any Tomcat Server (DEV, TEST, PROD).
*/
private final String CONTEXT_XML_PATH_ON_SERVER = "/opt/tomcat/apache-tomcat-version/conf/context.xml";
#Bean
#Primary
public DataSource dataSource() {
try {
Element datasourceElement = getDatasourceElementInTomcatContextXml();
return DataSourceBuilder
.create()
.username(datasourceElement.getAttribute("username"))
.password(datasourceElement.getAttribute("password"))
.url(datasourceElement.getAttribute("url"))
.driverClassName(datasourceElement.getAttribute("driverClassName"))
.build();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException | IOException e) {
e.printStackTrace();
}
return null;
}
private Element getDatasourceElementInTomcatContextXml() throws ParserConfigurationException, SAXException, IOException {
File contextXmlFile = new File(CONTEXT_XML_PATH_ON_SERVER);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(contextXmlFile);
//optional, but recommended
//read this - http://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-java-how-does-it-work
doc.getDocumentElement().normalize();
NodeList resourceElementList = doc.getElementsByTagName("Resource");
Node nNode = resourceElementList.item(0);
Element eElement = (Element) nNode;
return eElement;
}
}
Thanks Kedar and MKyong.
Thomas

Create SqlSession from existing Connection directly

I'm working with existing Java code wherein there is an existing JDBC connection pooling mechanism on deployed systems and an already existing setup to get JDBC connections. I'd like to leverage this to create a MyBatis SqlSession object without creating a Configuration, DataSource, and other things
I have code that already creates a java.sql.Connection object is given the desired resource. I'd like to leverage this and get that SqlSession object and use MyBatis from that point onwards.
I don't want MyBatis to manage connection pooling, determining which data source to use, etc. Is this possible?
I'm afraid you can't avoid creation of the Configuration object. It is used by the internal mybatis machinery like executors. But even if you could it will not help you much. In this case you will need to implement most of Configuration functionality yourself so it does not make sense to do that.
You main goal is to be able to use SqlSessionFactory.openSession(Connection connection) method. To do this you need to have SqlSessionFactory. The easiest way for you is to create Configuration like it is descried in mybatis documentation:
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
// set properties to configuration here
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configuration);
Now if your connection pool does implement DataSource you use it directly to create environment. If it does not you need to create an adapter around your pool which implements javax.sql.DataSource.
My solution is similar to Roman's above, but I needed to create an Oracle datasource. For various reasons, the connection needs to be created using the Class.forName type sequence
Class.forName("oracle.jdbc.driver.OracleDriver");
String connectionString = "jdbc:oracle:thin:#//yadayada";
String username = "myusername";
String password = "mypassword";
OracleDataSource oracleDataSource = new OracleDataSource();
oracleDataSource.setURL(connectionString);
oracleDataSource.setPassword(password);
oracleDataSource.setUser(username);
environment = new Environment("dev",transactionFactory,oracleDataSource);
configuration = new Configuration(environment);
configuration.addMappers("MyMybatisMapper");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
return sqlSessionFactory.openSession();
What I was missing was the OracleDataSource object.

How to provide the entity package to the HIbernate 5 with Map approach?

I am new to hibernate and trying to learn it with Hibernate 5 and Spring Boot Application.
I am using the Map approach to set the data-source related properties as shown below:
if(null == standardServiceRegistry) {
//the following map is an alternative to the hibernate.cfg.xml file
Map<String, String> dbSettings = new HashMap<>();
dbSettings.put(Environment.URL, "jdbc:postgresql://localhost:5432/saurabh");
dbSettings.put(Environment.USER, "postgres");
dbSettings.put(Environment.PASS, "root");
dbSettings.put(Environment.DRIVER, "org.postgresql.Driver");
dbSettings.put(Environment.SHOW_SQL, "true");
dbSettings.put(Environment.HBM2DDL_AUTO, "create");
dbSettings.put(Environment.DIALECT, "org.hibernate.dialect.PostgreSQL94Dialect");
dbSettings.put(Environment.NON_CONTEXTUAL_LOB_CREATION, "true");
// the rest of the code is same as with the xml file
standardServiceRegistry = new StandardServiceRegistryBuilder().applySettings(dbSettings).build();
MetadataSources metaDataSources = new MetadataSources(standardServiceRegistry);
Metadata metaData = metaDataSources.getMetadataBuilder().build();
sessionFactory = metaData.getSessionFactoryBuilder().build();
}
This works fine as far as sessionfactory is considered. I tried running a Select version() query and it works fine.
Now, I have my entities package com.saurabh.entities in a package parallel to the package containing above-mentioned configuration.
With this setup hibernate cannot manage to get my Entities and is not creating the tables by default.
However, if I switch to hibernate.cfg.xml approach, and provide following tag in xml file, it works fine.
<mapping class ="com.saurabh.entities.Employee" />
Can someone explain what's the best way of assigning the entire entity package(s) to the Hibernate at once.
Note: I have already tried assigning #EntityScan({basePackage="com.saurabh.entities"}) at Spring .run() file and HibernateConfig file (where I am getting session factory as shown above).
MetadataSources has the right method.
MetadataSources metaDataSources = new MetadataSources(standardServiceRegistry);
metaDataSources.addPackage("com.saurabh.entities");
give it a try.

javax.naming.NoInitialContextException with QueryDSL

I am trying to write a simple query using QueryDSL, but my attempt fails with the following exception.
Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
I tried executing query the following ways.
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
JPQLQuery query = new HibernateQuery(session);
QClient t = QClient.client;
List<Client> lst = query.from(t).list(t);
System.out.println(lst.size());
And another way.
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("my.package.entities");
EntityManager em = emf.createEntityManager();
JPAQuery query = new JPAQuery(em);
QClient t = QClient.client;
List<Client> lst = query.from(t).list(t);
System.out.println(lst.size());
As stated, both this ways failed with the same exception.
I am using Postrges DB, and the parameters are specified in hibernate.cfg.xml.
Do I need to setup something more for this to work?
The javax.naming package comprises the JNDI API. Since it's just an API, rather than an implementation, you need to tell it which implementation of JNDI to use. The implementations are typically specific to the server you're trying to talk to.
To specify an implementation, you pass in a Properties object when you construct the InitialContext. These properties specify the implementation to use, as well as the location of the server. The default InitialContext constructor is only useful when there are system properties present, but the properties are the same as if you passed them in manually.
As to which properties you need to set, that depends on your server. You need to hunt those settings down and plug them in.
Sample:
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
properties.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
properties.put("jnp.socket.Factory", "org.jnp.interfaces.TimedSocketFactory");
properties.setProperty("java.naming.provider.url", "jnp://" + remoteHost + :1099");
context = new InitialContext(properties);
or
look for this
Issue

Not able to get session factory in hibernate

try
{
System.out.println("openTxCoreSession() start...");
TxCoreSessionFactory sessionFactory =
TxCoreSessionFactory.getInstance("txcore.cfg.xml");
System.out.println("Session factory created....");
Session session = sessionFactory.openSession();
System.out.println("session created");
return session;
}
catch (Exception e)
{
System.out.println(e.getMessage());
e.printStackTrace();
return null;
}
Hibernate is not easy to start off with, does take a bit of time/effort.
Found one blog to get started with Hibernate, http://www.myeclipseide.com/documentation/quickstarts/hibernateintroduction/. But MyEclipse is a priced product. So personally I had to skip it for now.
I researched more and got Hibernator. Tried Hibernator which is a eclipse plugin to make Hibernate integration super easy (seemed too good to be true), but turned out to be a disaster. Gave it up. Also noticed that the code was as old as 2006, no one working on it now.
Then moved to Hibernate Tools from JBoss Hibernate Tools – http://download.jboss.org/jbosstools/updates/JBossTools-3.1.0.GA worked like a charm.
For people who are confused, MyEclipse or JBoss Hibernate Tools, its no different.
The main reason to use Hibernate on a server platform is to get rid of the complex JDBC hell hole. The only reason you thought you needed a object relational mapping solution was to get some neatness in code and good old re-usability built into the design.
Also the below works for me.
if (sessionFactory == null) {
try {
String jdbcProperty = "jdbc:mysql://"+Globals.DBSERVER+"/MyDB" ;
Configuration configuration = new Configuration().configure() ;
sessionFactory = configuration.buildSessionFactory(new ServiceRegistryBuilder()
.buildServiceRegistry());
} catch (Exception e) {
log.fatal("Unable to create SessionFactory for Hibernate");
log.fatal(e.getMessage());
log.fatal(e);
e.printStackTrace();
}
}
Hibernate.properties in the src folder of my eclipse project.
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql://localhost/MyDB
hibernate.connection.username=MYROOT
hibernate.connection.password=myPASSWORD
hibernate.connection.pool_size=2
hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect
Also ensure that your configuration xml file (txcore.cfg.xml) is in the classpath of your application.

Categories