Servlet 3.0 support in embedded Jetty 8.0 - servlets

For my unit-tests I use a simple test-server based on Jetty:
package eu.kostia.textanalysis.webservices.jetty;
import java.awt.Desktop;
import java.net.URI;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
public class TestServer {
static private final String CONTEXT_PATH = "/webservice";
static private final String PROJECT_HOME = System.getenv("MY_WORKSPACE_HOME") + "/WebServices";
static public final int PORT = 8080;
private Server server;
private Exception startException;
private static class SingletonHolder {
private static final TestServer INSTANCE = new TestServer();
}
/**
* Returns the singleton instance of the test server.
*
* #return the singleton instance of the test server.
*/
public static TestServer getInstance() {
return SingletonHolder.INSTANCE;
}
private TestServer() {
server = new Server(PORT);
WebAppContext context = new WebAppContext();
context.setDescriptor(PROJECT_HOME + "/web/WEB-INF/web.xml");
context.setResourceBase(PROJECT_HOME + "/web");
context.setContextPath(CONTEXT_PATH);
context.setParentLoaderPriority(true);
server.setHandler(context);
}
/**
* Start the test server. This method returns only when the server is
* complete started. There is no effect when you invoke this method and the
* server is already running.
*/
public void start() {
if (!server.isRunning()) {
startException = null;
new Thread("TestServer") {
public void run() {
try {
server.start();
server.join();
} catch (Exception exc) {
startException = exc;
}
}
}.start();
while (true) {
if (startException != null) {
throw new Error(startException);
}
// Block this method call until the server is started
if (server.isStarted()) {
return;
}
}
}
}
/**
* Stop the test server.
*/
public void stop() {
try {
if (server.isRunning()) {
server.stop();
}
} catch (Exception e) {
throw new Error(e);
}
}
/**
* Returns {#code true} is the server is running.
*
* #return {#code true} is the server is running.
*/
public boolean isRunning() {
return server.isRunning();
}
public static void main(String[] args) throws Exception {
TestServer.getInstance().start();
Desktop.getDesktop().browse(new URI("http://localhost:8080/webservice/"));
}
}
It works very well for servlet configured in web.xml but I'd now like to use the new annotation syntax introduced by the Servlet Specification 3.0, for example:
#WebServlet(urlPatterns = {"/hello"})
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter writer = response.getWriter();
writer.print("<h1>HttpServlet using Servlet 3.0</h1>");
}
}
How shoud I configure Jetty in my TestServer class to process also annotation-based servlets?

Add to your code
context.setConfigurations(new Configuration[] {
new AnnotationConfiguration(), new WebXmlConfiguration(),
new WebInfConfiguration(), new TagLibConfiguration(),
new PlusConfiguration(), new MetaInfConfiguration(),
new FragmentConfiguration(), new EnvConfiguration() });
You only need to set the AnnotationConfiguration to get the auto-discovery of annotated classes to work. The rest of the configurations are so you can enable other aspects of the container. Supposedly you should be able to do this from the commandline, using OPTIONS=annotations,jsp,(etc...), but I never got that working. At least this way it should pick up your annotated classes properly in the embedded environment.
Also as a side note it appears the Eclipse jetty project has annotation turned off by default, whereas riptide claims to have them turned on by default. I'm guessing this is a difference in the configuration files.

Answering yet another year later.
In the current version of Jetty (8.1) you can accomplish exactly what you want with the command line:
java -jar start.jar OPTIONS=annotations,plus etc/jetty-plus.xml
invoked from the jetty home directory.

Jetty 8 is implementing the servlet 3.0 specification but it's still experimental.
You could also use the embedded glassfish 3 plugin to run your tests. See the below links for some info:
http://wikis.sun.com/display/GlassFish/3.1EmbeddedOnePager
http://ocpsoft.com/java/using-embedded-glassfish-with-maven/
http://embedded-glassfish.java.net/
I realise as I write this that there are no authoritative resource for using the Glassfish plugin in the manner Jetty is often used. However it does work in a similar way.
I hope this helps at least a bit.

Related

How to get #Postconstruct with Spring batch in web container?

Currently I am trying to get my script to run on a tomcat server by using the basic web container guidelines for spring-batch-boot from the documentation https://docs.spring.io/spring-batch/reference/html/configureJob.html
The script was working correctly as a jar file before modifications to the main class but when I try converting it to a servlet I am having issues with my #PostConstruct starting only on server startup. This code sets application.properties to spring.batch.job.enabled=false and has a controller of
#Controller
public class JobLauncherController {
#Autowired
JobLauncher jobLauncher;
#Autowired
Job job;
#RequestMapping("/jobLauncher.html")
public void handle() throws Exception{
jobLauncher.run(job, new JobParameters());
}
With The main Application to start the servlet for tomcat as
#SpringBootApplication
#EnableBatchProcessing
public class BatchApplication extends SpringBootServletInitializer{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(BatchApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(BatchApplication.class, args);
}
The problem is that my job uses custom item readers and writers that initializes it before running it using #PostConstruct. It runs the #PostConstruct at server startup which is what helps initialize the beans for writing.
My item readers/writers look like this
public class CustomReader extends ItemStreamSupport implements ItemReader<Acct>, ResourceAwareItemReaderItemStream<Acct> {
//basic autowiring
private int nextAcctIndex;
private List<Acct> acctsList = new ArrayList();
#PostConstruct
private void initialize() throws IOException {
//logic to parse files
acctsList = Collections.unmodifiableList(acctsList);
nextAcctIndex = 0;
}
#Override
public Acct read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
// System.out.println("Start Read");
Acct nextAcct = null;
if (nextAcctIndex < acctsList.size()) {
nextAcct = acctsList.get(nextAcctIndex);
nextAcctIndex++;
//System.out.println(nextAcct);
}
The BatchConfiguration calls everything like most examples as
#Bean public
IteamReader<Acct> CustomReader(){ return new CustomReader();}
My question is am I going about this the wrong way or is there a way to make it so the #PostConstruct is able to be called only when the Controller request for it?
you need to use
#BeforeStep
public void beforeStep(StepExecution stepExecution) {
init();
}
#PostConstruct is used to initialize once after applicationContext is loaded.
In your case you want to run this initialization every time job is running (you don't want data to be leaked across different jobs, right?)

How do I implement the business logic of a MVC in a seperate class from the Servlet?

I have experimented putting the class in the servlets controller but I have trouble with the constructor and setting method access. I read its improves efficiency to have the business logic seperate, I even tried putting it in the JavaBean but I don't yet know how to send parameters from the controller to it. I still mave much to learn, just working on a project.
You can use EJB to separate business logic from Presentation tier i.e (Servlets and JSP) in JavaEE platform. If your project doesn't have much business logic code then simply use Java POJO classes. This example gives very raw idea. you can use web frameworks which have built in MVC design.
Controller:
Use Servlets to control navigation or perform other tasks against HTTP requests.
#WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public LoginServlet() {
super();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
LoginManager loginManager=new LoginManager();
if(loginManager.isValidUser("getUserID from request Params","password from request params")){
//initialize user session and redirect to dashboard
//response.sendRedirect("/userhome.jsp");
}else{
//display failure messages. etc...
//response.sendRedirect("/login.jsp");
}
}
}
Model:
POJO which contains set of methods for login related operations.
public class LoginManager {
private Connection con;
public LoginManager() {
}
private void initConnection(){
//register driver class and create a new connection
//you can create separate DBUtils class to get new connections
//to prevent boilerplate code.
//make new connection to database
// con=..
}
private void closeConnection() throws SQLException{
con.close();
}
public boolean isValidUser(String user,String password) throws SQLException{
initConnection();
PreparedStatement pstm=con.prepareStatement("select 1 from users where userID = ? and password=?");
//set userID and password params
ResultSet rs=pstm.executeQuery();
if (rs.next()){
if(checkpassword.....)
return true;
}
closeConnection();
return false;
}
}
View:
pages like login.jsp and userhome.jsp pages are views;

How to fix unsafe implementation of X509TrustManager in Android app

Google has advised that I have an unsafe implementation of the interface X509TrustManager in my Android application and need to change my code as follows:
To properly handle SSL certificate validation, change your code in the
checkServerTrusted method of your custom X509TrustManager interface to
raise either CertificateException or IllegalArgumentException whenever
the certificate presented by the server does not meet your
expectations. For technical questions, you can post to Stack Overflow
and use the tags “android-security” and “TrustManager.”
How can the following code be modified to fix the above issue?
public EasySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
mContext.init(null, new TrustManager[] { tm }, null);
}
I have solved this using the following code:
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
chain[0].checkValidity();
} catch (Exception e) {
throw new CertificateException("Certificate not valid or trusted.");
}
}
If you encounter this from external library you're using, check if appache libraray is the cause of it.
For me apache library caused the error : i was using deprecated class - MultipartEntity. This class uses SSLContextBuilder
which uses TrustManagerDelegate. TrustManagerDelegate implements X509TrustManager, which cause "unsafe implementation of TrustManager" error when uploading application to google play store.
The solution is : instead of deprecated MultipartEntity class, use MultipartEntityBuilder.
For example :
MultipartEntity httpMultipart = new MultipartEntity();
String contentType = httpMultipart.getContentType().getValue();
Will be replaced by :
MultipartEntityBuilder httpMultipart = new MultipartEntityBuilder();
String contentType = httpMultipart.build().getContentType().getValue();
Add the upgraded version of OKttps worked for me crashing in Android 10
implementation 'com.squareup.okhttp3:okhttp:4.8.0'
I have meet this problem.If your code is like that:
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
it will accept all certificate and it is a bad idea,so google send you mail.
We can make a change to accept self-signed certificate too.
I solved it,here is my question and my solution
If you are using HttpClient then the solution of #Nabeel is very nice, but if you are using HttpsUrlConnection then this code is very nice for that:
import android.util.Log;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
/**
* TrustManager that accepts all certificates and hosts.
* Useful when you want to use HTTPS but you have self-signed certificates.
* Works with HttpsUrlConnection.
* Use at your own risk and only for development.
*
* #author gotev (Aleksandar Gotev)
*/
public class AllCertificatesAndHostsTruster implements TrustManager, X509TrustManager {
#Override
public final void checkClientTrusted(final X509Certificate[] xcs, final String string)
throws CertificateException {
}
#Override
public final void checkServerTrusted(final X509Certificate[] xcs, final String string)
throws CertificateException {
}
#Override
public final X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
/**
* Gets an {#link SSLContext} which trusts all certificates.
* #return {#link SSLContext}
*/
public static SSLContext getSSLContext() {
final TrustManager[] trustAllCerts =
new TrustManager[] {new AllCertificatesAndHostsTruster()};
try {
final SSLContext context = SSLContext.getInstance("SSL");
context.init(null, trustAllCerts, new SecureRandom());
return context;
} catch (Exception exc) {
Log.e("CertHostTruster", "Unable to initialize the Trust Manager to trust all the "
+ "SSL certificates and HTTPS hosts.", exc);
return null;
}
}
/**
* Creates an hostname verifier which accepts all hosts.
* #return {#link HostnameVerifier}
*/
public static HostnameVerifier getAllHostnamesVerifier() {
return new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
}
/**
* Call this method once before all your network calls
* to accept all the self-signed certificates in HTTPS connections.
*/
public static void apply() {
final TrustManager[] trustAllCerts =
new TrustManager[] {new AllCertificatesAndHostsTruster()};
try {
final SSLContext context = SSLContext.getInstance("SSL");
context.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
} catch (Exception exc) {
Log.e("CertHostTruster", "Unable to initialize the Trust Manager to trust all the "
+ "SSL certificates and HTTPS hosts.", exc);
}
}
}
Source: https://gist.github.com/gotev/6784c1303793c6ee9e56
Then to use self-signed certificates, just invoke:
AllCertificatesAndHostsTruster.apply();
before any network calls.

Spring Boot: Log all incoming HTTP requests when using an embedded Jetty server?

I'm building a web app using Spring Boot 1.1.5.RELEASE and I've configured an embedded Jetty as described in the related Spring Boot's documentation.
I want to log all incoming HTTP requests and the only solution I can think (after reading "how to configure Jetty" in Spring Boot's docs) is to introduce an EmbeddedServletContainerCustomizer:
package com.acme.rest;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.NCSARequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* blah blah
*
* #author Dimi
*/
#Component
public class EmbededJettyConfig implements EmbeddedServletContainerCustomizer {
/*
* (non-Javadoc)
*
* #see org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer # customize
*/
#Override
public void customize(final ConfigurableEmbeddedServletContainer container) {
// checks whether the container is Jetty
if (container instanceof JettyEmbeddedServletContainerFactory) {
((JettyEmbeddedServletContainerFactory) container)
.addServerCustomizers(jettyServerCustomizer());
}
}
#Bean
public JettyServerCustomizer jettyServerCustomizer() {
return new JettyServerCustomizer() {
/*
* (non-Javadoc)
*
* #see org.springframework.boot.context.embedded.jetty.JettyServerCustomizer #
* customize
*/
#Override
public void customize(final Server server) {
HandlerCollection handlers = new HandlerCollection();
RequestLogHandler requestLogHandler = new RequestLogHandler();
handlers.setHandlers(new Handler[] {new DefaultHandler(), requestLogHandler});
server.setHandler(handlers);
NCSARequestLog requestLog = new NCSARequestLog("logs/requests.log");
requestLog.setExtended(false);
requestLogHandler.setRequestLog(requestLog);
}
};
}
}
The application now fails to start throwing an exception:
java.lang.IllegalArgumentException: A ServletContext is required to
configure default servlet handling
In short: what is the correct way of configuring the embedded Jetty's logging in Spring Boot?
Jetty 9.3.8 has a new method setRequestLog on the server.
#Component
public class EnableRequestLog implements EmbeddedServletContainerCustomizer {
private static final JettyServerCustomizer USE_SLF4J_REQUEST_LOG =
server -> server.setRequestLog(new Slf4jRequestLog());
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof JettyEmbeddedServletContainerFactory) {
((JettyEmbeddedServletContainerFactory) container)
.addServerCustomizers(USE_SLF4J_REQUEST_LOG);
} else {
throw new IllegalArgumentException(
"Expected a Jetty container factory but encountered " + container.getClass());
}
}
}
Instead of Jetty's Slf4jRequestLog you can implement a specific RequestLog. See http://www.eclipse.org/jetty/documentation/current/configuring-jetty-request-logs.html
Coming a bit late, but I experienced the same problem and the solution was to wrap the ServletHandler already created by boot in the RequestLogHandler like this:
#Component
public class EmbededJettyConfig implements EmbeddedServletContainerCustomizer {
/*
* (non-Javadoc)
*
* #see org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer # customize
*/
#Override
public void customize(final ConfigurableEmbeddedServletContainer container) {
// checks whether the container is Jetty
if (container instanceof JettyEmbeddedServletContainerFactory) {
((JettyEmbeddedServletContainerFactory) container)
.addServerCustomizers(jettyServerCustomizer());
}
}
#Bean
public JettyServerCustomizer jettyServerCustomizer() {
return new JettyServerCustomizer() {
/*
* (non-Javadoc)
*
* #see org.springframework.boot.context.embedded.jetty.JettyServerCustomizer #
* customize
*/
#Override
public void customize(final Server server) {
NCSARequestLog requestLog = new NCSARequestLog("logs/requests.log");
requestLog.setExtended(false);
RequestLogHandler requestLogHandler = new RequestLogHandler();
requestLogHandler.setRequestLog(requestLog);
requestLogHandler.setHandler(server.getHandler());
server.setHandler(requestLogHandler);
}
};
}
This doesn't work with Spring Boot version 1.1.9 because JettyEmbeddedServletContainer handlers only JettyEmbeddedWebAppContext (a handler) or HandlerWrapper. I think there is no way to set multiple handler to embedded Jetty in Spring Boot because of this.
Code snippet from org.springframework.boot.context.embedded.jettyJettyEmbeddedServletContainer.
#Override
public void start() throws EmbeddedServletContainerException {
this.server.setConnectors(this.connectors);
if (!this.autoStart) {
return;
}
try {
this.server.start();
for (Handler handler : this.server.getHandlers()) {
handleDeferredInitialize(handler);
}
Connector[] connectors = this.server.getConnectors();
for (Connector connector : connectors) {
connector.start();
this.logger.info("Jetty started on port: " + getLocalPort(connector));
}
}
catch (Exception ex) {
throw new EmbeddedServletContainerException(
"Unable to start embedded Jetty servlet container", ex);
}
}
private void handleDeferredInitialize(Handler handler) throws Exception {
if (handler instanceof JettyEmbeddedWebAppContext) {
((JettyEmbeddedWebAppContext) handler).deferredInitialize();
}
else if (handler instanceof HandlerWrapper) {
handleDeferredInitialize(((HandlerWrapper) handler).getHandler());
}
}

How to conduct a lookup of an EJB from a servlet context?

I have a POJO that was instantiated from a servlet. I need to make a lookup of an EJB within this POJO, either CDI or JNDI. My JEE container is TomEE 1.6.0.
My question is this: need the EJB have remote interface? Because if I instantiate it directly from the servlet by #EJB the remote interface does not need...
Just see this simple example which always throws NameNotFoundException.
#Stateless
public class MyEJB
{
public String sayHello()
{
return "Hello";
}
}
The next servlet try yo lookup MyEJB:
#WebServlet("/myServlet")
public class MyServlet extends HttpServlet
{
private static final long serialVersionUID=1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException
{
try
{
Context ctx = new InitialContext();
MyEJB ejb = (MyEJB) ctx.lookup("MyEJB");
System.out.println(ejb.sayHello());
}
catch(Exception ex)
{
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
}
The line: MyEJB ejb = (MyEJB) ctx.lookup("MyEJB"); always throws NameNotFoundException. But if I use #EJB it work fine. But I need do the lookup in JNDI mode because finally I will instantiate this EJB within a POJO.
So, why fails this lookup ?
This has nothing to do with remote interfaces.
When you declare MyEJB in servlet using #EJB it works, because servlet is container-managed - your TomEE server instantiates servlet object. However declaration of MyEJB in MyPojo (also using #EJB) won't work, because MyPojo is not container-managed - it is created using new MyPojo(), not by TomEE server.
You could for example make MyPojo another EJB (using #Stateless) and inject it using #EJB to the servlet - not by creating new MyPojo().
I can solve this problem by myself by instrospecting the JNDI tree with this simple class:
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
public class JndiInstrospector
{
public static void print()
{
try
{
Context ctx = new InitialContext();
String n = ctx.getNameInNamespace();
_print(n);
}
catch(Exception ex)
{
ex.printStackTrace();
throw new RuntimeException();
}
}
private static void _print(String name) throws Exception
{
try
{
System.out.println("Name in manespace: "+name);
Context ctx = new InitialContext();
NamingEnumeration<Binding> list = ctx.listBindings(name);
while( list.hasMoreElements() )
{
Binding b = list.nextElement();
String s = b.getName();
_print(name+"/"+s);
}
}
catch(Exception ex)
{
// ignore
}
}
}
Finally, the EJB That I was espected to lookup was found at:
"java:/openejb/local/FacadeBeanLocalBean", where FacadadeBean is the name of my EJB (stateless session bean).

Resources