It seems simple but ...
I'm just starting to learn liberty profile.
I can't use shared library.
here's documentation:
https://www.ibm.com/support/knowledgecenter/SSD28V_liberty/com.ibm.websphere.wlp.core.doc/ae/cwlp_sharedlibrary.html
so in server.xml I put (and restart server), for example:
<library>
<folder dir="C:/libs/gson/"></folder>
<!-- or even <file name="C:/libs/gson/gson-2.3.1.jar" /> -->
</library>
Anyway at runtime I receive: "java.lang.NoClassDefFoundError: com/google/gson/Gson"
On a servlet I just have the import and a simple use:
...
import com.google.gson.Gson;
...
#WebServlet("/")
public class HelloWorld extends HttpServlet {
...
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Serialization
Gson gson = new Gson();
...
What I'm missing?
Adding a library element to server configuration doesn't automatically make it available to applications. You need to configure the application to have access to the library, for example,
<application location="myapp.war">
<classloader commonLibraryRef="gsonLib"/>
</application>
<library id="gsonLib">
<file name="C:/libs/gson/gson-2.3.1.jar" />
</library>
The part you are missing is that the application needs to be configured to reference the library. There are a few ways to do this:
Use the Global shared library as described here: https://www.ibm.com/support/knowledgecenter/SSEQTP_liberty/com.ibm.websphere.wlp.doc/ae/twlp_classloader_global_libs.html
Then, simply change your library ID to global like this:
<library id="global">
Give your library an ID and add a <classloader commonLibraryRef="..."/> as described in #njr's answer
Related
Recently I tried Tomcat 10.0.10 and when trying to inject the connection pool as a JNDI resource find out that the #Resource annotation doesn't work.
Then I tried obtain it programmatically by creating a InitialContext and it worked. Initially I thought it was only for the java:comp/env/jdbc so I tried with a simple bean like below and tried to inject it with the #Resource annotation it didn't work again. When I try to obtain it programmatically by creating a InitialContext and it works. Then I check whether the #PostConstruct or #PreDestroy annotation works and found out that they also don't work.
package lk.ijse.test.tomcatdbcp;
public class Something {
}
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="bean/Something" auth="Container"
type="lk.ijse.test.tomcatdbcp.Something"
factory="org.apache.naming.factory.BeanFactory"
/>
</Context>
<?xml version="1.0" encoding="UTF-8"?>
<web-app metadata-complete="false" xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<resource-env-ref>
<resource-env-ref-name>bean/Something</resource-env-ref-name>
<resource-env-ref-type>lk.ijse.test.tomcatdbcp.Something</resource-env-ref-type>
</resource-env-ref>
</web-app>
package lk.ijse.test.tomcatdbcp;
import java.io.*;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import javax.naming.InitialContext;
import javax.naming.NamingException;
#WebServlet(name = "helloServlet", value = "/hello", loadOnStartup = 1)
public class HelloServlet extends HttpServlet {
private String message;
#Resource(name= "java:comp/env/bean/Something")
private Something something;
#PostConstruct
public void doSomething(){
System.out.println("Does it work?");
}
public void init() {
message = "Hello World!";
try {
InitialContext ctx = new InitialContext();
Something lookup = (Something) ctx.lookup("java:comp/env/bean/Something");
System.out.println(lookup);
System.out.println(something); // null
} catch (NamingException e) {
e.printStackTrace();
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
// Hello
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>" + message + "</h1>");
out.println("</body></html>");
}
public void destroy() {
}
}
To reproduce the same issue, I created a sample repo here: https://github.com/sura-boy-playground/play-with-tomcat10
(Complete code can be found there)
At first, I had used javax.annotation.Resource annotation, so I thought that was the reason because of the javax.* to jakarta.* namespace change. Then I tried it with jakarta.annotation.Resource but the result was same.
I tried the same application with Tomcat 9.0.41 plus javax.* namespace, it works perfectly.
Is there any extra stuff that I need to do on Tomcat 10.0.10 to enable these annotations? I dug the Tomcat 10 documentation but I wasn't able to find out any thing related to my issue.
I found out that there was a similar case in Tomcat 7 previously, but I don't like that kind of workaround now.
Tomcat #Resource annotations API annotation stops working in Tomcat 7
You should declare the scope of your jakarta.annotation dependency as provided:
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.0.0</version>
<scope>provided</scope>
</dependency>
If you have two copies of jakarta.annotation.Resource (one in the common classloader and one in your application's classloader), the two classes are different. The InstanceManager will look for fields annotated with the common classloader's copy of #Resource, while the something field is annotated with your webapp's copy of #Resource.
Edit: this problem was fixed in Tomcat 10.0.17 (cf. changelog), 9.0.59 and 10.1.0-M11.
Remark: You will have the same problem in Tomcat 9.0 if you use Java 11 or later. Before Java 11 the javax.annotation.* classes where included in the JRE. Servlet containers are required to look in the bootstrap/JRE classloader before looking in the webapp classloader (overriding javax.* classes is a breach of Java's licence), therefore Tomcat would never find the additional copy of the classes.
I have the context for my Web application defined similar to
<Context>
<Realm className="org.apache.catalina.realm.JDBCRealm" driverName="com.microsoft.sqlserver.jdbc.SQLServerDriver" connectionURL="jdbc:sqlserver://greensuite.database.windows.net:1433;database=greensuite_db;user=greensuiteapp#greensuite;password=K1B&i9i8*1id^dUzhsv^;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;" userTable="[User]" userNameCol="email" userCredCol="password" userRoleTable="UserGlobalRole" roleNameCol="role">
<CredentialHandler className="org.apache.catalina.realm.MessageDigestCredentialHandler" encoding="UTF-8" algorithm="SHA-512" iterations="1000000" saltLength="20" />
</Realm>
</Context>
What can I put in an action listener to be able to do something like:
String passwordHash = credentialHandler.mutate(newPassword);
where credentialHandler is a CredentialHandler instance that applies to the request being processed.
The point is to have the configuration in one place, the context configuration instead of duplicating the configuration in the context configuration and in the code generating and storing a new hash.
Basically you need to follow the indications given in Christopher Schultz's slides from ApacheCon 2016. Supposing you don't object to referencing Tomcat 8.x catalina.jar in your build process, first import the required classes:
import javax.servlet.ServletContext;
import org.apache.catalina.CredentialHandler;
import org.apache.catalina.Globals;
Then, getting the CredentialHandler is a simple matter of using the correct key to look up the ServletContext's attributes:
public static CredentialHandler getCredentialHandler(final ServletContext context) {
return (CredentialHandler) context.getAttribute(Globals.CREDENTIAL_HANDLER);
}
The slides give an alternative method using reflection to avoid having to include catalina.jar in your compilation classpath.
Beware though that this only works if you have a single unnested <Realm> configured in your context.xml. If you are wrapping your <Realm> with a LockoutRealm, for instance, the lookup will only consider the outermost Realm returning a generic CredentialHandler, instead of drilling down to the Realm containing the CredentialHandler you configured.
I am trying to create simple web-app. And stuck on datasource injection. There seems to be several problems. So I will start from my confusion. As I understand there's 2( at least) ways to inject the DataSource into Servlet:
web.xml
#Resource
web.xml sample
<resource-ref>
<res-ref-name>jdbc/MyDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<injection-target>
<injection-target-class>ua.test.TestServlet</injection-target-class>
<injection-target-name>dataSource</injection-target-name>
</injection-target>
</resource-ref>
#Resource sample
public class TestServlet extends HttpServlet{
#Resource
private DataSource dataSource;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
My confusion : web.xml doesn't work in Tomcat 7 on my simple project. In my opinion, web.xml option should work since there were no annotations before Java 5. Please explain.
Update:
Datasource configuration
<Resource name="jdbc/MyDB"
type="javax.sql.DataSource"
auth="Container"
username="SA"
password=""
driverClassName="org.hsqldb.jdbcDriver"
url="jdbc:hsqldb:file:~/database/my_db"
/>
Try taking out the injection-target entry in web.xml and using the name attribute on the #Resource annotation:
public class TestServlet extends HttpServlet {
#Resource(name = "jdbc/MyDB")
private DataSource dataSource;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
That worked in my local testing with Tomcat 7.0.50. If you're looking for the annotation-less way of doing it, I haven't gotten that to work, even though it should given their changelog1.
EDIT
I still haven't found a solution, but I was curious why this doesn't work so I took a look at the injection-target code. I found that it loads the context.xml entry first, and does pick up the settings from web.xml, but chooses not to override the configuration it found in context.xml because it already sees a jdbc/MyDB entry. I'm not sure how to get the injection-target settings into context.xml or the DB settings like driverClassName into web.xml.
As far as I know, tomcat is a nice servlet container, but it is not a full Java EE container. From The BalusC Code: How to install CDI in Tomcat?, I think that out of the box tomcat is not able to do any dependency injection. Tomcat alone works perfectly associated with Spring, because it is lightweight.
If you do not want to use Spring, the link I wrote above should give you some ways to do CDI with tomcat (TomEE instead of tomcat, Weld or OpenWebBeans).
EDIT:
Apparently, recent versions of tomcat 7 should accept DI - see below the link in comment from davidfmatheson.
I'm attempting to start a simple java maven webapp from eclipse Indigo containing only one class, a basic annotated websocket. I would like annotation scanning to identify and register the class automatically using the sourceforge eclipse-jetty plugin which is configured to use my own local jetty distribution 9.1.1.v20140108. Code for the websocket is as follows:
package com.myapp.websocket_sample;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
#ServerEndpoint(value = "/ws/broadcast", encoders = {}, decoders = {})
public class TestWebsocket {
private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());
private static final Logger logger = LoggerFactory.getLogger(TestWebsocket.class);
public TestWebsocket() {
logger.info("Initializing websocket.");
}
#OnOpen
public void onOpen(final Session session) {
logger.info("Opening new websocket session.");
sessions.add(session);
}
#OnClose
public void onClose(final Session session) {
logger.info("Closing new websocket session.");
sessions.remove(session);
}
#OnMessage
public void onMessage(final String message, final Session client) throws IOException, EncodeException {
logger.info("Got message {}.", message);
for (final Session session : sessions) {
session.getBasicRemote().sendObject(message);
}
}
}
When starting under eclipse-jetty, the websocket class is apparently not being identified and scanned by jetty. In looking at the (auto-generated) jetty.xml used by eclipse-jetty plugin, the class appears to be on the extraClasspath option.
<Set name="handler">
<New class="org.eclipse.jetty.webapp.WebAppContext">
<Arg type="String">src/main/webapp</Arg>
<Arg type="String">/ws-sample</Arg>
<Set name="extraClasspath">C:/Users/padolan/AppData/Local/eclipse-workspaces/websockets/websocket-sample/target/classes;C:/Users/padolan/.m2/repository/javax/websocket/javax.websocket-api/1.0/javax.websocket-api-1.0.jar;C:/Users/padolan/.m2/repository/org/slf4j/slf4j-api/1.6.1/slf4j-api-1.6.1.jar</Set>
</New>
</Set>
I can take the same project (built as a war file using maven), copy it to the jetty distribution's webapps folder, and start it using the jetty start.jar like so, and the websocket is picked up and made available:
java -jar start.jar --module=webapp,websocket
I suspect I need to add some special jetty configuration or additional classes to the classpath of the eclipse-jetty plugin to get it to pick up the annotated websocket class, but I'm not sure what that should be. Any suggestions?
So after piecing together solutions from various posts on configuring jetty, and through a bit of trial and error, I got this to work. The steps I had to follow were:
Create a new eclipse-jetty run configuration and point eclipse-jetty to the new jetty distribution (standard feature supported by the eclipse-jetty plugin).
Add the websocket classes (and other dependent classes) as custom classpath bootstrap entries in the eclipse-jetty plugin. While I ended up adding all jetty distribution jars in there, this was likely overkill and you could figure out which ones are really necessary.
Create and reference a new custom jetty.xml configuration which adds the proper annotation-scanning configuration classes. This has to be added as another jetty context configuration in the eclipse-jetty plugin's run configuration window, below the standard 'Eclipse Jetty Launcher Context' entry (which is an auto-generated jetty.xml file that you cannot change). The actual file I used looked like the following:
<?xml version="1.0"?>
<!-- DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd" -->
<!DOCTYPE Configure [
<!ELEMENT Configure (Set*,Call*)>
<!ELEMENT Call (Arg*,Call*)>
<!ELEMENT Arg (#PCDATA|Ref|Array)*>
<!ELEMENT Array (Item+)>
<!ELEMENT Item (#PCDATA)>
<!ATTLIST Configure id CDATA "">
<!ATTLIST Configure class CDATA "">
<!ATTLIST Call class CDATA "">
<!ATTLIST Call name CDATA "">
<!ATTLIST Arg name CDATA "">
<!ATTLIST Array type CDATA "">
]>
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="configurationClasses">
<Array type="java.lang.String">
<Item>org.eclipse.jetty.webapp.WebInfConfiguration</Item>
<Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
<Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
<!-- <Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
<Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
<Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item> -->
<Item>org.eclipse.jetty.annotations.AnnotationConfiguration</Item>
<Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
</Array>
</Set>
</Configure>
Special note: I was not able to directly reference the public jetty dtd, hence I hacked together the embedded one which allowed eclipse-jetty to load the config without error
That's it (ha)! With this I could add new annotated websocket classes to my web project (as well as annotated servlet classes) and jetty would pick them up when started with the eclipse-jetty run configuration.
The problem is, whenever you change the log4j.properties/log4j.xml, you need to restart the tomcat [ or say any other server ]. Is there any workaround of reloading the log4j configuration?
From http://logging.apache.org/log4j/1.2/faq.html#3.6
Is there a way to get log4j to
automatically reload a configuration
file if it changes?
Yes. Both the DOMConfigurator and the
PropertyConfigurator support automatic
reloading through the
configureAndWatch method. See the API documentation for more
details.
Because the configureAndWatch launches
a separate wathdog thread, and because
there is no way to stop this thread in
log4j 1.2, the configureAndWatch
method is unsafe for use in J2EE
envrironments where applications are
recycled.
Said that, I've successfully used PropertyConfigurator#configureAndWatch method in a Java EE environment (Sun One Web Server, not Tomcat).
As of log4j 2.x you can reload the config periodically, in this example every 30 seconds:
<configuration monitorInterval="30">
Please take a look here for more information on log4j 2.x configuration:
You can write a little initializer code with the following short steps:
listen for the "BEFORE_START_EVENT",
when the event happens (once per Tomcat restart), start log4j using the configureAndWatch method
also don't forget to install a shutdown hook to cleanup the watcher thread
See this blog post for details - reload log4j configuration in tomcat
They also moved it to github.
Update:If you are using lg4j2.xml, the configuration is the only thing you will need for log4j to be managed at runtime
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO" monitorInterval="30">
<Loggers>
-------
</Loggers>
</Configuration>
Monitor interval 30 loads the log4j changes every 30 seconds.
Below solution is if you are on older version of log4j.
Yes you can change the log4j level at run time without the need to restart the server provided you are using spring.
public class OptionalLog4jConfigurer extends Log4jConfigurer implements
InitializingBean {
public static final Long DEFAULT_REFRESH = 30000L;
private static final Log LOG = LogFactory
.getLog(OptionalLog4jConfigurer.class);
private String configLocation;
private Long refreshInterval;
public OptionalLog4jConfigurer(final String configLocation,
final Long refreshInterval) {
this.configLocation = configLocation;
if (refreshInterval == null) {
this.refreshInterval = DEFAULT_REFRESH;
}
else {
this.refreshInterval = refreshInterval;
}
}
public void afterPropertiesSet() throws Exception {
if (!StringUtils.isEmpty(this.configLocation)) {
LOG.info("Log4J configuration is being customized.");
this.initLoggingInternal();
}
else {
LOG
.info("Using default Log4J configuration. No customization requested");
}
}
public String getConfigLocation() {
return this.configLocation;
}
public Long getRefreshInterval() {
return this.refreshInterval;
}
}
Then do these changes to applicationContext.
<bean id="optionalLog4jInitialization" class="com.skg.jetm.OptionalLog4jConfigurer">
<constructor-arg index="0" type="java.lang.String" value="${log4j.configuration}" />
<constructor-arg index="1" type="java.lang.Long" value="100" />
</bean>
Full code and explanation can be found here
Changing log4j Level dynamically
You can create a strut action or a servlet which reload the properties file. So after editing the log4j.properties file, you will need to call the servlet to reload it.
For example:
public class Log4JServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
protected static Logger log = Logger.getLogger(Log4JTestServlet.class);
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("Reload Log4J prop file");
String path = "C:\\GlassFishESBv22\\glassfish\\domains\\domain1\\config\\log4j.properties";
PropertyConfigurator.configure(path);
/*
log.debug("debug message");
log.info("info message");
log.warn("warn message");
log.error("error message");
log.fatal("fatal message");
*/
}
}
Another way is to configure Spring Framework's Log4jConfigListener in web.xml
The Guido Garcia answer is quite on target.
Log4j 1 offers a way of reloading log4j configuration in a non JEE thread safe maner.
So if you are in a JEE continer, you can solve your problem trivially by:
(A) Create your #Singleton ejb timer to periodically scan your log4j.properties file
(b) Look at the implementaiton of the log4j log watch given by log4j.
What it does when it is time to relaoad a file is quite simply and conveniently, the following:
new PropertyConfigurator().doConfigure(filename,LogManager.getLoggerRepository());
Just do the same, if the time stamp on you configuration file changes.
That is it.
Another Method is to configure a file watcher using Java File WatcherService as explained below link and reload Log4J configuration on any file Modifications.
https://dzone.com/articles/how-watch-file-system-changes
Reloading can be done using DOMConfigurator's APIs
https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/DOMConfigurator.html