How to enable JSP in a Jetty server in a JAR file? - java

tl;dr: How can I enable JSP support for this project (which you can also download as a zip file)?
I'm trying to create a simple "hello world" web application using Jetty, and I'm pretty happy with what I have so far. The important files are:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.happycoding</groupId>
<artifactId>app-engine-hello-world</artifactId>
<version>1</version>
<properties>
<!-- App Engine currently supports Java 11 -->
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jetty.version>9.4.31.v20200723</jetty.version>
<!-- Project-specific properties -->
<exec.mainClass>io.happycoding.ServerMain</exec.mainClass>
<googleCloudProjectId>YOUR_PROJECT_ID_HERE</googleCloudProjectId>
</properties>
<dependencies>
<!-- Java Servlets API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!-- Jetty -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>${jetty.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Copy static resources like html files into the output jar file. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>copy-web-resources</id>
<phase>compile</phase>
<goals><goal>copy-resources</goal></goals>
<configuration>
<outputDirectory>
${project.build.directory}/classes/META-INF/resources
</outputDirectory>
<resources>
<resource><directory>./src/main/webapp</directory></resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<!-- Package everything into a single executable jar file. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${exec.mainClass}</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<!-- App Engine plugin for deploying to the live site. -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>2.2.0</version>
<configuration>
<projectId>${googleCloudProjectId}</projectId>
<version>1</version>
</configuration>
</plugin>
</plugins>
</build>
</project>
ServerMain.java
package io.happycoding;
import java.net.URL;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
/**
* Starts up the server, including a DefaultServlet that handles static files,
* and any servlet classes annotated with the #WebServlet annotation.
*/
public class ServerMain {
public static void main(String[] args) throws Exception {
// Create a server that listens on port 8080.
Server server = new Server(8080);
WebAppContext webAppContext = new WebAppContext();
server.setHandler(webAppContext);
// Load static content from inside the jar file.
URL webAppDir =
ServerMain.class.getClassLoader().getResource("META-INF/resources");
webAppContext.setResourceBase(webAppDir.toURI().toString());
// Enable annotations so the server sees classes annotated with #WebServlet.
webAppContext.setConfigurations(new Configuration[]{
new AnnotationConfiguration(),
new WebInfConfiguration(),
});
// Look for annotations in the classes directory (dev server) and in the
// jar file (live server)
webAppContext.setAttribute(
"org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
".*/target/classes/|.*\\.jar");
// Handle static resources, e.g. html files.
webAppContext.addServlet(DefaultServlet.class, "/");
// Start the server! 🚀
server.start();
System.out.println("Server started!");
// Keep the main thread alive while the server is running.
server.join();
}
}
index.jsp
<%# page import="java.util.Date" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Google Cloud Hello World</title>
</head>
<body>
<h1>Google Cloud Hello World</h1>
<p>This is a sample HTML file. Click here to see content served from a servlet.</p>
<p>Learn more at HappyCoding.io.</p>
<p>The current time is: <%= new Date().toString() %></p>
</body>
</html>
The server starts up and renders HTML perfectly. Servlets also work. But when I try to use JSP like above, I see the JSP code rendered in the HTML instead of being parsed as Java.
I've tried googling, but every tutorial I've found works by creating a separate WAR file. My server is launched from a JAR file, and I'm trying to keep the code as simple as possible, so I'm trying to avoid using a separate WAR file if possible.
Is there a small change I can make to my pom.xml file and my ServerMain.java file to enable JSP?

To enable JSP support in the embedded Jetty server you need to do two things, modify your project pom.xml file to include the necessary dependencies, and configure the JettyJspServlet and related Jetty stuff in your ServerMain class.
First, include the following dependencies on your project pom.xml. They will provide support for JSP an JSTL:
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jstl</artifactId>
<version>${jetty.version}</version>
<type>pom</type>
</dependency>
Then, use this modified version of the ServerMain class:
package io.happycoding;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import org.apache.tomcat.util.scan.StandardJarScanner;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.apache.jsp.JettyJasperInitializer;
import org.eclipse.jetty.jsp.JettyJspServlet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
/**
* Starts up the server, including a DefaultServlet that handles static files,
* and any servlet classes annotated with the #WebServlet annotation.
*/
public class ServerMain {
public static void main(String[] args) throws Exception {
// Create a server that listens on port 8080.
Server server = new Server(8080);
WebAppContext webAppContext = new WebAppContext();
server.setHandler(webAppContext);
// Load static content from inside the jar file.
URL webAppDir =
ServerMain.class.getClassLoader().getResource("META-INF/resources");
webAppContext.setResourceBase(webAppDir.toURI().toString());
// Enable annotations so the server sees classes annotated with #WebServlet.
webAppContext.setConfigurations(new Configuration[]{
new AnnotationConfiguration(),
new WebInfConfiguration(),
});
// Look for annotations in the classes directory (dev server) and in the
// jar file (live server)
webAppContext.setAttribute(
"org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
".*/target/classes/|.*\\.jar");
// Handle static resources, e.g. html files.
webAppContext.addServlet(DefaultServlet.class, "/");
// Configure JSP support.
enableEmbeddedJspSupport(webAppContext);
// Start the server! 🚀
server.start();
System.out.println("Server started!");
// Keep the main thread alive while the server is running.
server.join();
}
/**
* Setup JSP Support for ServletContextHandlers.
* <p>
* NOTE: This is not required or appropriate if using a WebAppContext.
* </p>
*
* #param servletContextHandler the ServletContextHandler to configure
* #throws IOException if unable to configure
*/
private static void enableEmbeddedJspSupport(ServletContextHandler servletContextHandler) throws IOException
{
// Establish Scratch directory for the servlet context (used by JSP compilation)
File tempDir = new File(System.getProperty("java.io.tmpdir"));
File scratchDir = new File(tempDir.toString(), "embedded-jetty-jsp");
if (!scratchDir.exists())
{
if (!scratchDir.mkdirs())
{
throw new IOException("Unable to create scratch directory: " + scratchDir);
}
}
servletContextHandler.setAttribute("javax.servlet.context.tempdir", scratchDir);
// Set Classloader of Context to be sane (needed for JSTL)
// JSP requires a non-System classloader, this simply wraps the
// embedded System classloader in a way that makes it suitable
// for JSP to use
ClassLoader jspClassLoader = new URLClassLoader(new URL[0], ServerMain.class.getClassLoader());
servletContextHandler.setClassLoader(jspClassLoader);
// Manually call JettyJasperInitializer on context startup
servletContextHandler.addBean(new JspStarter(servletContextHandler));
// Create / Register JSP Servlet (must be named "jsp" per spec)
ServletHolder holderJsp = new ServletHolder("jsp", JettyJspServlet.class);
holderJsp.setInitOrder(0);
holderJsp.setInitParameter("logVerbosityLevel", "DEBUG");
holderJsp.setInitParameter("fork", "false");
holderJsp.setInitParameter("xpoweredBy", "false");
holderJsp.setInitParameter("compilerTargetVM", "1.8");
holderJsp.setInitParameter("compilerSourceVM", "1.8");
holderJsp.setInitParameter("keepgenerated", "true");
servletContextHandler.addServlet(holderJsp, "*.jsp");
}
/**
* JspStarter for embedded ServletContextHandlers
*
* This is added as a bean that is a jetty LifeCycle on the ServletContextHandler.
* This bean's doStart method will be called as the ServletContextHandler starts,
* and will call the ServletContainerInitializer for the jsp engine.
*
*/
public static class JspStarter extends AbstractLifeCycle implements ServletContextHandler.ServletContainerInitializerCaller
{
JettyJasperInitializer sci;
ServletContextHandler context;
public JspStarter (ServletContextHandler context)
{
this.sci = new JettyJasperInitializer();
this.context = context;
this.context.setAttribute("org.apache.tomcat.JarScanner", new StandardJarScanner());
}
#Override
protected void doStart() throws Exception
{
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
try
{
sci.onStartup(null, context.getServletContext());
super.doStart();
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
}
}
}
As you can see, the principal difference with the previous code is the inclusion of the method call to a new method enableEmbeddedJspSupport while configuring the embedded Jetty server in main.
The enableEmbeddedJspSupport method and the companion class JspStarter are an adapted version of the code you can find in the Main class in this Github repository.

Related

Deploy servlet with IntelliJ IDEA to local Tomcat server

Trying to deploy simple servlet to Tomcat server. After select Run Tomcat... I'm redirected to http://localhost:8080/hi_war_exploded/ with webpage with one word - $END$. Logfiles reports no error.
I was expecting to see hw folder with my application in tc9\webapps, but found nothing.
What does the $END$ means? Where is my application on TomCat server? How to put my servlet to TomCat server?
Servlet:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
// Extend HttpServlet class
public class hw extends HttpServlet {
private String message;
public void init() throws ServletException {
// Do required initialization
message = "Hello World";
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Set response content type
response.setContentType("text/html");
// Actual logic goes here.
PrintWriter out = response.getWriter();
out.println("<h1>" + message + "</h1>");
}
public void destroy() {
// do nothing.
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>hw</servlet-name>
<servlet-class>hw</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hw</servlet-name>
<url-pattern>/hw</url-pattern>
</servlet-mapping>
</web-app>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Hello</groupId>
<artifactId>hw</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>LATEST</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
IntelliJ IDEA doesn't copy the webapp files into TOMCAT\webapps.
It modifies Tomcat configuration in CATALINA_BASE and deploys the artifact directly from its output directory to avoid copying the files which can take a lot of extra time, especially for the large projects.
hi_war_exploded is the context configured in Tomcat Run/Debug configuration, Deployment tab.
In the root of this context you have the default index.jsp page generated by IntelliJ IDEA on project creation.
When you open http://localhost:8080/hi_war_exploded/ URL, Tomcat serves index.jsp from the Web Resource root of your application.
$END$ is a part of the the new JSP file template. When you create a new JSP file in a project, cursor is placed at this location.
When the project wizard generates the Web Application project and places index.jsp file from the template, it doesn't expand the $END$ macro, so it appears in the JSP file. It's actually a known bug in IntelliJ IDEA.
Your servlet is available at http://localhost:8080/hi_war_exploded/hw URL.
To make it available at http://localhost:8080/hw URL instead you need to change the Application context to / as shown on this screenshot:

Spring routing gets 404 error because of invalid routing

For an assignment i'm practising Java's Spring MVC for creating a web application. I've build this whole project in the IDE Intellij Ultimate 2016.2.5.
I've created a Maven project for this, imported the correct and asked dependencies for this and build it.
The IDE build the following directory structure:
├───src
│ └───bas
│ └───animalkingdom
│ ├───config
│ ├───controllers
├───test
│ └───bas
│ └───animalkingdom
└───web
├───META-INF
├───resources
└───WEB-INF
└───pages
The config package is where my configuration class is, extending from WebMvcConfigurerAdapter:
package bas.animalkingdom.config;
import ...
#Configuration
#ComponentScan("bas.animalkingdom")
#EnableWebMvc
public class Config extends WebMvcConfigurerAdapter {
#Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/pages/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
I thought the #ComponentScan had to point to the main source directory where all your source files are.
I was told that I also need a class extending from the WebApplicationInitializer. I got this one from my school
package bas.animalkingdom.config;
import ...
public class WebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(Config.class);
ctx.setServletContext(servletContext);
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
This one is also in the config package.
The Config class is set as the Spring Application Context in the project structure settings in my IDE.
In the root directory is the web folder. In the folder WEB-INF is a empty web.xml file, which I was told I didn't need because the settings would be loaded via the configuration class. It looks like this:
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
</web-app>
In the root directory of the web folder is a index.jsp file.
In the bas.animalkingdom.controllers package are my controllers. For testing purposing, I only created one:
package bas.animalkingdom.controllers;
import ...
#Controller("AnimalC")
public class AnimalController {
#RequestMapping(value = "/animals", method = RequestMethod.GET)
public String getAnimals(ModelMap modelMap) {
Animal animal = new AfricanElephant(new Male(), "Body Covering", "Ename", " acolor", 123, 321);
modelMap.put("animal", animal);
return "animals";
}
}
With this controller I expected that I can go to the localhost/animals URL, and that it would load up the animals.jsp file located in my web\WEB-INF\pages\ package.
My code has no compile errors in it.
When I run my TomCat server, and open my browser to go to the localhost with the corresponding host, the index.jsp file just loads with no problem. This file is located in the web\ package.
When I go to the localhost:(port)/animals, I just get a 404 page, with the message that the page could not be found.
What does cause this? I've defined the controller which sets that route right?
Also, when looking up other Spring MVC tutorials, they all use a different packaging, does this work as well?
All your java classes and jsps are fine, problem is your module structure. There are basically 2 approaches to your problem, the conventional (recommended) and unconventional way. Let's start with the unconventional way, which is quicker and not recommended:
A. The Unconventional:
Add to your maven-war-plugin, the warSourceDirectory tag as shown below:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<warSourceDirectory>${basedir}/web</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
The disadvantage of using this approach is that other plugins may give you unexpected problems down the road, for example the maven-jetty-plugin will not run out of the box with this approach. It defaults to looking in src/main/webapp, although it is configurable. Maven life is easier if you follow the conventional approach.
B. Conventional Approach:
The solution lies in having a conventional maven structure for your module, make sure to do the following:
Delete Your web.xml file. If maven package failed because of missing web.xml file, add the below plugin in the build section of your pom.xml :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
Create folder structure /src/main/java and put all your java packages into this java directory. If your packages are not formatted properly, you can right click folder java and go to Mark Directory as -> Source Root.
Create folder structure /src/test/java and put all your test packages in the java directory.
Create a folder structure /src/main/webapp and put all the contents of folder web into the webapp directory.
After doing this you can test your application. You can try using jetty plugin to deploy your web app with the below configuration:
<build>
<finalName>spring-mvc</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.2.11.v20150529</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<webApp>
<contextPath>/</contextPath>
</webApp>
<httpConnector>
<port>8080</port>
</httpConnector>
</configuration>
</plugin>
</plugins>
</build>

why can't spring boot angularjs gateway app read from ui app?

I am learning about spring's scalability features using the tutorial at the following link. Specifically, part 6 of the tutorial uses a gateway app to regulate access to apps running on other servers.
I have followed the steps below precisely, but when I start all three apps and then type in localhost:8080/ui into my web browser, all I get is the word "Greeting" with no id or hello world, and with no css.
When I open the developer tools for the request in Firefox, I see that the GET requests for css and js resources are getting 404 errors pointing to urls like http://localhost:8080/js/hello.js instead of pointing to http://localhost:8080/ui/js/hello.js, as the test section of the tutorial suggests. How can I change this so that the greeting displays in the browser?
Here is what I have done step by step, following the tutorial's step six by first recreating the ui starting point from part one and the resource starting point from part three:
Create the UI sample starter app
# mkdir ui
# chmod -R 777 ui
# cd ui
# curl https://start.spring.io/starter.tgz -d style=web -d style=security -d name=ui | tar -xzvf -
Eclipse > File > Import > Existing Maven Projects > Navigate to ui folder > Finish
Create index.html in src/main/resources/static and add the following:
<!doctype html>
<html>
<head>
<title>Hello AngularJS</title>
<link href="css/angular-bootstrap.css" rel="stylesheet">
<style type="text/css">
[ng\:cloak], [ng-cloak], .ng-cloak {
display: none !important;
}
</style>
</head>
<body ng-app="hello">
<div class="container">
<h1>Greeting</h1>
<div ng-controller="home" ng-cloak class="ng-cloak">
<p>The ID is {{greeting.id}}</p>
<p>The content is {{greeting.content}}</p>
</div>
</div>
<script src="js/angular-bootstrap.js" type="text/javascript"></script>
<script src="js/hello.js"></script>
</body>
</html>
Add the following lines to src/main/resources/application.properties:
security.user.password=some.password
server.port: 8081
security.sessions: NEVER // The "security.sessions" setting means that Spring Security will accept cookies as authentication tokens but won’t create them unless they already exist.
Add the following to pom.xml in order to download and integrate angular and bootstrap etc using wro4j:
<build>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
<resource>
<directory>${project.build.directory}/generated-resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<!-- Serves *only* to filter the wro.xml so it can get an absolute
path for the project -->
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/wro</outputDirectory>
<resources>
<resource>
<directory>src/main/wro</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>ro.isdc.wro4j</groupId>
<artifactId>wro4j-maven-plugin</artifactId>
<version>1.7.6</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<configuration>
<wroManagerFactory>ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory</wroManagerFactory>
<cssDestinationFolder>${project.build.directory}/generated-resources/static/css</cssDestinationFolder>
<jsDestinationFolder>${project.build.directory}/generated-resources/static/js</jsDestinationFolder>
<wroFile>${project.build.directory}/wro/wro.xml</wroFile>
<extraConfigFile>${basedir}/src/main/wro/wro.properties</extraConfigFile>
<contextFolder>${basedir}/src/main/wro</contextFolder>
</configuration>
<dependencies>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>angularjs</artifactId>
<version>1.3.8</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
Eclipse > File > New > Source Folder > (create src/main/wro)
Add the following to src/main/wro/wro.properties (will compile css from less and minify javascript):
preProcessors=lessCssImport
postProcessors=less4j,jsMin
Add the following to src/main/wro/wro.xml (declares a single group angular-bootstrap with references to css, js, and main.less):
<groups xmlns="http://www.isdc.ro/wro">
<group name="angular-bootstrap">
<css>webjar:bootstrap/3.2.0/less/bootstrap.less</css>
<css>file:${project.basedir}/src/main/wro/main.less</css>
<js>webjar:jquery/2.1.1/jquery.min.js</js>
<js>webjar:angularjs/1.3.8/angular.min.js</js>
</group>
</groups>
Create src/main/wro/main.less and leave it empty for now. main.less can be used to customize bootstrap defaults.
Create src/main/resources/static/js/hello.js and add the following:
angular.module('hello', [])
.controller('home', function($scope, $http) {
$http.get('/resource/').success(function(data) {
$scope.greeting = data;
})
});
Change com.example.UiApplication.java to:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
#SpringBootApplication
#EnableRedisHttpSession
public class UiApplication {
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
}
// now add the following to the ui app's pom.xml:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
Create separate resource server
// navigate to root of workspace
# cd /home/username/someworkspace
# mkdir resource
# chmod -R 777 resource
# cd resource
# curl https://start.spring.io/starter.tgz -d style=web -d name=resource -d language=java | tar -xzvf -
Eclipse > Import > Existing Maven Project (navigate to resource folder just created)
// add the following to pom.xml in the resource app:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
// change com.example.ResourceApplication.java to the following:
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RestController
#EnableRedisHttpSession
public class ResourceApplication {
#RequestMapping("/resource")
public Map<String,Object> home() {
Map<String,Object> model = new HashMap<String,Object>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello World");
return model;
}
public static void main(String[] args) {
SpringApplication.run(ResourceApplication.class, args);
}
}
// Add the following to src/main/resources/application.properties:
server.port: 9000
security.sessions: NEVER
create the gateway app
navigate terminal to root of workspace, then
# cd /home/username/someworkspace
# mkdir gateway
# chmod -R 777 gateway
# cd gateway
# curl https://cloud-start.spring.io/starter.tgz -d style=web -d style=security -d style=cloud-zuul -d name=gateway -d style=redis | tar -xzvf -
Eclipse > File > Import > Existing Maven Project > (navigate to the gateway directory)
// change GatewayApplication.java to:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
#SpringBootApplication
#EnableRedisHttpSession
#EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
// change src/main/resources/application.properties to:
zuul.routes.ui.url: http://localhost:8081
zuul.routes.resource.url: http://localhost:9000
security.user.password: password
security.sessions: ALWAYS
.
Start and test the apps:
# cd /home/username/someworkspace/ui
# mvn spring-boot:run
Then open a second terminal and type
# cd /home/username/someworkspace/resource
# mvn spring-boot:run
Then open a third terminal and type:
# cd /home/username/someworkspace/gateway
# mvn spring-boot: run
// test the app by putting localhost:8080/ui in the browser
Note that only the word "Greeting" shows up in the browser at localhost:8080/ui, and that there is no id and no content. Also, the firefox developer tools show 404 errors for resources like http://localhost:8080/js/hello.js which should instead be http://localhost:8080/ui/js/hello.js
However, when I type localhost:8081 in the browser, I get the css-styled "Greeting" followed by "The ID is" and "The content is", but no dynamic content from the resource server. The firefox developer tools for the localhost:8081 request give a 404 for http://localhost:8081/resource/.
Note that, to test any changes to the above, you just type control C in the appropriate console, then type kill $(lsof -t -i:8080) or 8081 or 9000, and then mvn spring-boot:run
So what changes to I make to the code above to get the id and greeting to load through the gateway?
Looks like an issue in your html page. You should make sure that links are correct. What Zuul in essence does is mapping urls from the gateway to the backend. To make your example work you have to either change the url in your angular app to /resource/resource or change the controller in the resource app to /. Also make sure that all of your apps have spring-boot-starter-security as a dependency to allow sharing of the sec context. Or disable security completely to debug your issue first.

ServletException: No servlet class has been specified

I have what should be an extremely simple "Hello World" servlet, but I cannot get it to work. I am using Eclipse, Tomcat 8, Java 7, and Servlet 3.1.
I have looked at many tutorials and questions, but they have not completely helped. Most tutorials I have seen talk about creating servlets by extending HttpServlet. I got those to work. Now I'd like to try the cleaner annotation approach.
I've been referring to this tutorial, but it isn't quite complete and seems to have some incorrect or incomplete examples:
Packaging and Deploying RESTful Web Services
Why is com.testing.service.MyApplication not being loaded?
Any help on getting this thing to run would be immensely appreciated!
Here are my files:
MyApplication.java
package com.testing.service;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("app")
public class MyApplication extends Application {
public Set<Class<?>> getClasses() {
Set<Class<?>> s = new HashSet<Class<?>>();
s.add(HelloWorldResource.class);
return s;
}
}
HelloWorldResource.java
package com.testing.service;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
#Path("/helloworld")
public class HelloWorldResource {
#SuppressWarnings("unused")
private static final long serialVersionUID = 1L;
#GET
#Produces("text/plain")
public String sayHello() {
return "Hello World!";
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<display-name>service</display-name>
<servlet>
<servlet-name>com.testing.service.MyApplication</servlet-name>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.testing.service.MyApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
</web-app>
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.testing</groupId>
<artifactId>service</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<name>Rest Test</name>
<dependencies>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Whenever I run Tomcat, it displays the following error:
INFO: Marking servlet com.testing.service.MyApplication as unavailable
Feb 05, 2015 3:28:55 PM org.apache.catalina.core.StandardContext loadOnStartup
SEVERE: Servlet /service threw load() exception
javax.servlet.ServletException: No servlet class has been specified for servlet com.testing.service.MyApplication
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1099)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1041)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4944)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5230)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1399)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
I found the answer in the comments of a related question. The answer was provided by Alvin Thompson. Unfortunately, I don't have enough reputation to up-vote your answer.
If you're using a standard Tomcat install (or some other servlet
container), you need to include a REST implementation since Tomcat
doesn't come with one. If you're using Maven, add this to the
dependencies section:
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.bundles</groupId>
<artifactId>jaxrs-ri</artifactId>
<version>2.13</version>
</dependency>
...
</dependencies>
Then just add an application config class to your project. If you
don't have any special configuration needs aside from setting the
context path for the rest services, the class can be empty. Once this
class is added, you don't need to configure anything in web.xml (or
have one at all).
Originally posted here:
How to set up JAX-RS Application using annotations only (no web.xml)?

Exception Deploying EJB Jar to Glassfish

This is my first foray into using JMS. I have a successfully created/deployed a war file that contains a servlet that I can use to upload files. When a file is uploaded it sends a message to a JMS queue. Next I wrote a listener to retrieve the uploaded messages from the queue, but when I try to deploy it, I get this error:
SEVERE: Invalid ejb jar [file-listener-ejb-1.0.jar]: it contains zero ejb.
Note:
1. A valid ejb jar requires at least one session, entity (1.x/2.x style), or message- driven bean.
2. EJB3+ entity beans (#Entity) are POJOs and please package them as library jar.
3. If the jar file contains valid EJBs which are annotated with EJB component level annotations (#Stateless, #Stateful, #MessageDriven, #Singleton), please check server.log to see whether the annotations were processed properly.
at com.sun.enterprise.deployment.util.EjbBundleValidator.accept(EjbBundleValidator.java:76)
...<snip>...
It's a very simple project with one class, built using Maven. The class looks like this:
package my.package;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#MessageDriven(mappedName = "jms/FileUploadedQueue", activationConfig = {
#ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") })
public class FileListener implements MessageListener
{
private static Logger log = LoggerFactory.getLogger(FileListener.class);
public FileListener()
{
// empty constructor
}
public void onMessage(Message message)
{
try
{
log.info("Received message: " + ((TextMessage)message).getText());
}
catch (JMSException ex)
{
String error = "Received error code '"
+ ex.getErrorCode()
+ "' retrieving message from queue jms/FileUploadedQueue.";
Exception linkedEx = ex.getLinkedException();
if (linkedEx != null)
{
log.error(error += "Linked exception: ", linkedEx);
}
else
{
log.error(error, linkedEx);
}
}
}
}
My pom.xml looks like this:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>my.package</groupId>
<artifactId>uploaded-file-listener</artifactId>
<version>1.0</version>
<packaging>ejb</packaging>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArguments>
<bootclasspath>${settings.localRepository}/javax/javaee-endorsed-api/6.0/javaee-endorsed-api-6.0.jar${path.separator}${sun.boot.class.path}</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<version>2.3</version>
<configuration>
<ejbVersion>3.1</ejbVersion>
</configuration>
</plugin>
</plugins>
</build>
</project>
This builds a jar file which when I try to deploy to my Glassfish 3.1 server (via the admin console) results in the above error.
Since I have the #MessageDriven annotation on my class, I'm not sure what I'm doing wrong. Unfortunately, the server.log file does not contain any more details about the error.
Should I be packaging the jar in an ear and deploying that?
----------EDIT----------
I created an ear which includes the ejb jar, and I get the same error when I deploy the ear to Glassfish. So, I think it must be something to do with the annotation. However, I've looked at multiple examples/tutorials and I can't see what's wrong.
Any insights/suggestions would be most welcome!!
----------EDIT TWO----------
Contents of MANIFEST.MF files:
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: <name>
Build-Jdk: 1.6.0_24
Contents of application.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC
"-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN"
"http://java.sun.com/dtd/application_1_3.dtd">
<application>
<display-name>FileListener-ear</display-name>
<module>
<ejb>file-listener-ejb-1.0.jar</ejb>
</module>
</application>
----------EDIT THREE----------
Contents of ejb-jar file:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
version="3.1">
<display-name>FileListener</display-name>
<enterprise-beans>
<message-driven>
<ejb-name>FileListener</ejb-name>
<ejb-class>my.package.FileListener</ejb-class>
<transaction-type>Container</transaction-type>
</message-driven>
</enterprise-beans>
</ejb-jar>
Does your jar file contain an ejb-jar.xml file? If it was missing, then it could explain why the whole thing explodes upon deploy
Deploying on GF 4.1 our EAR that consists of an EJB and WAR and several JAR projects suddenly needs an application.xml.
Many deploys before it was not necessary.
Did you try to check the compatibility check box while deployment and see if deploys fine. We also got such error preventing the deployment and was resolved by checking the compatibility check box while deployment.

Categories

Resources