I build webApp . When I run app from Eclipse everything work OK. Now I build WAR file, put in tomcat root, andI want to all my system property put in context.xml in tomcat. Then, when app is start, that values from context.xml is read and use in app.
Problem is I don't know how to get values from context.xml in my webapp?
This is context:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="" docBase="fileupload.war" privileged="true" antiResourceLocking="false" antiJARLocking="false">
<Resource
mail.smtp_server = "127.0.0.1"
mail.smtp_username = "no-reply#gmail.rs"
mail.mailTo = "test#gmail.com"
rootFolder = "D:/project/"
rootFolderBackup = "D:/project/Backup/"
/>
Here I want to get a values from context.xml :
#RequestMapping(value="api/files")
public class FileController {
final static Logger logger = Logger.getLogger(FileController.class);
#Autowired
ApplicationContext applicationContext;
private String mailTo;
private String sender;
private String rootFolder = "";
private String rootFolderBackup = "";
#PostConstruct
private void init(){
try {
InitialContext ic = new InitialContext();
Context xmlContext = (Context) ic.lookup("java:comp/env"); // thats everything from the context.xml and from the global configuration
DataSource myDatasource = (DataSource) xmlContext.lookup("rootFolder");
} catch (NamingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
This is common error :
Name [rootFolder] is not bound in this Context. Unable to find [rootFolder].
I try few solution which I found on web but not working...
My context.xml file is in C:\tomcat-7.0\conf\Catalina\localhost , and war file is in C:\tomcat-7.0\webapps
If you want to define application properties you should probably use Environment variables instead. Ex you can set a variable in any context.xml like this:
<Environment name="PROPERTIES_FILE" override="false" type="java.lang.String" value="C:/path/to/propfile.properties" />
<!--define more variables here -->
and then in your application you can read those environment properties from JNDI like this.
initCtx = new InitialContext();
String path = (String) initCtx.lookup("java:comp/env/PROPERTIES_FILE");
// fetch more variables here
Further reading:
https://tomcat.apache.org/tomcat-8.0-doc/config/context.html#Environment_Entries
https://tomcat.apache.org/tomcat-8.0-doc/jndi-resources-howto.html
Related
I have a java project that implements some APIs, in Eclipse.
I have db.java file that enables the communication with the MySQL database.
Instead of have the MySQL credentials in a java file, I would like to have them in /META-INF/context.xml file.
Do you know how to do this?
This is my current code:
public class db {
private String userName = null;
private String password = null;
private String dbName = null;
private String db_connect_string = null;
public db() {
this.db_connect_string = "jdbc:mysql://localhost/mydb";
this.dbName = "name";
this.userName = "uname";
this.password = "pass";
}
protected Connection getDBMySQLCon() {
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
return DriverManager.getConnection(this.db_connect_string+"?useSSL=false", this.userName, this.password);
} catch (ClassNotFoundException | SQLException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
Instead of an XML file, you can have a properties file, that has the required information. Problem with XML file is that you will have to choose an XML parser and work with it.
If you want to go ahead with properties file you can consider the following snippet.
public void setProp() throws Exception{
FileReader reader=new FileReader("db.properties");
Properties p=new Properties();
p.load(reader);
// you can get values you want as properties using
this.db_connect_string = p.getProperty("db_connect_string");
this.dbName = p.getProperty("dbName");
}
And your file structure should be something like
db_connect_string=connection.string
dbName=name
userName=uname
password=pass
This is part of the environment of the container.
/META-INF/context.xml
The context.xml overides the tomcate context entry.
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- Specify a JDBC datasource -->
<Resource name="jdbc/mydatabase"
auth="Container"
type="javax.sql.DataSource"
username="YOUR_USERNAME"
password="YOUR_PASSWORD"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://mysql.metawerx.net:3306/YOUR_DATABASE_NAME?
autoReconnect=true"
validationQuery="select 1"
maxActive="10"
maxIdle="4"/>
</Context>
// Get DataSource
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/mydatabase");
// Get Connection and Statement
Connection c = ds.getConnection();
Statement s = c.createStatement();
For school they're making us connect to a postgresDB trough plain old dao's and tomcat. However the given code ain't working and I've been stuck here for quite a bit now.
So here goes.
The connectiondao given:
package nl.hu.v1wac.firstapp.persistence;
import java.sql.Connection;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class BaseDao {
protected final Connection getConnection() {
Connection result = null;
try {
InitialContext ic = new InitialContext();
DataSource ds = (DataSource)ic.lookup("java:comp/env/jdbc/PostgresDS");
result = ds.getConnection();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return result;
}
}
We had to write the server specifics into an context.xml file, and import the jar driver into the lib folder of tomcat (so far so good). The context.xml is in the src/main/webapp/META-INF directory
The context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="jdbc/PostgresDS"
url="jdbc:postgresql://localhost:5432/worlddb"
driverClassName="org.postgresql.Driver"
auth="Container"
type="javax.sql.DataSource"
username="postgres"
password="secret" />
</Context>
After setting up my Dao's, I try to fire them up in a main and get the following error:
Exception in thread "main" java.lang.RuntimeException: 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
which is said caused by:
DataSource ds = (DataSource)ic.lookup("java:comp/env/jdbc/PostgresDS");
Would anyone be able to help, as according the slides / manual this should be all to it :/
Thanks in advance!
p.s. we're using tomcat 8.5 and Eclipse Jee Neon
Edited Main class
package nl.hu.v1wac.firstapp.persistence;
import java.sql.SQLException;
public class Pattern {
public static void main(String[] args) {
// TODO Auto-generated method stub
CountryDao cdao = new CountryDaoPostgreSQL();
try {
cdao.findALl();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
You certainly shouldn't be getting that.
You're running this code within Tomcat proper? Not outside? The complaint is that the InitialContext doesn't have the underlying "plumbing" to do the lookup. Normally this is all managed by the environment that you're running in, unless you're running it "stand alone" in a Java SE app, you should never see this.
I have an application that i use for buiding reports, now I need to move this application to Web environment.
For that I am using Tomcat 8.0.15 and an Oracle Database 11g Enterprise Edition.
In my TOMCAT_HOME\conf\server.xml i have the following code:
<Resource auth="Container"
driverClassName="oracle.jdbc.OracleDriver"
maxIdle="10"
maxTotal="20"
maxWaitMillis="-1"
name="jdbc/reportDataSource"
username="some_username"
password="some_pass"
type="javax.sql.DataSource"
url="jdbc:oracle:thin:#(DESCRIPTION =(ADDRESS_LIST =(ADDRESS = (PROTOCOL = TCP)(HOST = some.host)(PORT = some.port)))(CONNECT_DATA =(SID = SOME_SID)(SERVICE_NAME = SOME_SERVICE)))"/>
Therefore in my PROJECT_HOME\WebContent\WEB-INF\web.xml I have the following:
<resource-ref>
<description>Oracle Datasource definition</description>
<res-ref-name>jdbc/reportDataSource</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Finally in my code I have a Java class with the following:
private void init() throws NamingException, SQLException {
try {
InitialContext initialContext = new InitialContext(); // JNDI initial context
Context eventContext = (Context) initialContext.lookup("java:comp/env/jdbc/reportDataSource"); // Event context
dataSource = (DataSource) eventContext.lookup("jdbc/reportDataSource"); // JNDI lookup
databaseConnection = dataSource.getConnection(); // database connection through data source
} catch (SQLException se) {
throw new SQLException("Connection object was not created. Rejected by host or not found.");
} catch (NamingException ne) {
throw new NamingException(ne.getMessage());
}
}
Finally in my project root I have have the following test setup:
#Before
public void setUp() throws Exception {
dbConnectorManager = new DatabaseConnectorManager();
assertNotNull(dbConnectorManager);
}
When I call the DatabaseConnectorManager() it calls the init() method shown in this question. However when I execute my test I got the following error related with line:
entContext eventContext = (Context) initialContext.lookup("java:comp/env/jdbc/reportDataSource"); // Event context
Hence, it is not possible to setup JNDI due the following error:
javax.naming.NamingException: 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
Can you please advise if I can create these JUnit tests for testing the connectivity, or if I can only test through a servlet?
Is there anything wrong with my configuration?
Update
I made the desiganted changes but now I get the following error:
javax.naming.NamingException: Name [jdbc/reportDataSource] is not bound in this Context. Unable to find [jdbc].
My data source now looks like the following:
InitialContext initialContext = new InitialContext(); // JNDI initial context
Context eventContext = (Context) initialContext.lookup("java:comp/env"); // Event context
dataSource = (DataSource) eventContext.lookup("jdbc/reportDataSource"); // JNDI lookup
databaseConnection = dataSource.getConnection(); // database connection through data source
'not sure this is the error, but you probably have a typo here:
Context eventContext =
(Context) initialContext.lookup("java:comp/env/jdbc/reportDataSource"); // Event context
// ^^^^^^^^^^^^^^^^^^^^^^
dataSource = (DataSource) eventContext.lookup("jdbc/reportDataSource"); // JNDI lookup
// ^^^^^^^^^^^^^^^^^^^^^^
Either you want to do a direct lookup:
dataSource =
(DataSource) initialContext.lookup("java:comp/env/jdbc/reportDataSource");
Or you want to get the context first, but in that case, you only requests java:comp/env:
Context eventContext =
(Context) initialContext.lookup("java:comp/env");
dataSource = (DataSource) eventContext.lookup("jdbc/reportDataSource");
In Server.xml, provide a name to your resource and do the lookup based on that name. Another point is, you can add a resource as a new context.xml under META-INF folder under webapps. This is done if you don't want to change your server.xml
<Resource name="tomcat/JDBCdatasource" auth="Container" ... />
Context ctx;
ctx = new InitialContext();
Context envContext = (Context) ctx.lookup("java:/comp/env");
// Look up a data source
javax.sql.DataSource ds
= (javax.sql.DataSource) envContext.lookup ("tomcat/JDBCdatasource");
Is is possible to create JNDI llookup and it's reference in standalone application means without any application server.
java:comp/env/jdbc
Regards,
Chaitu
JNDI is a service which is provided by Java platform. Refer to below link
http://www.javaworld.com/javaworld/jw-04-2002/jw-0419-jndi.html
You can use the class org.springframework.mock.jndi.SimpleNamingContextBuilder of Spring, either the dependency or the file spring-mock-1.0.2.jar. e.g.:
Setup:
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
builder.bind("jdbc/Oracle", ods);
builder.activate();
Use:
DataSource ds = InitialContext.doLookup("jdbc/Oracle");
I found a better solution courtesy of Injecting JNDI datasources
(https://web.archive.org/web/20140530014804/https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit)
NEW SOLUTION
How to Run JUnit Tests that require "java:/comp/env/jdbc/keyofsomethingegdatabase"
Add the following Jar's to the JUnit test-case's CLASSPATH:
TOMCAT_HOME/bin/tomcat-juli.jar (required by catalina.jar)
TOMCAT_HOME/lib/catalina.jar (contains the actual factory)
Create the binding that you require in the static "for-all-tests" method:
#BeforeClass
public static void setUpClass() throws Exception {
...
// Use Apache Tomcat's Directory
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
// Standard hook
InitialContext initialContext = new InitialContext();
// Create binding
initialContext.createSubcontext("java:");
initialContext.createSubcontext("java:comp");
initialContext.createSubcontext("java:comp/env");
initialContext.createSubcontext("java:comp/env/jdbc");
// Construct DataSource
OracleConnectionPoolDataSource dataSource = new OracleConnectionPoolDataSource();
dataSource.setURL("jdbc:oracle:thin:#myserver:1521:MYSID");
dataSource.setUser("username");
dataSource.setPassword("password");
initialContext.bind("java:comp/env/jdbc/mydatabase", dataSource);
...
}
Then you can create this method in your Singleton class (either lookup method works):
public Connection getConnection() throws NamingException, SQLException {
if (dataSource == null) {
Context initialContext = new InitialContext();
boolean bLooksLikeChangeDirectory = false;
if (bLooksLikeChangeDirectory) {
Context context = (Context) initialContext.lookup("java:comp/env");
dataSource = (DataSource) context.lookup("jdbc/mydatabase");
} else {
dataSource = (DataSource) initialContext.lookup("java:comp/env/jdbc/mydatabase");
}
}
Connection result = dataSource.getConnection();
return result;
}
OLD SOLUTION FOLLOWS
This is how you can use a stand-alone JNDI (file-based) for running tests involving: "new InitialContext() ... "lookup".
This also describes how to use BoneCP (Connection Pool) with JNDI.
You can use the "com.sun.jndi.fscontext.RefFSContextFactory" (fscontext.jar and providerutil.jar).
I wanted "lookup" to be runnable from inside an application server as well, so it would be useful if someone could tell me for sure if one has to use lookup("java:comp/env/jdbc/mydbnickname") instead of lookup("jdbc/mydbnickname") when running inside an application server.
The latter is preferable, because "java:comp/env" does not exist in the stand-alone RefFSContextFactory directory, so you'd have to have a System property that specifies the JNDI lookup parameter.
Overview
You can use "jdbc/mydbnickname" as the argument to "lookup" and to "rebind" (i.e. no "scheme:").
In this case, "RefFSContextFactory" uses the "default scheme", whatever that is ("file:" or "jndi:").
With the following in "jndi.properties" (on the CLASSPATH)
java.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory
java.naming.provider.url=file:///u:/workdirectory
and this in "persistence.xml" (JTA setup)
<jta-data-source>jdbc/mydbnickname</jta-data-source>
When you use:
...rebind("jdbc/mydbnickname", ...)
and
...lookup("jdbc/mydbnickname")
then the ".bindings" file (created by "rebind") is
"u:/workdirectory/.bindings"
and it looks like this (I have sorted it and tested it with just "lookup"):
#This file is used by the JNDI FSContext.
#Thu Jan 09 16:02:17 EST 2014
jdbc/mydbnickname/ClassName=com.jolbox.bonecp.BoneCPDataSource
jdbc/mydbnickname/FactoryName=com.jolbox.bonecp.BoneCPDataSource
jdbc/mydbnickname/RefAddr/0/Content=oracle.jdbc.OracleDriver
jdbc/mydbnickname/RefAddr/0/Encoding=String
jdbc/mydbnickname/RefAddr/0/Type=driverClassName
jdbc/mydbnickname/RefAddr/1/Content=jdbc\:oracle\:thin\:#myserver\:1521\:mysid
jdbc/mydbnickname/RefAddr/1/Encoding=String
jdbc/mydbnickname/RefAddr/1/Type=jdbcUrl
jdbc/mydbnickname/RefAddr/2/Content=myusername
jdbc/mydbnickname/RefAddr/2/Encoding=String
jdbc/mydbnickname/RefAddr/2/Type=username
jdbc/mydbnickname/RefAddr/3/Content=mypassword
jdbc/mydbnickname/RefAddr/3/Encoding=String
jdbc/mydbnickname/RefAddr/3/Type=password
If you use
"jndi:jdbc/mydbnickname"
instead of
"jdbc/mydbnickname",
then the file created is
u:/workdirectory/jdbc/.bindings
and it looks like this:
mydbnickname/ClassName=com.jolbox.bonecp.BoneCPDataSource
mydbnickname/FactoryName=com.jolbox.bonecp.BoneCPDataSource
mydbnickname/RefAddr/0/Content=oracle.jdbc.OracleDriver
mydbnickname/RefAddr/0/Encoding=String
mydbnickname/RefAddr/0/Type=driverClassName
mydbnickname/RefAddr/1/Content=jdbc\:oracle\:thin\:#myserver\:1521\:mysid
mydbnickname/RefAddr/1/Encoding=String
mydbnickname/RefAddr/1/Type=jdbcUrl
mydbnickname/RefAddr/2/Content=myusername
mydbnickname/RefAddr/2/Encoding=String
mydbnickname/RefAddr/2/Type=username
mydbnickname/RefAddr/3/Content=mypassword
mydbnickname/RefAddr/3/Encoding=String
mydbnickname/RefAddr/3/Type=password
Rebind (in a JUnit Test)
#BeforeClass
public static void setUpClass() throws Throwable {
final String sMyName = "setUpClass";
try {
if (Boolean.parseBoolean(System.getProperty("test.initialcontext.rebind", "true"))) {
final InitialContext initialContext = new InitialContext();
final String contextName = "jdbc/mydbnickname";
final Reference contextValue = new Reference("com.jolbox.bonecp.BoneCPDataSource", "com.jolbox.bonecp.BoneCPDataSource", null);
contextValue.add(new StringRefAddr("driverClassName", "oracle.jdbc.OracleDriver"));
contextValue.add(new StringRefAddr("jdbcUrl", "jdbc:oracle:thin:#myserver:1521:mysid"));
contextValue.add(new StringRefAddr("username", "myusername"));
contextValue.add(new StringRefAddr("password", "mypassword"));
initialContext.rebind(contextName, contextValue);
}
} catch (final Throwable exception) {
Utils.getInstance().logExceptionStack(logger, Level.ERROR, sMyName, exception);
throw exception;
}
}
Lookup (in production code)
protected Connection getConnection() throws Exception {
Connection result = null;
// "An InitialContext instance is not synchronized against concurrent access by multiple threads"
synchronized (this) {
if (context == null) {
context = new InitialContext();
}
final BoneCPDataSource connectionPool = (BoneCPDataSource) context.lookup("jdbc/mydbnickname");
result = connectionPool.getConnection();
}
return result;
}
CLASSPATH
BoneCP Connection Pool
<classpathentry kind="var" path="JAVA_LIB/bonecp-0.8.0.RELEASE.jar" sourcepath="/JAVA_LIB/bonecp-0.8.0.RELEASE-sources.jar"/>
<classpathentry kind="var" path="JAVA_LIB/slf4j-api-1.7.5.jar" sourcepath="/JAVA_LIB/slf4j-api-1.7.5-sources.jar"/>
<classpathentry kind="var" path="JAVA_LIB/guava-15.0.jar" sourcepath="/JAVA_LIB/guava-15.0-sources.jar"/>
<classpathentry kind="var" path="JAVA_LIB/slf4j-simple-1.7.5.jar" sourcepath="/JAVA_LIB/slf4j-simple-1.7.5-sources.jar"/>
Eclipse JPA (2.5.1)
<classpathentry kind="var" path="JAVA_LIB/javax.persistence-2.1.0.jar"/>
<classpathentry kind="var" path="JAVA_LIB/eclipselink-2.5.1.jar"/>
JNDI
<classpathentry kind="var" path="JAVA_LIB/fscontext.jar"/>
<classpathentry kind="var" path="JAVA_LIB/providerutil.jar"/>
I am new in Java EJB 3.0. It is possible to call a (session) bean—deployed on JBoss—from a desktop application client?
Thanks in advance.
Yes you can. Some specifics are here (references EJB2 but it the same for EJB3 when it comes to remote clients): http://www.theserverside.com/discussions/thread.tss?thread_id=9197
Paraphrased:
Hashtable env = new Hashtable();
env.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
env.put("java.naming.provider.url", "jnp://localhost:1099");
env.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
Context ctx = new InitialContext(env);
// name is whatever JNDI name you gave it
Object o = ctx.lookup("home name");
EJBHome ejbHome = (EJBHome) PortableRemoteObject.narrow(o,EJBHome.class);
// This is userID should be the one passed.
EJB ejb = ejbHome.create(..);
Yes.
public static void main(String args[]) throws Exception {
InitialContext ctx = new InitialContext();
YourService yourService = (YourService) ctx.lookup("com.example.session.YourService");
String time = yourService.getTime();
System.out.println("Time is: " + time);
}
For client configuration you must provide jndi.properties file with contents
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost
If you are looking for working examples on JBoss try download source code of Enterprise JavaBeans 3.0, Fifth Edition
Let's assume you have the following remote interface:
#Remote
public interface HelloBeanRemote {
public String sayHello();
}
And a session bean implementing it:
#Stateless
public class HelloBean implements HelloBeanRemote {
...
}
And that this EJB is correctly packaged and deployed on JBoss.
On the client side, create a jndi.properties with the following content and put it on the classpath:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099
Then use the following code to call your EJB:
Context context;
try {
context = new InitialContext();
HelloBeanRemote beanRemote = (HelloBeanRemote)context.lookup("HelloBean/remote");
beanRemote.test();
} catch (NamingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
Alternatively, if you don't want to provide a jndi.properties file, you can explicitly setup the JNDI environment in the code and create the context like this:
Properties properties = new Properties();
properties.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs","=org.jboss.naming:org.jnp.interfaces");
properties.put("java.naming.provider.url","localhost:1099");
Context context = new InitialContext(properties);
But I'd recommend using the jndi.properties for the sake of portability.
You can also expose the bean as a web service. I believe this is available as of EJB 3. It is quite nice considering you can do it with annotations. You may wish to consider using this option to decrease coupling. Here is a link to a tutorial.