For a Liferay 6.2 custom portlet accessing a non liferay Oracle database we are running into an issue where the data source returned is null.
We have configured the tomcat/conf/context.xml
<!-- Adding custom New non liferay datasource -->
<Resource name="jdbc/NewPool"
auth="Container"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:#(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = dbservernameorip)(PORT = 9999))
(CONNECT_DATA = (SERVER = DEDICATED)
(SERVICE_NAME = dbSIDorservicename)))"
username="user"
password="pwd"
maxActive="35"
maxIdle="10"
maxWait="20000"
removeAbandoned="true"
logAbandoned="true"
minEvictableIdleTimeMillis="3600000"
timeBetweenEvictionRunsMillis="1800000"
testOnBorrow="true"
testOnReturn="false"
/>
The portlet web.xml contains:
<resource-ref>
<description>Oracle Datasource example</description>
<res-ref-name>jdbc/NewPool</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
The code for lookup is:
String JNDI = "jdbc/NewPool"
_log.debug("JNDI Name is: " + JNDI);
_log.debug("dataSource in dbConnect is :" + dataSource);
Context context = new InitialContext();
Context envContext = (Context)context.lookup("java:/comp/env");
_log.debug("envContext in dbConnect is :" + envContext);
try {
DataSource ds = (DataSource)envContext.lookup(JNDI);
Liferay can use the context.xml resource with a similar data source for the Liferay Oracle database successfully.
Is some other wiring needed for Liferay portlet to establish a connection to another database?
The same portlet code works on weblogic without the web.xml change. Similar JNDI data source lookup code and configuration works on vanilla tomcat (without liferay) and a plain war (non liferay portlet) file.
Update:
I have checked db connections on the server with netstat -an|grep dbport. this does not show an established connection.
I have also tried setting the portal.security.manager.strategy=none in portal-ext.properties. This did not work either.
Any insight is much appreciated as we are kind of stuck here.
Thanks!
I just stumbled over this thread in the Liferay Forum which basically says. oput this in your tomcat/conf/server.xml
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
<Resource name="jdbc/XXX" auth="Container" type="javax.sql.DataSource"
maxActive="20" maxIdle="10" maxWait="5000"
removeAbandoned="true" removeAbandonedTimeout="250" validationQuery="SELECT 1"
username="user2" password="pwd2"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost/myOtherDb"/>
and this in your context.xml:
<ResourceLink name="jdbc/XXX" global="jdbc/XXX" type="javax.sql.DataSource">
It should do the trick. If you really asking WHY Liferay can find the jndi resource, but not your portlet:
I have no clue...
We also had some problems using the resource and pools. Since we have very few requests to handle and performance were not a concern for our scenario we used a JDBC connection directly without a pool and it's working fine (we are connecting to a MS Sql server)
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
String url = "jdbc:jtds:sqlserver://host/dbname";
String driver = "net.sourceforge.jtds.jdbc.Driver";
String db_userName = PropsUtil.get("jdbc.default.username");
String db_password = PropsUtil.get("jdbc.default.password");
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, db_userName, db_password);
String sql = "SELECT * FROM Users";
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
while(rs.next()){
// DO WHAT YOU WANT
return true;
}
rs.close();
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
}finally{
//finally block used to close resources
try{
if(stmt!=null)
conn.close();
}catch(SQLException se){
}// do nothing
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}//end finally try
}//end try
And it's working fine. Username and password are configured in portal-ext.properties (We are using the same account used for liferay own db)
Hope this helps
I believe we found our issue. It seems like it was a typo.
All references to dataSource need to be changed to ds. There was a code changed. It turned out the the variable name was not changed to ds in code after the ds variable was declared during troubleshooting the issue.
String JNDI = "jdbc/NewPool"
_log.debug("JNDI Name is: " + JNDI);
_log.debug("dataSource in dbConnect is :" + ds);
Context context = new InitialContext();
Context envContext = (Context)context.lookup("java:/comp/env");
_log.debug("envContext in dbConnect is :" + envContext);
try {
DataSource ds = (DataSource)envContext.lookup(JNDI);
_log.debug("dataSource in dbConnect is :" + ds)
We need to test this out. I will post the results after the final test.
Related
Good day, respective all!
My environment:
Tomcat 8.5 under windows 64-bit
All needed jars are placed into $CATALINA_HOME/lib
It is my first attempt to write servlet using ConnectionPool.
After "googling"I made an entry inside $CATALINA_HOME/conf/server.xml:
<context docbase="msgsend" path="/msgsend" reloadable="true">
<context docbase="ssr" path="/ssr" reloadable="true">
<Resource
name="jdbc/OrServlet"
auth="Container"
type="javax.sql.DataSource"
user="STERN"
username="STERN"
password="pwdxxx"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:STERN/pwdxxx#XEPDB1"
/>
</context>
I included following into WEb-INF/web.xml:
<resource-ref>
<description>just a test</description>
<res-ref-name>jdbc/OrServlet</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
When I try to connect to Oracle in traditional way:
try {
cn = DriverManager.getConnection(
"jdbc:oracle:thin:#XEPDB1", "STERN", "pwdxx");
}
catch (Exception e) { }
everything works fine.
But when I try to connect through context:
Connection cn = null;
DataSource ds = null;
try
{
Context initCtx = new InitialContext();
Context envCtx = (Context)initCtx.lookup("java:comp/env");
ds = (DataSource)envCtx.lookup("jdbc/OrServlet");
cn = ds.getConnection();
}
catch(NamingException n) {}
catch(SQLException s) {out.println(ds.getClass().getName()+" exception "+s);
;}
I get :
**exception java.sql.SQLException: Cannot create JDBC driver of class '' for connect URL 'null'**
and ds.getClass().getName() in exception handler returns:
org.apache.tomcat.dbcp.dbcp2.BasicDataSource
which is distinct from javax.sql.DataSource as declared in
and .
And it seems to me that ds = (DataSource)envCtx.lookup("jdbc/OrServlet")
doesn't wvwn try to look inside Resource section.
What I missed?
Any help will be very appreciated.
Regards,
Andrew.
In Spring,how to use context paramters in the tomcat location conf/context.xml in the junit testing or how to create context parameters in junit
<Context>
<Parameter name="mail_host" value="smtp.gmail.com" override="true"/>
</Context>
I am implementing the above parameters in my code as i specified below
#Value("#{contextParameters.mail_host}").It is working fine when i am using tomcat.
But i am getting an error 'Field or property 'contextParameters' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext'
when running in junit
Please help me,I am struggling in this point.Thanks in advance
You could construct JNDI before you run your test and in your test you can retrieve the information from context. Take a look a this example how he simulates injecting datasource using jndi here.
And your context would look something like this(in actual tomcat)
<Resource name="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource"
username="root"
password="password"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mysql"/>
So now we want to simulate this as if we would retrieve it using JNDI.
So in JUnit test cast we would like to setup Before class.Please note that not all information is shown here(constructing the datasource in #Before), but you got the point.
#BeforeClass
public static void setUpClass() throws Exception {
// rcarver - setup the jndi context and the datasource
try {
// Create initial context
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES,
"org.apache.naming");
InitialContext ic = new InitialContext();
ic.createSubcontext("java:");
ic.createSubcontext("java:/comp");
ic.createSubcontext("java:/comp/env");
ic.createSubcontext("java:/comp/env/jdbc");
// Construct DataSource
OracleConnectionPoolDataSource ds = new OracleConnectionPoolDataSource();
ds.setURL("jdbc:oracle:thin:#host:port:db");
ds.setUser("MY_USER_NAME");
ds.setPassword("MY_USER_PASSWORD");
ic.bind("java:/comp/env/jdbc/nameofmyjdbcresource", ds);
} catch (NamingException ex) {
Logger.getLogger(MyDAOTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
and in your test you can get this information
Context initContext = new InitialContext();
Context webContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource) webContext.lookup("jdbc/nameofmyjdbcresource");
Hope it helps.
EDIT
e.g.
MyMailHost mailHost = new MyMailHost();
mailHost.setName("mail_host");
mailHost.setValue("smtp.gmail.com");
mailHost.setOverride(true);
ic.bind("java:/comp/env/jdbc/mymailhost", mailHost);
in #Test
Context initContext = new InitialContext();
Context webContext = (Context)initContext.lookup("java:/comp/env");
MyMailHost ds = (MyMailHost) webContext.lookup("jdbc/mymailhost");
One solution that I've used is to stub the context parameters in my test spring applicationContext file with a simple properties bean.
So in your case:
<util:properties id="contextParameters">
<prop key="mail_host">smtp.gmail.com</prop>
</util:properties>
This is the easiest way to handle it.
Hope it helps!
Been pulling my hair trying to set up a data source in in Tomcat for an application. The steps I've taken are
Create a META-INF/context.xml with the following content
<Context>
<Resource name="jdbc/mydb"
auth="Container"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:#gpqa6.myserver.com:1526:gpqa6"
username="foo"
password="foobar"
maxActive="20"
maxIdle="30"
maxWait="-1" />
</Context>
Enter the following text in WEB-INF/web.xml
<resource-ref>
<description>Datasource</description>
<res-ref-name>jdbc/mydb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Using it in the code
public class MyServlet extends HttpServlet {
private DataSource ds;
public void init() {
try {
Context ctx = new InitialContext();
ds = (DataSource) ctx.lookup("java:comp/env/jdbc/mydb");
} catch (NamingException e) {
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//this is where the exception is thrown
Connection conn = ds.getConnection();
//...do stuff with the connection
}
But keep on getting the following error
"Encounter exception during mapping. Error: Cannot create PoolableConnectionFactory (ORA-01017: invalid username/password; logon denied
)
org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (ORA-01017: invalid username/password; logon denied"
I know that the username and password are correct. Because the following code works
Class.forName("oracle.jdbc.OracleDriver").newInstance();
conn = DriverManager.getConnection(
"jdbc:oracle:thin:#gpqa6.myserver.com:1526:gpqa6", "foo", "foobar");
I've even tried to enter an invalid url but still get the same error. What's going on???
Also, does Tomcat have some way of testing the data source similar to WebLogic or Glassfish?
Now it's working. Seem like after I establish a connection with the following code
Class.forName("oracle.jdbc.OracleDriver").newInstance();
conn = DriverManager.getConnection(
"jdbc:oracle:thin:#gpqa6.myserver.com:1526:gpqa6", "foo", "foobar");
then switching back to using the data source, it's fine. Maybe it's a caching issue?
--Update--
It is a caching issue. Ijust have to delete the \conf\Catalina folder.
This link really helped.
http://pwu-developer.blogspot.com/2010/02/why-isnt-my-datasource-configuration.html
This page on Spring JDBC says
The DataSourceUtils class … provides static methods to obtain connections from JNDI
However the API doc for DataSourceUtils does not include the said static methods, as far as I can see.
What am I missing?
As I understand, what would be really useful to you is JndiObjectFactoryBean. This Spring factory bean returns object published in JNDI.
You would configure it like this, and then you would get the Connection using DataSourceUtils on the injected DataSource:
<bean name="myDataSourceInJndi" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/MyDataSource</value>
</property>
</bean>
<bean name="myBean" class="MyClass">
...
<property name="dataSource" ref="myDataSourceInJndi">
...
</bean>
Hmm... Somehow, the Javadoc of DataSourceUtils is more "accurate" (I mean, the doc is not wrong but technically, you obtain a connection from a DataSource - that can be obtained via JNDI):
Helper class that provides static methods for obtaining JDBC Connections from a DataSource.
And the following methods should be what you're looking for:
DataSourceUtils.getConnection(DataSource)
DataSourceUtils.getGetConnection(DataSource)
Basic example (from the MySQL documentation):
// Create a new application context. this processes the Spring config
ApplicationContext ctx = new ClassPathXmlApplicationContext("ex1appContext.xml");
// Retrieve the data source from the application context
DataSource ds = (DataSource) ctx.getBean("dataSource");
// Open a database connection using Spring's DataSourceUtils
Connection c = DataSourceUtils.getConnection(ds);
try {
// retrieve a list of three random cities
PreparedStatement ps = c.prepareStatement(
"select City.Name as 'City', Country.Name as 'Country' " +
"from City inner join Country on City.CountryCode = Country.Code " +
"order by rand() limit 3");
ResultSet rs = ps.executeQuery();
while(rs.next()) {
String city = rs.getString("City");
String country = rs.getString("Country");
System.out.printf("The city %s is in %s%n", city, country);
}
} catch (SQLException ex) {
// something has failed and we print a stack trace to analyse the error
ex.printStackTrace();
// ignore failure closing connection
try { c.close(); } catch (SQLException e) { }
} finally {
// properly release our connection
DataSourceUtils.releaseConnection(c, ds);
}
For further future references: the jndi is managed on the application server i.e. : standalone.xml (Wildfly) with Postgresql driver:
<datasource jta="true" jndi-name="java:comp/env/jdbc/MyDataSource" pool-name="myDS" enabled="true">
<connection-url>jdbc:postgresql:[<//host>[:<5432>/]]<database></connection-url>
<driver>postgresql</driver>
<security>
<user-name>$USER</user-name>
<password>$PWD</password>
</security>
</datasource>
I'd like to know how to modify the server.xml file so all my webservices built on axis2 can talk to the DB using Connection Pooling. Each webservice has a different data source, one points to one instance of the DB and the other to another DB server. How do I specify the context that should be used by each service?
Thanks in advance,
Pojo
If you want to use connection pool in your project , Ensure that you have the following code set up for the Tomcat connection pooling to work in context.xml file:
1)Create file with name "context.xml" if it's not exist under directory "WebContent/META-INF/context.xml" with the following content:
For My Project , Please modify it with appropriate value :
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/dbcp" docBase="dbcp">
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
removeAbandoned="true" removeAbandonedTimeout="30" maxActive="80"
maxIdle="30" maxWait="10000" username="sontn" password="nhantien"
driverClassName="org.postgresql.Driver"
url = "jdbc:postgresql://localhost/group8" useUnicode="true"
characterEncoding="utf-8" characterSetResults="utf8"/>
</Context>
Or you can copy file : context.xml into directory "$Catalian\webapps\axis2\META-INF"
How can you get connection pool?
In your webservice method : create method getConnection() with following content:
public Connection getConnection() {
Connection connection = null;
try {
Context envCtx = (Context) new InitialContext().lookup("java:comp/env");
DataSource ds = (DataSource) envCtx.lookup("jdbc/TestDB");
connection = ds.getConnection();
}
catch (Exception e) {
System.out.println("Connection error: " + e.getMessage());
}
return connection;
}
Thanks