I have a multi-module project with two projects: backend and client. The backend is a normal Spring Boot Rest API, nothing special. The client module is just a Java Library using the Rest API.
The backend has packaging of "war" as the backend as it uses JSPs, too and needs to be deployed to a servlet container. The backend is still easily testable with #SpringBootTest.
Now I want to have some integration tests inside the client module using the backend module as a sandbox server.
To use all the backend classes in the client module I added
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<attachClasses>true</attachClasses>
</configuration>
</plugin>
and configured the backend as a test dependency in client with classes
In my client/src/test/java I have a helper class which starts up the backend module
#Configuration
public class SandboxServer {
#Bean
public ConfigurableApplicationContext backend() {
return
new SpringApplicationBuilder(BackendApplication.class)
.sources(SandboxServerConfig.class)
.run("spring.profiles.active=sandbox")
}
}
The profile "sandbox" is used to setup a test database etc. But I had more problems. First problem was regarding the document root, so I configured it:
public class SandboxServerConfig
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Override
public void customize(TomcatServletWebServerFactory factory) {
factory.setDocumentRoot(new File("../backend/src/main/webapp"));
}
}
But it still does not work as Spring is not picking up backend/src/main/resources/application.properties
That might be correct as it is not in the root classpath of the client module.
So it does not really work. I guess it is not possible to just start up the sibling module in an Integration test.
How can I achieve to start up the sibling spring boot module for integration testing? What is the best practice for szenarios like this?
You can override the application.properties location using TestPropertySource like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = BlaApplication.class)
#TestPropertySource(locations="/path/to/backend/src/main/resources/application.properties")
public class ExampleApplicationTests {
}
I found a much more solid solution. In my sibling Project "frontend" I have a Component which is starting up the backend server in integration mode if and only if it is not already running.
Benefits:
The real WAR is tested
You can start the WAR before in your IDE and let the tests run fast
If you run it with maven it is started up before all tests only once
No build configuration needed (like pre-integration in maven)
process is seperated from Junit runtime so no hassle with complex setups.
Drawbacks:
You need to build the package before you can run any integration test in the frontend. But hey, you should build your package before you test it. That's what integration test is all about.
And here is my SandboxServerProcess.class.
import org.springframework.stereotype.Component;
import javax.annotation.*;
import javax.annotation.*;
import java.io.*;
import java.net.*;
import java.util.*;
#Component
#Profile("integration")
public class SandboxServerProcess {
private static final String WAR = "../backend/target/backend.war";
private final static int PORT = 8081;
private boolean startedByMe;
#PostConstruct
public void start() throws Exception {
if (isStarted()) {
return;
}
testWarExists();
packagedWar("start");
if (waitForStartup()) {
startedByMe = true;
return;
}
throw new RuntimeException("Sandbox Server not started");
}
private void testWarExists() {
File file = new File(WAR);
if (!file.exists()) {
throw new RuntimeException("WAR does not exist:" + file.getAbsolutePath());
}
}
#PreDestroy
public void stop() throws IOException {
if (startedByMe) {
packagedWar("stop");
}
}
private void packagedWar(String command) throws IOException {
ProcessBuilder builder = new ProcessBuilder();
builder.environment().put("MODE", "service");
builder.environment().put("SPRING_PROFILES_ACTIVE", "integration");
builder.environment().put("APP_NAME", "backend");
builder.environment().put("PID_FOLDER", "./");
builder.environment().put("LOG_FOLDER", "./");
List<String> commands = new ArrayList<>();
commands.add(WAR);
commands.add(command);
builder.command(commands);
builder.inheritIO();
builder.redirectErrorStream(true);
builder.start();
}
private boolean isStarted() {
try {
Socket socket = new Socket();
InetSocketAddress sa = new InetSocketAddress("localhost", PORT);
socket.connect(sa, 500);
logger.warn("SandboxServer is started");
return true;
} catch (IOException e) {
return false;
}
}
private boolean waitForStartup() throws InterruptedException {
for (int i = 1; i < 30; i++) {
if (isStarted()) {
return true;
}
logger.warn("SandboxServer not yet ready, tries: " + i);
Thread.sleep(1000);
}
return false;
}
}
I need deply WAR on Jetty server, but war can't deployed, what is my mistake?
public class JettyJmxServer {
private static final int SERVER_PORT = 8080;
private static final String PATH_TO_WAR = "src/main/webapp/";
public static void main(String[] args) {
try {
Server server = new Server(SERVER_PORT);
WebAppContext root = new WebAppContext();
root.setWar(PATH_TO_WAR);
root.setParentLoaderPriority(true);
server.setHandler(root);
server.start();
server.join();
} catch (Exception ex){
System.out.println(ex);
}
}
}
I see WAR on localhost:8080, but when i address to localhost:8080/jolokia-war-1.5.0 nothing found.
When I use ready Jetty Server, and place WAR to folder 'webapps' everything works perfectly.
I am using Grizzly to serve my REST service which can have multiple "modules". I'd like to be able to use the same base URL for the service and for static content so I can access all these urls:
http://host:port/index.html
http://host:port/module1/index.html
http://host:port/module1/resource
http://host:port/module2/index.html
http://host:port/module2/resource
The code I'm trying to set this up with looks like this:
private HttpServer createServer(String host, int port, ResourceConfig config)
{
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create("http://" + host + ":" + port + "/"), config, false);
HttpHandler httpHandler = new CLStaticHttpHandler(HttpServer.class.getClassLoader(), "docs/");
server.getServerConfiguration().addHttpHandler(httpHandler, "/");
return server;
}
With this code, I am only able to see the html pages and I get a "Resource identified by path does not exist" response when I try to get my resources.
When I comment out the code to add the HttpHandler, then I am able to access my resources (but don't have the docs of course).
What do I need to do to access both my resources and my static content?
I ended up writing a service to handle static resources myself. I decided to serve my files from the file system, but this approach would also work for serving them from a jar - you'd just have to get the file as a resource instead of creating the File directly.
#Path("/")
public class StaticService
{
#GET
#Path("/{docPath:.*}.{ext}")
public Response getHtml(#PathParam("docPath") String docPath, #PathParam("ext") String ext, #HeaderParam("accept") String accept)
{
File file = new File(cleanDocPath(docPath) + "." + ext);
return Response.ok(file).build();
}
#GET
#Path("{docPath:.*}")
public Response getFolder(#PathParam("docPath") String docPath)
{
File file = null;
if ("".equals(docPath) || "/".equals(docPath))
{
file = new File("index.html");
}
else
{
file = new File(cleanDocPath(docPath) + "/index.html");
}
return Response.ok(file).build();
}
private String cleanDocPath(String docPath)
{
if (docPath.startsWith("/"))
{
return docPath.substring(1);
}
else
{
return docPath;
}
}
}
One thing you can do is run Grizzly as a servlet container. That way you can run Jersey as servlet filter, and add a default servlet to handle the static content. For example
public class Main {
public static HttpServer createServer() {
WebappContext context = new WebappContext("GrizzlyContext", "");
createJerseyFilter(context);
createDefaultServlet(context);
HttpServer server = GrizzlyHttpServerFactory
.createHttpServer(URI.create("http://localhost:8080/"));
context.deploy(server);
return server;
}
private static void createJerseyFilter(WebappContext context) {
ResourceConfig rc = new ResourceConfig().packages("com.grizzly.test");
// This causes Jersey to forward 404s to default servlet
// which will catch all the static content requests.
rc.property(ServletProperties.FILTER_FORWARD_ON_404, true);
FilterRegistration reg = context.addFilter("JerseyApp", new ServletContainer(rc));
reg.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), "/*");
}
private static void createDefaultServlet(WebappContext context) {
ArraySet<File> baseDir = new ArraySet<>(File.class);
baseDir.add(new File("."));
ServletRegistration defaultServletReg
= context.addServlet("DefaultServlet", new DefaultServlet(baseDir) {});
defaultServletReg.addMapping("/*");
}
public static void main(String[] args) throws IOException {
HttpServer server = createServer();
System.in.read();
server.stop();
}
}
You will need to add the Jersey Grizzly servlet dependency
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-servlet</artifactId>
<version>${jersey2.version}</version>
</dependency>
The only problem with this approach is that the default servlet is meant to serve files from the file system, not from the classpath, as you are currently trying to do. You can see in the createDefaultServlet method I just set the base directory to the current working directory. So that's where all your files would need to be. You can change it to "docs" so all your files would be in the docs folder, which would be in the current working directory.
If you want to read files from the classpath, you may need to implement your own servlet. You can look at the source code for DefaultServlet and try to modify it to serve from the classpath. You can also check out Dropwizard's AssetServlet, which already does serve content from the classpath.
Or you can just say forget it, and just serve from the file system :-)
I'm trying to create a simple webapp without any XML configuration using Spring 3.1 and an embedded Jetty 8 server.
However, I'm struggling to get Jetty to recognise my implementaton of the Spring WebApplicationInitializer interface.
Project structure:
src
+- main
+- java
| +- JettyServer.java
| +- Initializer.java
|
+- webapp
+- web.xml (objective is to remove this - see below).
The Initializer class above is a simple implementation of WebApplicationInitializer:
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.web.WebApplicationInitializer;
public class Initializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.out.println("onStartup");
}
}
Likewise JettyServer is a simple implementation of an embedded Jetty server:
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
public class JettyServer {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
WebAppContext webAppContext = new WebAppContext();
webAppContext.setResourceBase("src/main/webapp");
webAppContext.setContextPath("/");
webAppContext.setConfigurations(new Configuration[] { new AnnotationConfiguration() });
webAppContext.setParentLoaderPriority(true);
server.setHandler(webAppContext);
server.start();
server.join();
}
}
My understanding is that on startup Jetty will use AnnotationConfiguration to scan for
annotated implementations of ServletContainerInitializer; it should find Initializer and wire it in...
However, when I start the Jetty server (from within Eclipse) I see the following on the command-line:
2012-11-04 16:59:04.552:INFO:oejs.Server:jetty-8.1.7.v20120910
2012-11-04 16:59:05.046:INFO:/:No Spring WebApplicationInitializer types detected on classpath
2012-11-04 16:59:05.046:INFO:oejsh.ContextHandler:started o.e.j.w.WebAppContext{/,file:/Users/duncan/Coding/spring-mvc-embedded-jetty-test/src/main/webapp/}
2012-11-04 16:59:05.117:INFO:oejs.AbstractConnector:Started SelectChannelConnector#0.0.0.0:8080
The important bit is this:
No Spring WebApplicationInitializer types detected on classpath
Note that src/main/java is defined as a source folder in Eclipse, so should be on the classpath. Also note that the Dynamic Web Module Facet is set to 3.0.
I'm sure there's a simple explanation, but I'm struggling to see the wood for the trees! I suspect the key is with the following line:
...
webAppContext.setResourceBase("src/main/webapp");
...
This makes sense with a 2.5 servlet using web.xml (see below), but what should it be when using AnnotationConfiguration?
NB: Everything fires up correctly if I change the Configurations to the following:
...
webAppContext.setConfigurations(new Configuration[] { new WebXmlConfiguration() });
...
In this case it finds the web.xml under src/main/webapp and uses it to wire the servlet using DispatcherServlet and AnnotationConfigWebApplicationContext in the usual way (completely bypassing the WebApplicationInitializer implementation above).
This feels very much like a classpath problem, but I'm struggling to understand quite how Jetty associates itself with implementations of WebApplicationInitializer - any suggestions would be most appreciated!
For info, I'm using the following:
Spring 3.1.1
Jetty 8.1.7
STS 3.1.0
The problem is that Jetty's AnnotationConfiguration class does not scan non-jar resources on the classpath (except under WEB-INF/classes).
It finds my WebApplicationInitializer's if I register a subclass of AnnotationConfiguration which overrides configure(WebAppContext) to scan the host classpath in addition to the container and web-inf locations.
Most of the sub-class is (sadly) copy-paste from the parent. It includes:
an extra parse call (parseHostClassPath) at the end of the configure method;
the parseHostClassPath method which is largely copy-paste from
AnnotationConfiguration's parseWebInfClasses;
the getHostClassPathResource method which grabs the first non-jar URL
from the classloader (which, for me at least, is the file url to my
classpath in eclipse).
I am using slightly different versions of Jetty (8.1.7.v20120910) and Spring (3.1.2_RELEASE), but I imagine the same solution will work.
Edit: I created a working sample project in github with some modifications (the code below works fine from Eclipse but not when running in a shaded jar) - https://github.com/steveliles/jetty-embedded-spring-mvc-noxml
In the OP's JettyServer class the necessary change would replace line 15 with:
webAppContext.setConfigurations (new Configuration []
{
new AnnotationConfiguration()
{
#Override
public void configure(WebAppContext context) throws Exception
{
boolean metadataComplete = context.getMetaData().isMetaDataComplete();
context.addDecorator(new AnnotationDecorator(context));
AnnotationParser parser = null;
if (!metadataComplete)
{
if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered())
{
parser = createAnnotationParser();
parser.registerAnnotationHandler("javax.servlet.annotation.WebServlet", new WebServletAnnotationHandler(context));
parser.registerAnnotationHandler("javax.servlet.annotation.WebFilter", new WebFilterAnnotationHandler(context));
parser.registerAnnotationHandler("javax.servlet.annotation.WebListener", new WebListenerAnnotationHandler(context));
}
}
List<ServletContainerInitializer> nonExcludedInitializers = getNonExcludedInitializers(context);
parser = registerServletContainerInitializerAnnotationHandlers(context, parser, nonExcludedInitializers);
if (parser != null)
{
parseContainerPath(context, parser);
parseWebInfClasses(context, parser);
parseWebInfLib (context, parser);
parseHostClassPath(context, parser);
}
}
private void parseHostClassPath(final WebAppContext context, AnnotationParser parser) throws Exception
{
clearAnnotationList(parser.getAnnotationHandlers());
Resource resource = getHostClassPathResource(getClass().getClassLoader());
if (resource == null)
return;
parser.parse(resource, new ClassNameResolver()
{
public boolean isExcluded (String name)
{
if (context.isSystemClass(name)) return true;
if (context.isServerClass(name)) return false;
return false;
}
public boolean shouldOverride (String name)
{
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
if (context.isParentLoaderPriority())
return false;
return true;
}
});
//TODO - where to set the annotations discovered from WEB-INF/classes?
List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>();
gatherAnnotations(annotations, parser.getAnnotationHandlers());
context.getMetaData().addDiscoveredAnnotations (annotations);
}
private Resource getHostClassPathResource(ClassLoader loader) throws IOException
{
if (loader instanceof URLClassLoader)
{
URL[] urls = ((URLClassLoader)loader).getURLs();
for (URL url : urls)
if (url.getProtocol().startsWith("file"))
return Resource.newResource(url);
}
return null;
}
},
});
Update: Jetty 8.1.8 introduces internal changes that are incompatible with the code above. For 8.1.8 the following seems to work:
webAppContext.setConfigurations (new Configuration []
{
// This is necessary because Jetty out-of-the-box does not scan
// the classpath of your project in Eclipse, so it doesn't find
// your WebAppInitializer.
new AnnotationConfiguration()
{
#Override
public void configure(WebAppContext context) throws Exception {
boolean metadataComplete = context.getMetaData().isMetaDataComplete();
context.addDecorator(new AnnotationDecorator(context));
//Even if metadata is complete, we still need to scan for ServletContainerInitializers - if there are any
AnnotationParser parser = null;
if (!metadataComplete)
{
//If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we need to search for annotations
if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered())
{
_discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context));
_discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context));
_discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context));
}
}
//Regardless of metadata, if there are any ServletContainerInitializers with #HandlesTypes, then we need to scan all the
//classes so we can call their onStartup() methods correctly
createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(context));
if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
{
parser = createAnnotationParser();
parse(context, parser);
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
context.getMetaData().addDiscoveredAnnotations(((AbstractDiscoverableAnnotationHandler)h).getAnnotationList());
}
}
private void parse(final WebAppContext context, AnnotationParser parser) throws Exception
{
List<Resource> _resources = getResources(getClass().getClassLoader());
for (Resource _resource : _resources)
{
if (_resource == null)
return;
parser.clearHandlers();
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
{
if (h instanceof AbstractDiscoverableAnnotationHandler)
((AbstractDiscoverableAnnotationHandler)h).setResource(null); //
}
parser.registerHandlers(_discoverableAnnotationHandlers);
parser.registerHandler(_classInheritanceHandler);
parser.registerHandlers(_containerInitializerAnnotationHandlers);
parser.parse(_resource,
new ClassNameResolver()
{
public boolean isExcluded (String name)
{
if (context.isSystemClass(name)) return true;
if (context.isServerClass(name)) return false;
return false;
}
public boolean shouldOverride (String name)
{
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
if (context.isParentLoaderPriority())
return false;
return true;
}
});
}
}
private List<Resource> getResources(ClassLoader aLoader) throws IOException
{
if (aLoader instanceof URLClassLoader)
{
List<Resource> _result = new ArrayList<Resource>();
URL[] _urls = ((URLClassLoader)aLoader).getURLs();
for (URL _url : _urls)
_result.add(Resource.newResource(_url));
return _result;
}
return Collections.emptyList();
}
}
});
I was able to resolve in an easier but more limited way by just providing explicitly to the AnnotationConfiguration the implementation class (MyWebApplicationInitializerImpl in this example) that I want to be loaded like this:
webAppContext.setConfigurations(new Configuration[] {
new WebXmlConfiguration(),
new AnnotationConfiguration() {
#Override
public void preConfigure(WebAppContext context) throws Exception {
MultiMap<String> map = new MultiMap<String>();
map.add(WebApplicationInitializer.class.getName(), MyWebApplicationInitializerImpl.class.getName());
context.setAttribute(CLASS_INHERITANCE_MAP, map);
_classInheritanceHandler = new ClassInheritanceHandler(map);
}
}
});
Jetty 9.0.1 contains an enhancement which allows for scanning of annotations of non-jar resources (ie classes) on the container classpath. See comment #5 on the following issue for how to use it:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=404176#c5
Jan
The code below did the trick in my maven project:
public static void main(String[] args) throws Exception {
Server server = new Server();
ServerConnector scc = new ServerConnector(server);
scc.setPort(Integer.parseInt(System.getProperty("jetty.port", "8080")));
server.setConnectors(new Connector[] { scc });
WebAppContext context = new WebAppContext();
context.setServer(server);
context.setContextPath("/");
context.setWar("src/main/webapp");
context.getMetaData().addContainerResource(new FileResource(new File("./target/classes").toURI()));
context.setConfigurations(new Configuration[]{
new WebXmlConfiguration(),
new AnnotationConfiguration()
});
server.setHandler(context);
try {
System.out.println(">>> STARTING EMBEDDED JETTY SERVER, PRESS ANY KEY TO STOP");
System.out.println(String.format(">>> open http://localhost:%s/", scc.getPort()));
server.start();
while (System.in.available() == 0) {
Thread.sleep(5000);
}
server.stop();
server.join();
} catch (Throwable t) {
t.printStackTrace();
System.exit(100);
}
}
Based on my testing and this thread http://forum.springsource.org/showthread.php?127152-WebApplicationInitializer-not-loaded-with-embedded-Jetty I don't think it works at the moment. If you look in AnnotationConfiguration.configure:
parseContainerPath(context, parser);
// snip comment
parseWebInfClasses(context, parser);
parseWebInfLib (context, parser);
it seems coupled to a war-like deployment rather than embedded.
Here is an example using Spring MVC and embedded Jetty that might be more useful:
http://www.jamesward.com/2012/08/13/containerless-spring-mvc
It creates the Spring servlet directly rather then relying on annotations.
To those experiencing this lately, it appears this gets around the issue:
#Component
public class Initializer implements WebApplicationInitializer {
private ServletContext servletContext;
#Autowired
public WebInitializer(ServletContext servletContext) {
this.servletContext = servletContext;
}
#PostConstruct
public void onStartup() throws ServletException {
onStartup(servletContext);
}
public void onStartup(ServletContext servletContext) throws ServletException {
System.out.println("onStartup");
}
}
To make it work on Jetty 9 set attribute AnnotationConfiguration.CLASS_INHERITANCE_MAP on WebAppContext
webAppContext.setAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP, createClassMap());
And here is how to create this map:
private ClassInheritanceMap createClassMap() {
ClassInheritanceMap classMap = new ClassInheritanceMap();
ConcurrentHashSet<String> impl = new ConcurrentHashSet<>();
impl.add(MyWebAppInitializer.class.getName());
classMap.put(WebApplicationInitializer.class.getName(), impl);
return classMap;
}
I placed that solution on gitHub
What about just setting the context attribute that tells the scanner which things belong on the container classpath that need to be scanned?
context attribute:
org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern
./servlet-api-[^/].jar$
It is designed to be used with jar names, but you could just match everything.
You'd need to use the WebInfConfiguration as well as the AnnotationConfiguration classes.
cheers
Jan
In our case these lines helped in Jetty startup code:
ClassList cl = Configuration.ClassList.setServerDefault(server);
cl.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration", "org.eclipse.jetty.annotations.AnnotationConfiguration");
Jetty 9 version of "magomarcelo" answer:
context.setConfigurations(
new org.eclipse.jetty.webapp.Configuration[] { new WebXmlConfiguration(), new AnnotationConfiguration() {
#Override
public void preConfigure(WebAppContext context) throws Exception {
final ClassInheritanceMap map = new ClassInheritanceMap();
final ConcurrentHashSet<String> set = new ConcurrentHashSet<>();
set.add(MyWebAppInitializer.class.getName());
map.put(WebApplicationInitializer.class.getName(), set);
context.setAttribute(CLASS_INHERITANCE_MAP, map);
_classInheritanceHandler = new ClassInheritanceHandler(map);
}
} });
For Jetty 9, if you have webjars, the solution provided does not work straight away as those Jars need to be on the classpath and the JAR contents need to be available as resources for your webapp. So, for that to work together with webjars, the config would have to be:
context.setExtraClasspath(pathsToWebJarsCommaSeparated);
context.setAttribute(WebInfConfiguration.WEBINF_JAR_PATTERN, ".*\\.jar$");
context.setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, ".*\\.jar$");
context.setConfigurations(
new org.eclipse.jetty.webapp.Configuration[] {
new WebInfConfiguration(), new MetaInfConfiguration(),
new AnnotationConfiguration() {
#Override
public void preConfigure(WebAppContext context) throws Exception {
final ClassInheritanceMap map = new ClassInheritanceMap();
final ConcurrentHashSet<String> set = new ConcurrentHashSet<>();
set.add(MyWebAppInitializer.class.getName());
map.put(WebApplicationInitializer.class.getName(), set);
context.setAttribute(CLASS_INHERITANCE_MAP, map);
_classInheritanceHandler = new ClassInheritanceHandler(map);
}
} });
The order here is important (WebInfConfiguration has to come before MetaInf).
Solution that worked for me and does not involve scanning, but uses WebApplicationInitializer class that you provide. Jetty version: 9.2.20
public class Main {
public static void main(String... args) throws Exception {
Properties properties = new Properties();
InputStream stream = Main.class.getResourceAsStream("/WEB-INF/application.properties");
properties.load(stream);
stream.close();
PropertyConfigurator.configure(properties);
WebAppContext webAppContext = new WebAppContext();
webAppContext.setResourceBase("resource");
webAppContext.setContextPath(properties.getProperty("base.url"));
webAppContext.setConfigurations(new Configuration[] {
new WebXmlConfiguration(),
new AnnotationConfiguration() {
#Override
public void preConfigure(WebAppContext context) {
ClassInheritanceMap map = new ClassInheritanceMap();
map.put(WebApplicationInitializer.class.getName(), new ConcurrentHashSet<String>() {{
add(WebInitializer.class.getName());
add(SecurityWebInitializer.class.getName());
}});
context.setAttribute(CLASS_INHERITANCE_MAP, map);
_classInheritanceHandler = new ClassInheritanceHandler(map);
}
}
});
Server server = new Server(Integer.parseInt(properties.getProperty("base.port")));
server.setHandler(webAppContext);
server.start();
server.join();
}
}
The source (in russian) of this code snippet is here: https://habrahabr.ru/post/255773/
did a simple maven project to demonstrate how it can be done cleanly.
public class Console {
public static void main(String[] args) {
try {
Server server = new Server(8080);
//Set a handler to handle requests.
server.setHandler(getWebAppContext());
//starts to listen at 0.0.0.0:8080
server.start();
server.join();
} catch (Exception e) {
log.error("server exited with exception", e);
}
}
private static WebAppContext getWebAppContext() {
final WebAppContext webAppContext = new WebAppContext();
//route all requests via this web-app.
webAppContext.setContextPath("/");
/*
* point to location where the jar into which this class gets packaged into resides.
* this could very well be the target directory in a maven development build.
*/
webAppContext.setResourceBase("directory_where_the_application_jar_exists");
//no web inf for us - so let the scanning know about location of our libraries / classes.
webAppContext.getMetaData().setWebInfClassesDirs(Arrays.asList(webAppContext.getBaseResource()));
//Scan for annotations (servlet 3+)
final AnnotationConfiguration configuration = new AnnotationConfiguration();
webAppContext.setConfigurations(new Configuration[]{configuration});
return webAppContext;
}
}
and that's all - the spring WebApplicationInitializer that you use will get detected without explicitly letting jetty server know about the existence of such an app initializer.
Knowing nothing of web services, I'm just trying to call some "isAlive" service that is described by a wsdl.
This seems to me like something that should take no more than 2-5 lines of code but I can't seem to find anything but huge long examples involving 3rd party packages etc.
Anyone has any ideas? If it is always suppose to be long maybe a good explanation as to why it has to be so complicated will also be appreciated.
I'm using Eclipse and the wsdl is SOAP.
JDK 6 comes with jax-ws, everything you need to develop a client for a web service.
I'm unable to find some simple enough examples to post , but start at https://jax-ws.dev.java.net/
Edit: here's a simple example - a client for this web service: http://xmethods.com/ve2/ViewListing.po?key=427565
C:\temp> md generated
C:\temp>"c:\Program Files\Java\jdk1.6.0_17"\bin\wsimport -keep -d generated http://www50.brinkster.com/vbfacileinpt/np.asmx?wsdl
Create PrimeClient.java which look like:
import javax.xml.ws.WebServiceRef;
import com.microsoft.webservices.*;
//the above namespace is from the generated code from the wsdl.
public class PrimeClient {
//Cant get this to work.. #WebServiceRef(wsdlLocation="http://www50.brinkster.com/vbfacileinpt/np.asmx?wsdl")
static PrimeNumbers service;
public static void main(String[] args) {
try {
service = new PrimeNumbers();
PrimeClient client = new PrimeClient();
client.doTest(args);
} catch(Exception e) {
e.printStackTrace();
}
}
public void doTest(String[] args) {
try {
System.out.println("Retrieving the port from the following service: " + service);
PrimeNumbersSoap pm = service.getPrimeNumbersSoap();
System.out.println("Invoking the getPrimeNumbersSoap operation ");
System.out.println(pm.getPrimeNumbers(100));
} catch(Exception e) {
e.printStackTrace();
}
}
}
Compile and run:
C:\temp>"c:\Program Files\Java\jdk1.6.0_17"\bin\javac -cp generated PrimeClient.java
C:\temp>"c:\Program Files\Java\jdk1.6.0_17"\bin\java -cp .;generated PrimeClient
Retrieving the port from the following service: com.microsoft.webservices.PrimeN
umbers#19b5393
Invoking the getPrimeNumbersSoap operation
1,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97
There are plugins for IDE's which generate the needed code to consume a web service for you.
After the plugin generates you the base methods you simply call a web service like that:
TransportServiceSoap service = new TransportServiceLocator().getTransportServiceSoap();
service.getCities();
Have a look at http://urbas.tk/index.php/2009/02/20/eclipse-plug-in-as-a-web-service-client/
There are three ways to write a web service client
Dynamic proxy
Dynamic invocation interface (DII)
Application client
Example for Dynamic Proxy Client
import java.net.URL;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;
import dynamicproxy.HelloIF;
public class HelloClient {
public static void main(String[] args) {
try {
String UrlString = "Your WSDL URL"; //
String nameSpaceUri = "urn:Foo";
String serviceName = "MyHelloService";
String portName = "HelloIFPort";
System.out.println("UrlString = " + UrlString);
URL helloWsdlUrl = new URL(UrlString);
ServiceFactory serviceFactory =
ServiceFactory.newInstance();
Service helloService =
serviceFactory.createService(helloWsdlUrl,
new QName(nameSpaceUri, serviceName));
dynamicproxy.HelloIF myProxy =
(dynamicproxy.HelloIF)
helloService.getPort(
new QName(nameSpaceUri, portName),
dynamicproxy.HelloIF.class);
System.out.println(myProxy.sayHello("Buzz"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
I hope , this would solve your question.
The easiest I've found so far to use is the Idea IntelliJ wizard which - using Metro libraries - generate a very small code snippet which works fine with Java 6.