Trying to setup my first REST API (using Jersey 2 and Gradle) and add some documentation to it by using swagger. But when adding swagger dependencies and following this swagger documentation, "Using a custom Application subclass" approach, it throws me this exception, when executing the main method from Eclipse:
Exception in thread "main" java.lang.NoClassDefFoundError: javax/servlet/ServletConfig
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.getDeclaredMethods(Class.java:1975)
at org.glassfish.jersey.server.model.IntrospectionModeller$2.run(IntrospectionModeller.java:253)
at java.security.AccessController.doPrivileged(Native Method)
at org.glassfish.jersey.server.model.IntrospectionModeller.getAllDeclaredMethods(IntrospectionModeller.java:247)
at org.glassfish.jersey.server.model.IntrospectionModeller.checkForNonPublicMethodIssues(IntrospectionModeller.java:172)
at org.glassfish.jersey.server.model.IntrospectionModeller.doCreateResourceBuilder(IntrospectionModeller.java:119)
at org.glassfish.jersey.server.model.IntrospectionModeller.access$000(IntrospectionModeller.java:80)
at org.glassfish.jersey.server.model.IntrospectionModeller$1.call(IntrospectionModeller.java:112)
at org.glassfish.jersey.server.model.IntrospectionModeller$1.call(IntrospectionModeller.java:109)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.processWithException(Errors.java:255)
at org.glassfish.jersey.server.model.IntrospectionModeller.createResourceBuilder(IntrospectionModeller.java:109)
at org.glassfish.jersey.server.model.Resource.from(Resource.java:797)
at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:465)
at org.glassfish.jersey.server.ApplicationHandler.access$500(ApplicationHandler.java:184)
at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:350)
at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:347)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.processWithException(Errors.java:255)
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:347)
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:299)
at org.glassfish.jersey.jdkhttp.JdkHttpHandlerContainer.<init>(JdkHttpHandlerContainer.java:98)
at org.glassfish.jersey.jdkhttp.JdkHttpServerFactory.createHttpServer(JdkHttpServerFactory.java:111)
at org.glassfish.jersey.jdkhttp.JdkHttpServerFactory.createHttpServer(JdkHttpServerFactory.java:93)
at example.MyApp.main(MyApp.java:21)
Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletConfig
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 29 more
My code looks like this:
package example;
import static org.glassfish.jersey.jdkhttp.JdkHttpServerFactory.createHttpServer;
import java.net.URI;
import javax.ws.rs.core.UriBuilder;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import com.sun.net.httpserver.HttpServer;
import io.swagger.jaxrs.listing.ApiListingResource;
import io.swagger.jaxrs.listing.SwaggerSerializers;
public class MyApp extends ResourceConfig {
public static void main(String[] args) throws Throwable {
URI baseUri = UriBuilder.fromUri("http://localhost/").port(9999).build();
HttpServer server = createHttpServer(baseUri, new MyApp());
System.out.println("SERVICE started at: " + baseUri);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
server.stop(0);
}));
}
public MyApp() {
packages("example");
register(SwaggerSerializers.class); // <-- swagger specific
register(ApiListingResource.class); // <-- swagger specific
register(JacksonFeature.class);
}
}
My gradle dependencies
dependencies {
compile 'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:+'
compile 'org.glassfish.jersey.containers:jersey-container-jdk-http:+'
compile 'org.glassfish.jersey.media:jersey-media-moxy:+'
compile 'org.glassfish.jersey.media:jersey-media-json-jackson:+'
compile 'io.swagger:swagger-jersey2-jaxrs:1.5.9'
}
Using jdk1.8.0_77 on Windows 7
However, if I comment out the swagger dependency and the swagger specifics in the code, then the actual REST service works as expected. How can I make swagger work without using a servlet container? The REST service can work without it
dependencies {
compile 'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:+'
compile 'org.glassfish.jersey.containers:jersey-container-jdk-http:+'
compile 'org.glassfish.jersey.media:jersey-media-moxy:+'
compile 'org.glassfish.jersey.media:jersey-media-json-jackson:+'
// compile 'io.swagger:swagger-jersey2-jaxrs:1.5.9'
}
code:
package example;
import static org.glassfish.jersey.jdkhttp.JdkHttpServerFactory.createHttpServer;
import java.net.URI;
import javax.ws.rs.core.UriBuilder;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import com.sun.net.httpserver.HttpServer;
import io.swagger.jaxrs.listing.ApiListingResource;
import io.swagger.jaxrs.listing.SwaggerSerializers;
public class MyApp extends ResourceConfig {
public static void main(String[] args) throws Throwable {
URI baseUri = UriBuilder.fromUri("http://localhost/").port(9999).build();
HttpServer server = createHttpServer(baseUri, new MyApp());
System.out.println("SERVICE started at: " + baseUri);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
server.stop(0);
}));
}
public MyApp() {
packages("example");
// register(SwaggerSerializers.class); // <-- swagger specific
// register(ApiListingResource.class); // <-- swagger specific
register(JacksonFeature.class);
}
}
So it looks like the issue is derived from you running in a non-servlet environment. While Jersey supports it, swagger-core... not so much. This poses some issues with specific deployment, although they are less common.
The easiest solution would, obviously, be to use a servlet-container engine. Something lightweight like Jetty would work.
I know that is a litlle bit late, but I have the same issue and came up with a solution to run swagger on a non-servlet environment.
Hope to be helpful for the next devs.
pom.xml
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jersey2-jaxrs</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
</dependencies>
...
<properties>
<jersey.version>2.28</jersey.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
Main.java
package me.nunum.whereami;
import me.nunum.whereami.facade.ApiListingResource;
import me.nunum.whereami.framework.interceptor.PrincipalInterceptor;
import org.glassfish.grizzly.http.server.CLStaticHttpHandler;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.ServerConfiguration;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Main class.
*/
public class Main {
// Base URI the Grizzly HTTP server will listen on
private static final String BASE_URI = "http://0.0.0.0:8080";
private static final Logger LOGGER = Logger.getLogger("Main");
/**
* Starts Grizzly HTTP server exposing JAX-RS resources defined in this application.
*
* #return Grizzly HTTP server.
*/
public static HttpServer startServer() {
// create a resource config that scans for JAX-RS resources and providers
// in me.nunum.whereami.facade package
final ResourceConfig rc = new ResourceConfig().packages("me.nunum.whereami.facade");
rc.setApplicationName("where");
rc.register(PrincipalInterceptor.class);
rc.register(ApiListingResource.class);
rc.register(io.swagger.jaxrs.listing.SwaggerSerializers.class);
// create and start a new instance of grizzly http server
// exposing the Jersey application at BASE_URI
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
}
/**
* Main method.
*
* #param args
* #throws IOException
*/
public static void main(String[] args) throws IOException {
final HttpServer server = startServer();
ClassLoader loader = Main.class.getClassLoader();
CLStaticHttpHandler docsHandler = new CLStaticHttpHandler(loader, "swagger-ui/dist/");
docsHandler.setFileCacheEnabled(false);
ServerConfiguration cfg = server.getServerConfiguration();
cfg.addHttpHandler(docsHandler, "/docs/");
Main.LOGGER.log(Level.INFO,"Jersey app started with WADL available at "
+ "{0} \nHit enter to stop it...", BASE_URI);
System.in.read();
server.shutdown();
}
}
Refactor io.swagger.jaxrs.listing.ApiListingResource class into a new class (created in my facade package) to work on a non-servlet environment.
package me.nunum.whereami.facade;
import io.swagger.annotations.ApiOperation;
import io.swagger.config.FilterFactory;
import io.swagger.config.Scanner;
import io.swagger.config.SwaggerConfig;
import io.swagger.core.filter.SpecFilter;
import io.swagger.core.filter.SwaggerSpecFilter;
import io.swagger.jaxrs.Reader;
import io.swagger.jaxrs.config.JaxrsScanner;
import io.swagger.jaxrs.config.ReaderConfig;
import io.swagger.jaxrs.listing.SwaggerSerializers;
import io.swagger.models.Swagger;
import io.swagger.util.Yaml;
import java.util.*;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Path("/api/doc")
#Singleton
public class ApiListingResource {
static boolean initialized = false;
Logger LOGGER = LoggerFactory.getLogger(io.swagger.jaxrs.listing.ApiListingResource.class);
public Swagger mSwaggerConfig;
public ApiListingResource() {
mSwaggerConfig = new Swagger();
mSwaggerConfig.setBasePath("/");
}
public ApiListingResource(Swagger swagger){
this.mSwaggerConfig = swagger;
}
protected synchronized Swagger scan(Application app) {
Swagger swagger = null;
Scanner scanner = new Scanner() {
#Override
public Set<Class<?>> classes() {
return app.getClasses();
}
#Override
public boolean getPrettyPrint() {
return false;
}
#Override
public void setPrettyPrint(boolean b) {
}
};
this.LOGGER.debug("using scanner " + scanner);
SwaggerSerializers.setPrettyPrint(scanner.getPrettyPrint());
swagger = this.mSwaggerConfig;
new HashSet();
Set classes;
if (scanner instanceof JaxrsScanner) {
classes = null;
} else {
classes = scanner.classes();
}
if (classes != null) {
Reader reader = new Reader(swagger, new ReaderConfig() {
#Override
public boolean isScanAllResources() {
return false;
}
#Override
public Collection<String> getIgnoredRoutes() {
return new ArrayList<>();
}
});
swagger = reader.read(classes);
if (scanner instanceof SwaggerConfig) {
swagger = ((SwaggerConfig)scanner).configure(swagger);
} else {
SwaggerConfig configurator = new SwaggerConfig() {
#Override
public Swagger configure(Swagger swagger) {
return swagger;
}
#Override
public String getFilterClass() {
return "";
}
};
this.LOGGER.debug("configuring swagger with " + configurator);
configurator.configure(swagger);
}
}
initialized = true;
return swagger;
}
#GET
#Produces({"application/json"})
#Path("/swagger.json")
#ApiOperation(
value = "The swagger definition in JSON",
hidden = true
)
public Response getListingJson(#Context Application app, #Context HttpHeaders headers, #Context UriInfo uriInfo) {
Swagger swagger = this.mSwaggerConfig;
if (!initialized) {
this.mSwaggerConfig = this.scan(app);
}
if (swagger != null) {
SwaggerSpecFilter filterImpl = FilterFactory.getFilter();
if (filterImpl != null) {
SpecFilter f = new SpecFilter();
swagger = f.filter(swagger, filterImpl, this.getQueryParams(uriInfo.getQueryParameters()), this.getCookies(headers), this.getHeaders(headers));
}
return Response.ok().entity(swagger).build();
} else {
return Response.status(404).build();
}
}
#GET
#Produces({"application/yaml"})
#Path("/swagger.yaml")
#ApiOperation(
value = "The swagger definition in YAML",
hidden = true
)
public Response getListingYaml(#Context Application app, #Context HttpHeaders headers, #Context UriInfo uriInfo) {
Swagger swagger = this.mSwaggerConfig;
if (!initialized) {
this.mSwaggerConfig = this.scan(app);
}
try {
if (swagger != null) {
SwaggerSpecFilter filterImpl = FilterFactory.getFilter();
this.LOGGER.debug("using filter " + filterImpl);
if (filterImpl != null) {
SpecFilter f = new SpecFilter();
swagger = f.filter(swagger, filterImpl, this.getQueryParams(uriInfo.getQueryParameters()), this.getCookies(headers), this.getHeaders(headers));
}
String yaml = Yaml.mapper().writeValueAsString(swagger);
String[] parts = yaml.split("\n");
StringBuilder b = new StringBuilder();
String[] arr$ = parts;
int len$ = parts.length;
for(int i$ = 0; i$ < len$; ++i$) {
String part = arr$[i$];
int pos = part.indexOf("!<");
int endPos = part.indexOf(">");
b.append(part);
b.append("\n");
}
return Response.ok().entity(b.toString()).type("application/yaml").build();
}
} catch (Exception var16) {
var16.printStackTrace();
}
return Response.status(404).build();
}
protected Map<String, List<String>> getQueryParams(MultivaluedMap<String, String> params) {
Map<String, List<String>> output = new HashMap();
if (params != null) {
Iterator i$ = params.keySet().iterator();
while(i$.hasNext()) {
String key = (String)i$.next();
List<String> values = (List)params.get(key);
output.put(key, values);
}
}
return output;
}
protected Map<String, String> getCookies(HttpHeaders headers) {
Map<String, String> output = new HashMap();
if (headers != null) {
Iterator i$ = headers.getCookies().keySet().iterator();
while(i$.hasNext()) {
String key = (String)i$.next();
Cookie cookie = (Cookie)headers.getCookies().get(key);
output.put(key, cookie.getValue());
}
}
return output;
}
protected Map<String, List<String>> getHeaders(HttpHeaders headers) {
Map<String, List<String>> output = new HashMap();
if (headers != null) {
Iterator i$ = headers.getRequestHeaders().keySet().iterator();
while(i$.hasNext()) {
String key = (String)i$.next();
List<String> values = (List)headers.getRequestHeaders().get(key);
output.put(key, values);
}
}
return output;
}
}
Any questions, please ask.
I was facing the same issue, I solved it by following the same swagger documentation, The only difference is that I provided my own ApiListingResource implementation
package com.example;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.annotations.ApiOperation;
import io.swagger.jaxrs.config.BeanConfig;
import io.swagger.models.Swagger;
import io.swagger.util.Yaml;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
#Path("/docs")
#ApplicationScoped
public class ApiListingResource {
private final Swagger swagger;
public ApiListingResource() {
BeanConfig beanConfig = new BeanConfig();
beanConfig.setTitle("MY REST API");
beanConfig.setVersion("v1");
beanConfig.setBasePath("/api");
beanConfig.setResourcePackage("com.example.resource");
beanConfig.setScan(true);
this.swagger = beanConfig.getSwagger();
}
#GET
#Produces({"application/json"})
#Path("/swagger.json")
public Response getListingJson() {
return Response.ok(this.swagger).build();
}
#GET
#Produces({"application/yaml"})
#Path("/swagger.yaml")
public Response getListingYaml() throws JsonProcessingException {
String yaml = Yaml.mapper().writeValueAsString(this.swagger);
return Response.ok(yaml).build();
}
}
Then I registered the resource along with the SwaggerSerializers provider.
Related
I'm pretty new to Springboot and Java in general and because we got this in school I'm fiddeling arround.
I'm now trying to save an entity outside of the Springboot Entities, Repositories or RestController with the following code:
InfMApplication.java:
package com.domain.springboot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.domain.springboot.repositories.MovieRepository;
import com.domain.springboot.services.MovieImport;
#SpringBootApplication
public class InfMApplication {
public static void main(String[] args) {
SpringApplication.run(InfMApplication.class, args);
MovieImport movieImport = new MovieImport();
movieImport.saveToDb();
}
}
MovieImport.java:
package com.domain.springboot.services;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.CrossOrigin;
import java.io.*;
import java.net.URL;
import com.google.gson.Gson;
import com.domain.omdbapi.entities.Movie;
import com.domain.omdbapi.entities.SearchResponse;
import com.domain.omdbapi.entities.SearchResult;
import com.domain.springboot.repositories.ComplexRepository;
import com.domain.springboot.repositories.DocumentRepository;
import com.domain.springboot.repositories.MovieRepository;
import com.domain.springboot.repositories.SimpleRepository;
#Service
public class MovieImport {
private final MovieRepository movieRepository;
public MovieImport(MovieRepository movieRepository){
this.movieRepository = movieRepository;
}
public void main() {
String randomImdbId = fetchRandomMovie();
Movie movie = fetchMovieDetails(randomImdbId);
saveToDb(movie);
}
public void saveToDb(Movie movie) {
com.domain.springboot.entities.Movie springbootMovie = new com.domain.springboot.entities.Movie(movie.Title, movie.imdbID);
this.movieRepository.save(springbootMovie);
}
public String fetchRandomMovie() {
String randomWord = getRandomWord();
String url = "https://www.omdbapi.com/?apikey=<API_KEY>&type=movie&s=" + randomWord;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(
URI.create(url))
.header("accept", "application/json")
.build();
HttpResponse<String> response = null;
try {
response = client.send(request, BodyHandlers.ofString());
} catch (Exception e) {
System.out.println(e);
}
Gson gson = new Gson();
SearchResponse searchResponse = gson.fromJson(response.body(), SearchResponse.class);
int randomIndex = new Random().nextInt(0, searchResponse.getSearch().length);
SearchResult randomResult = searchResponse.getSearch()[randomIndex];
return randomResult.getImdbID();
}
public Movie fetchMovieDetails(String imdbId) {
String url = "https://www.omdbapi.com/?apikey=<API_KEY>&type=movie&plot=full&i=" + imdbId;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(
URI.create(url))
.header("accept", "application/json")
.build();
HttpResponse<String> response = null;
try {
response = client.send(request, BodyHandlers.ofString());
} catch (Exception e) {
System.out.println(e);
}
Gson gson = new Gson();
Movie movie = gson.fromJson(response.body(), Movie.class);
return movie;
}
public String getRandomWord() {
URL resource = getClass().getClassLoader().getResource("Wordlist.txt");
List<String> words = new ArrayList<>();
try {
File file = new File(resource.toURI());
words = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
int randomIndex = new Random().nextInt(0, words.size());
return words.get(randomIndex);
}
}
If I use "this.movieRepository.save(movieObject);" to save a movie in the MovieRestController the same way, it works. I also tried adding the "#Autowire" annotation, but this didn't work.
I always get the error
java.lang.NullPointerException: Cannot invoke "com.domain.springboot.repositories.MovieRepository.save(Object)" because "this.movieRepository" is null
How can I get to use the movieRepository in other Java classes like in the RestControllers?
java.lang.NullPointerException: Cannot invoke
"com.domain.springboot.repositories.MovieRepository.save(Object)"
because "this.movieRepository" is null
Above is perfectly valid if we look at your following shared code.
public class MovieImport {
private MovieRepository movieRepository;
public void saveToDb() {
// Create movie
com.domain.springboot.entities.Movie springbootMovie = new com.domain.springboot.entities.Movie("Iron Man", "284cb8fgf");
this.movieRepository.save(springbootMovie);
}
}
You've to correct certain things in your code base.
First you're not initializing the movieRepository and therefore, you're getting the null pointer exception. As you've been using the springboot you can use construction injection to initialized the field by spring container. Also. this class should be scanned by spring and you should also put some annotation such as Component or Service on top of it.
Following will work if your MovieImport and MovieRepository classess will scan by springboot.
package com.domain;
import com.domain.omdbapi.entities.Movie;
import com.domain.springboot.repositories.MovieRepository;
#Service
public class MovieImport {
private final MovieRepository movieRepository;
public MovieImport(MovieRepository movieRepository){
this.movieRepository = movieRepository;
}
public void saveToDb() {
// Create movie
com.domain.springboot.entities.Movie springbootMovie = new com.domain.springboot.entities.Movie("Iron Man", "284cb8fgf");
this.movieRepository.save(springbootMovie);
}
}
Updated
#SpringBootApplication
public class InfMApplication implements CommandLineRunner {
#Autowired
private MovieImport movieImport;
public static void main(String[] args) {
SpringApplication.run(InfMApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
movieImport.saveToDb();
}
}
Trying to create a restful clint in Java with Jersey2.
There is 2 problem with this code:
1. Prety slow needs more than 2 seconds
2. Something is wrong with the data binding, only 2/6 data is binded well
Main Java:
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
public class JerseyTestClient {
public static void main(String[] args) {
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("https://wtfismyip.com/json");
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
Response response = invocationBuilder.get();
WtfIsMyConfig wtfIsMyConfig = response.readEntity(WtfIsMyConfig.class);
System.out.println(response);
System.out.println(wtfIsMyConfig);
}
}
Model
public class WtfIsMyConfig implements Serializable {
private String YourIPAddress;
private String YourLocation;
private String YourHostname;
private String YourISP;
private String YourTorExit;
private String YourCountryCode;
public WtfIsMyConfig() {
}
//getters setters
}
Pom.xml
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.bundles</groupId>
<artifactId>jaxrs-ri</artifactId>
<version>2.26</version>
</dependency>
</dependencies>
Output console:
InboundJaxrsResponse{context=ClientResponse{method=GET, uri=https://wtfismyip.com/json, status=200, reason=OK}}
WtfIsMyConfig{YourIPAddress='192.168.0.0', YourLocation='null', YourHostname='192-168-0-0.pool.digikabel.hu', YourISP='null', YourTorExit='null', YourCountryCode='null'}
Process finished with exit code 0
Compilation completed successfully with 3 warnings in 2s 500ms
Requested Output:
{
"YourIPAddress": "192.168.0.0",
"YourLocation": "XXX, 18, Hungary",
"YourHostname": "192-168-0-0.pool.digikabel.hu",
"YourISP": "DIGI Tavkozlesi es Szolgaltato Kft.",
"YourTorExit": "false",
"YourCountryCode": "HU"
}
I want to get the attribute value from XML output that is being created by my Rest Web Service but without the tags in my Java client. I tried XPath but it doesn't seem to work with URLs, only with XML files that are stored in the drive. And all the answers about XPath are specifically for stored XML files not online. I am using Netbeans. The concept is, web service takes two numbers and provides the sum as an XML. Webservice url that I use in this example http://localhost:8080/WSDemo/rest/book/5/2
Rest Web service
ApplicationConfig.java
package wbs;
import java.util.Set;
import javax.ws.rs.core.Application;
import javax.xml.bind.annotation.XmlRootElement;
#javax.ws.rs.ApplicationPath("rest")
public class ApplicationConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
addRestResourceClasses(resources);
return resources;
}
private void addRestResourceClasses(Set<Class<?>> resources) {
resources.add(wbs.GenericResource.class);
}
}
GenericResource.java
package wbs;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.annotation.XmlRootElement;
#Path("book")
public class GenericResource {
#Context
private UriInfo context;
#GET
#Produces(MediaType.APPLICATION_XML)
#Path("{n1}/{n2}")
public String getSum(#PathParam ("n1") int a, #PathParam ("n2") int b) {
int c = a+b;
return "<Sum>" + c + "</Sum>";
}
}
Client
Sum.java
package restclient;
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.WebTarget;
public class Sum {
private WebTarget webTarget;
private Client client;
private static final String BASE_URI = "http://localhost:8080/WSDemo/rest/";
public Sum(){
client = javax.ws.rs.client.ClientBuilder.newClient();
webTarget = client.target(BASE_URI).path("book");
}
public <T> T getSum(Class<T> responseType, String n1, String n2) throws ClientErrorException {
WebTarget resource = webTarget;
resource = resource.path(java.text.MessageFormat.format("{0}/{1}", new Object[]{n1, n2}));
return resource.request(javax.ws.rs.core.MediaType.APPLICATION_XML).get(responseType);
}
public void putXml(Object requestEntity) throws ClientErrorException {
webTarget.request(javax.ws.rs.core.MediaType.APPLICATION_XML).put(javax.ws.rs.client.Entity.entity(requestEntity, javax.ws.rs.core.MediaType.APPLICATION_XML));
}
public void close() {
client.close();
}
}
RestClient.java
package com.emmanouil;
import restclient.Sum;
public class RestClient {
public static void main(String[] args) {
Sum client = new Sum();
String response = client.getSum (String.class,"5" , "2");
System.out.println(response);
client.close();
}
}
Output
I want to clear the tags and get the result (7 in this case). Of course, I can trim the string or any other other similar way but it's something that I don't want to do.
I am trying to send a sms using twillio, here the code:
import com.twilio.sdk.TwilioRestClient;
import com.twilio.sdk.TwilioRestException;
import org.apache.http.NameValuePair;
import org.apache.http.auth.Credentials;
import org.apache.http.message.BasicNameValuePair;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public class SendSms extends HttpServlet {
private Logger logger = Logger.getLogger(SendSms.class.getName());
private TwilioRestClient client;
private Credentials credentials;
#Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws IOException,
ServletException {
final String twilioAccountSid = System.getenv("TWILIO_ACCOUNT_SID");
final String twilioAuthToken = System.getenv("TWILIO_AUTH_TOKEN");
final String twilioNumber = System.getenv("TWILIO_NUMBER");
final String toNumber = (String) twilioNumber;
String message = "prova";
client = new TwilioRestClient(twilioAccountSid, twilioAuthToken);
sendMessage(toNumber, message);
}
public void sendMessage(String to, String message) {
List<NameValuePair> params = getParams(to, message);
try {
this.client.getAccount().getMessageFactory().create(params);
} catch (TwilioRestException exception) {
exception.printStackTrace();
}
}
private List<NameValuePair> getParams(String to, String message) {
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("Body", message));
params.add(new BasicNameValuePair("To", to));
params.add(new BasicNameValuePair("From", to));
return params;
}
}
Is just a test so I don't matter about performarce, code cleaning and so on. Calling the servlet I am getting the following error:
WARNING: Error for /SendSms
java.lang.NoClassDefFoundError: com/twilio/sdk/TwilioRestException
I am using maven, importing the related twillio apis, here the pom dependency:
<dependencies>
<dependency>
<groupId>com.twilio.sdk</groupId>
<artifactId>twilio-java-sdk</artifactId>
<version>(6.0,6.9)</version>
</dependency>
Does someone has some suggestions?
Thank you in advance.
Perhaps the missing <scope> for compile is the problem:
<dependency>
<groupId>com.twilio.sdk</groupId>
<artifactId>twilio-java-sdk</artifactId>
<version>(6.0,6.9)</version>
<scope>compile</scope>
</dependency>
And for the full detailed instructions regarding the dev environment:
https://www.twilio.com/docs/quickstart/java/devenvironment#pomxml
I have a tired application layered like this
WebServices
Services
DAO
Common
I call twilio in the services module, so I put the maven dependency in the pom file in that maven module. For some reason I kept getting the same error as you. Adding the dependency to the WebServices module fixed the issue
I'm currently trying to develop a new module for our existing web application. I'm trying to add LDAP functionality and have problems initializing the LDAP context as the SimpleNamingContextBuilder registers a context that is not working together with the LdapTemplate.
In our spring applicationContext.xml we have several JNDI lookups, so before running a test case I have to create mock JNDI-Resources using the SimpleNamingContextBuilder in the test cases constructor.
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
builder.bind("someJNDIname",someObject); //e.g. for some datasource
builder.activate();
In our Spring application-context-test.xml we have the following ldapConfiguration:
<bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="ldap://ourserver:389" />
<property name="base" value="CN=Groups,CN=ourcompany,DC=com" />
<property name="userDn" value="CN=binduser" />
<property name="password" value="password" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="ldapContextSource" />
</bean>
We run the testcase with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:application-context-test.xml"})
public class TestClass {
public TestClass(){
.. //init the SimpleNamingContextBuilder
}
#Autowired
private LdapTemplate template;
#Test
public void someTestcase(){
ldapTemplate.search("", "(objectclass=user)" ,new LdapUserMapper());
}
}
As the SimpleNamingContextBuilder is already registering a simple InitialContext I get the following error:
org.springframework.ldap.NotContextException: DirContext object is required.; nested exception is javax.naming.NotContextException: DirContext object is required.
at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:198)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:319)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:259)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:571)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:556)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:411)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:431)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:451)
at com.somecompany.TestClass.someTestcase(TestClass.java:30)
[...]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: javax.naming.NotContextException: DirContext object is required.
at javax.naming.directory.InitialDirContext.castToDirContext(InitialDirContext.java:106)
at javax.naming.directory.InitialDirContext.getURLOrDefaultInitDirCtx(InitialDirContext.java:112)
at javax.naming.directory.InitialDirContext.search(InitialDirContext.java:245)
at org.springframework.ldap.core.LdapTemplate$4.executeSearch(LdapTemplate.java:253)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:293)
... 35 more
The error tells me that the LDAP requires a DirContext. How can I get the SimpleNamingContextBuilder to create and use such a DirContext.
If I don't register the SimpleNamingContextBuilder then creating the LDAPTemplate will work. However I will run into other problems as other parts of the application require the JNDI lookups.
I did not manage to get the SimpleNamingContextBuilder to create an instance of DirContext, but using a custom DirContextBuilder was the solution to get around this limitation.
The mocked SimpleNamingContext is mainly there to provide bound objects via e.g.
InitialContext.doLookup(String name)
methods - to let those ones work and provide proper support for e.g. LDAP DirContext instances, just omit the check for the "activated" context - you will bootstrap your code to apply activate() anyway, so this is no problem - at least not for the given JNDI + LDAP test case.
Missing this check, the code looks for the "java.naming.factory.initial" environment key and if the environment is empty (this is the case for InitialContext.doLookup(String name)) you get the mocked SimpleNamingContext with your bound objects.
If you use the LdapTemplate the environment is not empty and the key "java.naming.factory.initial" is set to "com.sun.jndi.ldap.LdapCtxFactory" or something similar which is at least (hopefully) a DirContext.
In this case you get a working DirContext instance back from the createInitialContextFactory call and the LdapTemplate lookup is successful.
So just create a class DirContextBuilder - take the code from SimpleNamingContextBuilder - like this:
public class DirContextBuilder implements InitialContextFactoryBuilder {
...
public InitialContextFactory createInitialContextFactory(Hashtable<?,?> environment) {
if (environment != null) {
...
}
Omit the check for activated == null and you are ready to test your bound JNDI objects and have a working LdapTemplate.
I faced the same issue. But overcome it with the below trick
#BeforeClass
public static void setUp(){
OracleDataSource ods = null;
try {
ods= new OracleDataSource();
} catch (SQLException e) {
e.printStackTrace();
}
ods.setURL("jdbc:oracle:thin:#(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=....;
ods.setUser(..);
ods.setPassword(..);
SimpleNamingContextBuilder builder = null;
try {
builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
builder.bind("some/name", ods);
} catch (NamingException e) {
e.printStackTrace();
}
}
#Before
public void beforeTest(){
SimpleNamingContextBuilder.getCurrentContextBuilder().deactivate();
}
#Test
public void yourTest(){
.....
}
This will bind your database with some/name and also let you to connect to the ldap correctly.
I also faced the same issue. I researched causes and internal behaviour in Java and SpringLdap why it happens. I came to the following decision.
I customized ContextSource bean creation in order to solve it. This method is crutch and requires modification config that checking test mode. But it works.
Below I present simple project demostrated it. For embeded LDAP server I used Apache Directory Server.
CommonConfig.java consisted this crutch:
package com.stackoverflow.question8325740.config;
import com.stackoverflow.question8325740.JndiExplorer;
import com.stackoverflow.question8325740.LdapSettings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.AbstractContextSource;
import org.springframework.ldap.core.support.LdapContextSource;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.NoInitialContextException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.spi.InitialContextFactory;
import javax.naming.spi.NamingManager;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
#Configuration
public class CommonConfig {
private static class CustomLdapContextSource extends AbstractContextSource {
#Override
protected DirContext getDirContextInstance(Hashtable<String, Object> environment) throws NamingException {
return new CustomLdapContext(environment, null);
}
}
private static class CustomLdapContext extends InitialLdapContext {
public CustomLdapContext() throws NamingException {
}
public CustomLdapContext(Hashtable<?, ?> environment, Control[] connCtls) throws NamingException {
super(environment, connCtls);
}
#Override
protected Context getDefaultInitCtx() throws NamingException {
String className = "com.sun.jndi.ldap.LdapCtxFactory";
InitialContextFactory factory;
try {
factory = (InitialContextFactory) Class.forName(className).newInstance();
} catch (Exception e) {
NoInitialContextException ne =
new NoInitialContextException(
"Cannot instantiate class: " + className);
ne.setRootCause(e);
throw ne;
}
return factory.getInitialContext(myProps);
}
}
private static boolean checkTestMode() {
//checking test mode using reflection in order to not collapse in real execution
try {
Class clazz = Class.forName("org.springframework.mock.jndi.SimpleNamingContextBuilder");
Object result = clazz.getMethod("getCurrentContextBuilder").invoke(null);
return NamingManager.hasInitialContextFactoryBuilder() && result != null;
} catch (Exception e) {
return false;
}
}
#Bean
#Autowired
public ContextSource ldapContextSource(LdapSettings ldapSettings) {
AbstractContextSource contextSource;
if (checkTestMode()) {
contextSource = new CustomLdapContextSource();
} else {
contextSource = new LdapContextSource();
}
contextSource.setUrl(ldapSettings.getUrl());
contextSource.setUserDn(ldapSettings.getLogin());
contextSource.setPassword(ldapSettings.getPassword());
contextSource.setPooled(true);
contextSource.setAnonymousReadOnly(false);
Map<String, Object> baseEnvironmentProperties = new HashMap<String, Object>();
baseEnvironmentProperties.put(Context.SECURITY_AUTHENTICATION, "simple");
baseEnvironmentProperties.put(Context.REFERRAL, "follow");
contextSource.setBaseEnvironmentProperties(baseEnvironmentProperties);
return contextSource;
}
#Bean
#Autowired
public LdapOperations ldapTemplate(ContextSource ldapContextSource) {
return new LdapTemplate(ldapContextSource);
}
#Bean
public JndiExplorer jndiExplorer() {
return new JndiExplorer();
}
}
MainTest.java using JNDI and LdapOperations:
package com.stackoverflow.question8325740;
import com.stackoverflow.question8325740.config.CommonConfig;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.ldap.test.LdapTestUtils;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttributes;
import java.util.List;
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {ApacheDsEmbededConfiguration.class, CommonConfig.class})
public class MainTest {
public static final String TEST_VALUE = "testValue";
private static SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
#BeforeAll
public static void setUp() throws Exception {
builder.bind(JndiExplorer.JNDI_TEST, TEST_VALUE);
builder.activate();
LdapTestUtils.startEmbeddedServer(ApacheDsEmbededConfiguration.PORT, ApacheDsEmbededConfiguration.DEFAULT_PARTITION_SUFFIX, "test");
Thread.sleep(1000);
}
#AfterAll
public static void shutdown() throws Exception {
LdapTestUtils.shutdownEmbeddedServer();
builder.deactivate();
}
#Autowired
private JndiExplorer jndiExplorer;
#Autowired
private LdapOperations ldapOperations;
#Test
public void testLdapTemplateWithSimpleJndi() {
Assertions.assertEquals(TEST_VALUE, jndiExplorer.getValue());
String cn = "testCN";
String sn = "testSN";
Attributes attrs = new BasicAttributes();
attrs.put("objectClass", "inetOrgPerson");
attrs.put("cn", cn);
attrs.put("sn", sn);
ldapOperations.bind("cn=" + cn + "," + ApacheDsEmbededConfiguration.DEFAULT_PARTITION_SUFFIX, null, attrs);
AttributesMapper<String> mapper = new AttributesMapper<String>() {
#Override
public String mapFromAttributes(Attributes attributes) throws NamingException {
return (String) attributes.get("sn").get();
}
};
List<String> sns = ldapOperations.search(LdapQueryBuilder.query().base(ApacheDsEmbededConfiguration.DEFAULT_PARTITION_SUFFIX).attributes("*").where("sn").is(sn), mapper);
Assertions.assertEquals(1, sns.size());
String resultSn = sns.get(0);
Assertions.assertEquals(sn, resultSn);
}
}
ApacheDsEmbededConfiguration.java:
package com.stackoverflow.question8325740;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ApacheDsEmbededConfiguration {
//default password
static final String PASSWORD = "secret";
//default admin DN
static final String PRINCIPAL = "uid=admin,ou=system";
static final String DEFAULT_PARTITION_SUFFIX = "dc=stackoverflow,dc=com";
static final int PORT = 1888;
#Bean
public LdapSettings ldapSettings() {
LdapSettings settings = new LdapSettings();
settings.setUrl("ldap://localhost:" + PORT);
settings.setLogin(PRINCIPAL);
settings.setPassword(PASSWORD);
return settings;
}
}
Pojo LdapSettings.java:
package com.stackoverflow.question8325740;
public class LdapSettings {
private String url;
private String login;
private String password;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Bean using JNDI-variable JndiExplorer.java:
package com.stackoverflow.question8325740;
import javax.annotation.Resource;
public class JndiExplorer {
public static final String JNDI_TEST = "com/anything";
#Resource(mappedName = JNDI_TEST)
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
And 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>com.stackoverflow</groupId>
<artifactId>question-8325740</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>5.3.2</junit.version>
<spring.version>5.1.4.RELEASE</spring.version>
<spring.ldap.version>2.3.2.RELEASE</spring.ldap.version>
<apacheDirectoryService.version>1.5.5</apacheDirectoryService.version>
<apacheDirectoryService.shared.version>0.9.15</apacheDirectoryService.shared.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>${spring.ldap.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-test</artifactId>
<version>${spring.ldap.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core</artifactId>
<version>${apacheDirectoryService.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core-entry</artifactId>
<version>${apacheDirectoryService.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-protocol-shared</artifactId>
<version>${apacheDirectoryService.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-protocol-ldap</artifactId>
<version>${apacheDirectoryService.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>${apacheDirectoryService.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.shared</groupId>
<artifactId>shared-ldap</artifactId>
<version>${apacheDirectoryService.shared.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
</plugin>
</plugins>
</build>
</project>