Lookup datasource with custom variable in runtime - java

I have context.xml defined in META-INF as follow:
<Context path="/7Restaurant">
<Resource name="datasource"
type="javax.sql.DataSource"
auth="Container"
maxActive="10"
maxIdle="3"
maxWait="10000"
username="work"
password=""
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://127.0.0.1:5432/7restaurant"/>
</Context>
I am about to deploy my web app (Servlet, in Tomcat 7). The only problem is that, how can I change the url, username, etc. to match that with the environment the WAR package is deployed.
So, in essence, how could such a file is modified; or any other technique so that user can finely connect to the database in the environment it will be deployed on.
I am so new in web-app eclipse, tomcat, postgresql stack; so I expect my question to be wrong, if that's the case; please let me know any other way to solve this.
My context initializer in Java:
package com.restaurant.web;
import java.io.File;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URL;
import javax.naming.*;
import javax.servlet.*;
import javax.sql.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
import com.restaurant.dao.dbpostgres.DBDAO;
import com.restaurant.setup.GeneralConfigurerSetup;
import com.restaurant.web.Logger;
import sun.java2d.loops.DrawGlyphListAA.General;
public class Database implements ServletContextListener {
private void contextInitialized2(ServletContext servletContext) throws Exception
{
/*
//prepare META-INF/context.xml
DocumentBuilder docbldr = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = docbldr.newDocument();
Element context = doc.createElement("Context");
context.setAttribute("path", "/7Restaurant");
Element resource = doc.createElement("Resource");
resource.setAttribute("name", "datasource");
resource.setAttribute("type", "javax.sql.DataSource");
resource.setAttribute("auth", "Container");
resource.setAttribute("maxActive", "10");
resource.setAttribute("maxIdle", "3");
resource.setAttribute("maxWait", "10000");
resource.setAttribute("username", Configurer.get(GeneralConfigurerSetup.DB_USERNAME));
resource.setAttribute("password", Configurer.get(GeneralConfigurerSetup.DB_PASSWORD));
resource.setAttribute("driverClassName", Configurer.get(GeneralConfigurerSetup.DB_CLASS));
resource.setAttribute("url", Configurer.get(GeneralConfigurerSetup.DB_JDBCURL));
context.appendChild(resource);
doc.appendChild(context);
System.out.println(System.getProperty("java.class.path"));
File fOut = new File("META-INF/context.xml");
System.out.println(fOut.getAbsolutePath());
if (fOut.exists()) fOut.delete();
TransformerFactory tff = TransformerFactory.newInstance();
Transformer tf = tff.newTransformer();
Source input = new DOMSource(doc);
Result output = new StreamResult(fOut);
tf.transform(input, output);
*/
//META-INF/context
InitialContext enc = new InitialContext();
Context compContext = (Context) enc.lookup("java:comp/env");
DataSource dataSource = (DataSource) compContext.lookup("datasource");
DBDAO.setDataSource(dataSource);
}
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
try {
contextInitialized2(servletContext);
} catch(Exception e) {
Logger.error("Initializing failed: " + e.getMessage());
System.exit(-1);
}
}
public void contextDestroyed(ServletContextEvent sce) {
}
}

Remove context.xml from your build (just to be safe), and put it in your tomcat folder under conf/Catalina/localhost. Rename it so it's whatever you want your app context name to be (like myapp.xml).
As you can see, that allows each environment to have different myapp.xml files, and when you deploy the war, it will pick that up instead of using an internal one.

Related

JNDI Lookup Failing For Embedded Jetty Server

I have an integration test in one of my projects that I want to run against an embedded jetty server. I followed along with the example here: https://www.eclipse.org/jetty/documentation/jetty-9/index.html#jndi-embedded but when I go to actually run my test it fails with the error:
javax.naming.NameNotFoundException: env is not bound; remaining name 'env/jdbc/NavDS'
at org.eclipse.jetty.jndi.NamingContext.getContext(NamingContext.java:241)
at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:491)
at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:491)
at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:505)
at org.eclipse.jetty.jndi.java.javaRootURLContext.lookup(javaRootURLContext.java:101)
at javax.naming.InitialContext.lookup(InitialContext.java:417)
at com.tura.eyerep.test.TestWebServices.setUpBeforeClass(TestWebServices.java:63)
I'm sure there must be a simple mistake I'm making somewhere but I just can't seem to spot it. Can anyone give a suggestion of what I'm doing wrong here?
In my test I'm setting up the server with:
#BeforeClass
public static void setUpBeforeClass() throws Exception {
server = new Server(8080);
ClassList classList = ClassList.setServerDefault(server);
classList.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration", "org.eclipse.jetty.plus.webapp.EnvConfiguration", "org.eclipse.jetty.plus.webapp.PlusConfiguration");
WebAppContext context = new WebAppContext();
context.setExtractWAR(false);
context.setDescriptor("src/main/webapp/WEB-INF/web.xml");
context.setResourceBase("src/main/webapp");
context.setConfigurationDiscovered(false);
BasicDataSource ds = null;
ds = new BasicDataSource();
ds.setUrl("jdbc:h2:mem:myDB;create=true;MODE=MSSQLServer;DATABASE_TO_UPPER=FALSE;");
org.eclipse.jetty.plus.jndi.Resource mydatasource = new org.eclipse.jetty.plus.jndi.Resource(context, "jdbc/NavDS",ds);
server.setHandler(context);
server.start();
}
#Test
public void testLookup()
{
InitialContext ctx = new InitialContext();
DataSource myds = (DataSource) ctx.lookup("java:comp/env/jdbc/NavDS");
assertNotNull( myds);
}
In my web.xml I have a resource ref entry:
<resource-ref>
<description>Nav Datasource</description>
<res-ref-name>jdbc/NavDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Lets cleanup your testcase first.
// meaningless when you have a WAR that is a directory.
context.setExtractWAR(false); // remove this line
// This prevents Servlet 3 behaviors when using Servlet 2.x descriptors
context.setConfigurationDiscovered(false); // Remove this
The error you are getting ...
javax.naming.NameNotFoundException: env is not bound;
remaining name 'env/jdbc/NavDS'
That likely means that a server is still running somewhere, probably forgot to stop/cleanup the previous server instance. (Look at the example junit5 testcase below, for how it deals with non-fixed ports, how to reference a non-fixed port, and how it stops the lifecycle of the server).
Next, be aware of your scopes.
new org.eclipse.jetty.plus.jndi.Resource(context, "jdbc/NavDS",ds);
That will bind the resource entry to jdbc/NavDS on the scope context.
Which means if you look up the resource outside of the WebAppContext scope,
like you do with testLookup() method it will exist at initialContext.lookup("jdbc/NavDS"), and nowhere else, the java:comp/env prefix/tree doesn't even exist to that testLookup() method scope.
Inside of your webapp, such as in a Filter or Servlet, that context specific resource is bound and available at jdbc:comp/env/jdbc/NavDS.
You have 3 typical scopes.
Order
Scope
EnvEntry or Resource first parameter
1
WebApp
new EnvEntry(webappContext, ...) or new Resource(webappContext, ...)
2
Server
new EnvEntry(server, ...) or new Resource(server, ...)
3
JVM
new EnvEntry(null, ...) or new Resource(null, ...)
If the value doesn't exist at the WebApp scope, the Server scope is checked, and then the JVM scope is checked.
Your Server can have a value for the name val/foo and a specific webapp can have a different value for the same name val/foo, simply by how the scopes are defined.
Next, there's the binding in the Servlet spec, you have specify the <resource-ref> and this combined with the declaration at the server side, bound to context means you can access java:comp/env/jdbc/NavDS from your servlet in that specific webapp.
To see this a different way, in code ...
package jetty.jndi;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.List;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.FragmentConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class WebAppWithJNDITest
{
private static Server server;
private static WebAppContext context;
public static class JndiDumpServlet extends HttpServlet
{
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/plain");
PrintStream out = new PrintStream(resp.getOutputStream(), false, StandardCharsets.UTF_8);
try
{
dumpJndi(out);
}
catch (NamingException e)
{
throw new ServletException(e);
}
}
}
#BeforeAll
public static void startServer() throws Exception
{
server = new Server(0); // let os/jvm pick a port
Configuration.ClassList classList = Configuration.ClassList.setServerDefault(server);
classList.addAfter(FragmentConfiguration.class.getName(),
EnvConfiguration.class.getName(),
PlusConfiguration.class.getName());
context = new WebAppContext();
context.setContextPath("/");
// This directory only has WEB-INF/web.xml
context.setBaseResource(new PathResource(Paths.get("src/main/webroots/jndi-root")));
context.addServlet(JndiDumpServlet.class, "/jndi-dump");
new org.eclipse.jetty.plus.jndi.Resource(null, "val/foo", Integer.valueOf(707));
new org.eclipse.jetty.plus.jndi.Resource(server, "val/foo", Integer.valueOf(808));
new org.eclipse.jetty.plus.jndi.Resource(context, "val/foo", Integer.valueOf(909));
new org.eclipse.jetty.plus.jndi.EnvEntry(null, "entry/foo", Integer.valueOf(440), false);
new org.eclipse.jetty.plus.jndi.EnvEntry(server, "entry/foo", Integer.valueOf(550), false);
new org.eclipse.jetty.plus.jndi.EnvEntry(context, "entry/foo", Integer.valueOf(660), false);
server.setHandler(context);
server.start();
}
#AfterAll
public static void stopServer()
{
LifeCycle.stop(server);
}
public static void dumpJndi(PrintStream out) throws NamingException
{
InitialContext ctx = new InitialContext();
List<String> paths = List.of("val/foo", "entry/foo");
List<String> prefixes = List.of("java:comp/env/", "");
for (String prefix : prefixes)
{
for (String path : paths)
{
try
{
Integer val = (Integer)ctx.lookup(prefix + path);
out.printf("lookup(\"%s%s\") = %s%n", prefix, path, val);
}
catch (NameNotFoundException e)
{
out.printf("lookup(\"%s%s\") = NameNotFound: %s%n", prefix, path, e.getMessage());
}
}
}
}
#Test
public void testLookup() throws NamingException, IOException
{
System.out.println("-- Dump from WebApp Scope");
HttpURLConnection http = (HttpURLConnection)server.getURI().resolve("/jndi-dump").toURL().openConnection();
try (InputStream in = http.getInputStream())
{
String body = IO.toString(in, StandardCharsets.UTF_8);
System.out.println(body);
}
System.out.println("-- Dump from Test scope");
dumpJndi(System.out);
}
}
Contents of src/main/webroot/jndi-root/WEB-INF/web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<resource-ref>
<description>My Foo Resource</description>
<res-ref-name>val/foo</res-ref-name>
<res-type>java.lang.Integer</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
The output looks like ...
2021-10-26 17:05:16.834:INFO:oejs.Server:main: jetty-9.4.44.v20210927; built: 2021-06-30T11:07:22.254Z; git: 526006ecfa3af7f1a27ef3a288e2bef7ea9dd7e8; jvm 11.0.12+7
2021-10-26 17:05:17.012:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext#19932c16{/,file:///home/joakim/code/jetty/junk/src/main/webroots/jndi-root/,AVAILABLE}
2021-10-26 17:05:17.033:INFO:oejs.AbstractConnector:main: Started ServerConnector#212b5695{HTTP/1.1, (http/1.1)}{0.0.0.0:45387}
2021-10-26 17:05:17.034:INFO:oejs.Server:main: Started #816ms
-- Dump from WebApp Scope
lookup("java:comp/env/val/foo") = 909
lookup("java:comp/env/entry/foo") = 660
lookup("val/foo") = 707
lookup("entry/foo") = 440
-- Dump from Test scope
lookup("java:comp/env/val/foo") = NameNotFound: env is not bound
lookup("java:comp/env/entry/foo") = NameNotFound: env is not bound
lookup("val/foo") = 707
lookup("entry/foo") = 440
2021-10-26 17:05:17.209:INFO:oejs.AbstractConnector:main: Stopped ServerConnector#212b5695{HTTP/1.1, (http/1.1)}{0.0.0.0:0}
2021-10-26 17:05:17.210:INFO:oejs.session:main: node0 Stopped scavenging
2021-10-26 17:05:17.214:INFO:oejsh.ContextHandler:main: Stopped o.e.j.w.WebAppContext#19932c16{/,file:///home/joakim/code/jetty/junk/src/main/webroots/jndi-root/,STOPPED}
Hopefully the scope differences are obvious above.
A variation of the above test is now available at the Eclipse Jetty Embedded Cookbook project.
https://github.com/jetty-project/embedded-jetty-cookbook/
Available in 3 different Jetty flavors
Jetty 9.4.x - WebAppContextWithJNDI.java
Jetty 10.0.x - WebAppContextWithJNDI.java
Jetty 11.0.x - WebAppContextWithJNDI.java

How to set relaxedQueryChars of tomcat from the application level

Allowing special characters in request URL is possible in tomcat_folder/conf/server.xml as far as I know, but I am somewhat required to find another way to set these special characters from the application side (if it is possible), I mean using web.xml or any other way. Here, I have enabled "[" (opening square bracket) and "]" (closing square bracket) in the query:
<Connector port="8080" protocol="HTTP/1.1" relaxedQueryChars='|[]'
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"/>
This works pretty fine and I would appreciate it if anyone can help me to set without touching server.xml.
Info about the application environment:
Tomcat version: 7.0.96,
Spring: 4.1.7.RELEASE
I agree with #M.Deinum.
It is not recommended to touch server.xml of the tomcat using java code as it can stop other application from working properly.
But still you want to do, you can take the risk.
Sample Java code :
import java.io.File;
import java.util.Arrays;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class RelaxedQueryCharsExample {
public static void main(String[] args)
throws ParserConfigurationException, SAXException, TransformerException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
File file = new File("D:/tomcat/conf/server.xml");
Document document = documentBuilder.parse(file);
NodeList list = document.getElementsByTagName("Connector");
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
NamedNodeMap map = node.getAttributes();
if (map.getNamedItem("port").getNodeValue().equalsIgnoreCase("8080")
&& map.getNamedItem("protocol").getNodeValue().equalsIgnoreCase("HTTP/1.1")) {
map.getNamedItem("relaxedQueryChars").setNodeValue("|[]");
}
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
document.setXmlStandalone(true);
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(file);
transformer.transform(source,result);
}
}
Note: Run this code before running tomcat server. In between running this may crash the server.

read manifest.mf while initializing JAX-RS

I am trying to read information from MANIFEST.MF while initializing JAX-RS. I use Glassfish app server.
My first idea was to use the following piece of code:
Properties prop = new Properties();
prop.load(getClasses().getClass().getResourceAsStream("/META-INF/MANIFEST.MF"));
String version = prop.getProperty("Implementation-Version");
But this code reads info from another MANIFEST.MF, not from my WAR so I need to use ServletContext:
package com.remal.forum.service.configuration.rest;
import java.io.InputStream;
import java.util.jar.Manifest;
import javax.servlet.ServletContext;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
#ApplicationPath("api")
public class Configurator extends Application {
#Context
protected ServletContext servletContext;
public Configurator() {
...
InputStream inputStream = servletContext.getResourceAsStream("/META-INF/MANIFEST.MF");
Manifest manifest = new Manifest(inputStream);
manifest.getAttributes("Implementation-Version");
...
}
}
But the injected servletContext is always null. What is wrong here?

How to get the initial context factory from Weblogic Server?

I am trying to create a java program which will just receive the name of initial context factory in Weblogic Server.
You may find below the java source code that i am trying to execute :
import java.io.IOException;
import java.io.Serializable;
import java.rmi.MarshalledObject;
import java.util.Hashtable;
import java.util.Map.Entry;
import javax.naming.Binding;
import javax.naming.CommunicationException;
import javax.naming.ConfigurationException;
import javax.naming.Context;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.Context;
import javax.naming.InitialContext;
public class GetInitialContextClass
{
public static void main(String[] args) {
Hashtable env = new Hashtable(5);
Context ctx = getInitialContext(env);
System.out.println(ctx);
}
}
But i have received the bellow error :
symbol : method getInitialContext(java.util.Hashtable)
location: class GetInitialContextClass
Context ctx = getInitialContext(env);
^
Please for your help.
receive the name of initial context factory in Weblogic Server
means nothing.If you need to connect to the WebLogic Server jndi tree use the following code :
Hashtable env = new Hashtable(5);
env.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL,
"t3://weblogicServer:7001");
Context ctx = new InitialContext(env);
Change the PROVIDER_URL env variable while preparing the Initial Context as follows:
private static Context getInitialContext() throws NamingException {
Hashtable env = new Hashtable();
// WebLogic Server 10.x/12.x connection details
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL, "t3://oc-144-21-91-92.compute.oraclecloud.com:9073");
return new InitialContext(env);
}
Please note the host:port combination should be as follows:

How to read a configuration file in eclipse dynamic web project?

I know that there is a configuration file called web.xml
What I want to achieve is have another configuration file that has application specific configuration and it has to be read when the web server is started. I also want a Class to be able to read this configuration. Is there a way I can configure this is web.xml file itself or is there another way
You can use the Apache Commons Configuration. Have a look at the user guide. Since you want it to be done on startup here is a sample ServletContextListener:
package test;
import java.io.File;
import java.net.MalformedURLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
public class ConfigurationListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
File configFile;
try {
configFile = new File(context.getResource("/WEB-INF/configuration.xml").getPath());
Configuration config = new XMLConfiguration(configFile);
context.setAttribute("configuration", config);
} catch (ConfigurationException | MalformedURLException ex) {
Logger.getLogger(ConfigurationListener.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void contextDestroyed(ServletContextEvent sce) {}
}
Now get your configuration anywhere in your web application like this:
Configuration config = (Configuration) request.getServletContext().getAttribute("configuration");
I would create a class to hold the configuration though rather than adding it as an attribute to the ServletContext. The class would simply provide access to the configuration through a static method.

Categories

Resources