Netty HttpServer api changed/differs from available examples - java

Netty server instantiation in Arjen Poutsma's blog post and Josh Long's video example is done by creating an reactor.ipc.netty.http.HttpServer instance and then calling it's start or startAndAwait method with an ReactorHttpHandlerAdapter instance as an argument.
However the API seems to have changed as now start and startAndAwait methods now expect a lambda with the following signature:
java.util.function.Function<? super reactor.ipc.netty.http.HttpChannel,? extends org.reactivestreams.Publisher<java.lang.Void>>
Project dependencies and their versions are the same as in Arjen Poutsma's example project
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>io.projectreactor.ipc</groupId>
<artifactId>reactor-netty</artifactId>
<version>0.5.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web-reactive</artifactId>
<version>5.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.2</version>
</dependency>
What is the new/proper way of instantiating a netty server with spring reactor support?

The recommended way to set up project for now is to use http://start.spring.io/ as Josh Long suggests in his video. This is because spring reactive is only release candidate now and we need compatible versions to run samples.This is achieved via adding this piece to code:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-dependencies-web-reactive</artifactId>
<version>0.1.0.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
According your question about HttpServer interface change, the minimal working example is the following:
import org.reactivestreams.Publisher;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.RouterFunction;
import org.springframework.web.reactive.function.ServerRequest;
import org.springframework.web.reactive.function.ServerResponse;
import reactor.core.publisher.Mono;
import reactor.ipc.netty.http.server.HttpServer;
import java.io.IOException;
import static org.springframework.web.reactive.function.RequestPredicates.GET;
import static org.springframework.web.reactive.function.RouterFunctions.route;
import static org.springframework.web.reactive.function.RouterFunctions.toHttpHandler;
public class FunctionalReactiveServer {
public static final String HOST = "localhost";
public static final int PORT = 8080;
public static void main(String[] args) throws InterruptedException, IOException {
RouterFunction<?> route = route(GET("/sayHello"), FunctionalReactiveServer::sayHelloHandler);
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
HttpServer server = HttpServer.create(HOST, PORT);
server.newHandler(adapter).block();
System.out.println("Press ENTER to exit.");
System.in.read();
}
public static ServerResponse<Publisher<String>> sayHelloHandler(ServerRequest request) {
return ServerResponse.ok().body(Mono.just("Hello!"), String.class);
}
}

Related

Quarkus, Hibernate ORM and REST - RESTEASY008200: JSON Binding deserialization error:

I'm trying to create a project that uses Hibernate Panache and Rest, similar to the quickstart on https://github.com/quarkusio/quarkus-quickstarts/tree/master/hibernate-orm-panache-resteasy.
When I try to #Post an entity that extends PanacheEntity, as shown below, I get the following error:
javax.ws.rs.ProcessingException: RESTEASY008200: JSON Binding deserialization error: Can't create instance
Entity
#Entity
#Cacheable
class Trade extends PanacheEntity {
#Column(length = 40, unique = true)
String name;
}
Rest resource
import javax.enterprise.context.ApplicationScoped;
import javax.transaction.Transactional;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
#Path("/trades")
#ApplicationScoped
#Produces("application/json")
#Consumes("application/json")
public class TradeReporterResource {
#POST
#Transactional
public Response add(Trade trade) {
System.out.println("begin");
//t.closePrice = trade.closePrice;
System.out.println("persisting");
trade.persist();
System.out.println("persisted");
return Response.ok(trade).build();
}
}
Pom dependencies
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bom</artifactId>
<version>${quarkus.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
</dependencies>
Problem appears to be with Penache
When I remove the extends PanacheEntity from the Trade entity, then at least I can POST successfully.
The problem turns out to be rather simple, all you need to do is make class Trade a public class.
It should be noted that this is not a Quarkus limitation, but a JSON-B limitation (which requires de-serialized classes to have a public or protected no-arg constructor - see section 3.7 of the JSON-B spec)

Accessing Azure Service Bus with Apache Camel?

How can you access the Azure Service Bus using Apache Camel using Camel as a standalone Java application?
If you are trying to access Azure Service Bus using Apache Camel you can do so by using the Camel AMQ libraries.
You can use the following Maven dependencies in case of you are using Maven:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-parent</artifactId>
<version>2.17.6</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jms</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-amqp</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.qpid/qpid-amqp-1-0-client-jms -->
<dependency>
<groupId>org.apache.qpid</groupId>
<artifactId>qpid-amqp-1-0-client-jms</artifactId>
<version>0.32</version>
</dependency>
</dependencies>
Note: I am using a relatively old version of Apache Camel here, but this setup should work with newer versions too.
If you want to create an endpoint for consuming messages from the Azure Service Bus you can create an AMQComponent which acts as a connection factory bind it to the registry and then use it to listen to messages.
Here is an example:
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.amqp.AMQPComponent;
import org.apache.camel.main.Main;
import org.apache.qpid.amqp_1_0.jms.impl.ConnectionFactoryImpl;
public class AzureMQToFileAMQ {
public static void main(String[] args) throws Exception {
Main main = new Main();
AMQPComponent connectionFactory = new AMQPComponent(
ConnectionFactoryImpl
.createFromURL("amqps://"
+ "dev.emea-uk-test.q:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx#xxxxxxxxxxxxxxxxxxxxxxxxxxx.servicebus.windows.net"
+ ":" + "5671"));
main.bind("amqp", connectionFactory);
main.addRouteBuilder(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("amqp:queue:dev.emea.uk.test.q?consumerType=Simple")
.process(exchange -> {
final String body = new String((byte[])exchange.getIn().getBody());
System.out.println(body);
});
}
});
main.run();
}
}
This project is a very good option.

geoserver embedded in spring boot and hibernate/jpa conflict

I am trying to create a web application with spring boot web that needs geoserver to deserve GeoJSON layers.
I succed to launch geoserver in the same tomcat that my spring-boot application. But there is a problem when GeoServer is trying to access postgresql/postgis database. Geoserver said:
Error occurred getting featuresUnable to obtain connection: Cannot create PoolableConnectionFactory
Due to:
java.lang.ClassCastException: class org.postgis.PGbox2d
at java.lang.Class.asSubclass(Class.java:3404) ~[na:1.8.0_151]
at org.postgresql.jdbc.PgConnection.initObjectTypes(PgConnection.java:645) ~[postgresql-42.1.1.jar:42.1.1]
at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:296) ~[postgresql-42.1.1.jar:42.1.1]
at org.postgresql.Driver.makeConnection(Driver.java:450) ~[postgresql-42.1.1.jar:42.1.1]
at org.postgresql.Driver.connect(Driver.java:252) ~[postgresql-42.1.1.jar:42.1.1]
...
I can access to data stored in postgresql/postgis via spring application.
Here there is my pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>net.postgis</groupId>
<artifactId>postgis-jdbc</artifactId>
<version>2.2.0</version>
<exclusions>
<exclusion>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
<version>5.2.12.Final</version>
</dependency>
<dependency>
<groupId>com.bedatadriven</groupId>
<artifactId>jackson-datatype-jts</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
There is also Application:
package fr.app;
import fr.app.dao.GreffeRepository;
import fr.app.util.TomcatDeployer;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import java.util.logging.Logger;
#SpringBootApplication
public class GroLocApplication
{
private static final Logger LOGGER = Logger.getLogger(GroLocApplication.class.getName());
public static void main(String... args)
{
SpringApplication.run(GroLocApplication.class, args);
}
#Bean
public EmbeddedServletContainerFactory servletContainerFactory()
{
return new TomcatDeployer();
}
}
The TomcatDeployer, that deploy war in war folder in resources:
package fr.app.util;
import org.apache.catalina.Context;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.logging.Logger;
final public class TomcatDeployer extends TomcatEmbeddedServletContainerFactory
{
private static final Logger LOGGER = Logger.getLogger(TomcatDeployer.class.getName());
private static final String WAR_TO_DEPLOY = "war/geoserver.war";
private String copyResourceToTmp() throws IllegalStateException
{
try (InputStream resourceStream = getClass().getClassLoader().getResourceAsStream(WAR_TO_DEPLOY))
{
File tmpWar = File.createTempFile("geoserver", ".war");
Files.copy(resourceStream, tmpWar.toPath(), StandardCopyOption.REPLACE_EXISTING);
return tmpWar.getAbsolutePath();
}
catch (IOException e)
{
throw new IllegalStateException("Cannot copy geoserver.war resource", e);
}
}
#Override
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat)
throws IllegalStateException
{
if (!new File(tomcat.getServer().getCatalinaBase(), "webapps").mkdirs())
{
throw new IllegalStateException("Cannot create a webapps directory on tomcat");
}
try
{
final String pathToWar = copyResourceToTmp();
LOGGER.info(pathToWar);
final Context context = tomcat.addWebapp("/geoserver", pathToWar);
final WebappLoader loader = new WebappLoader(Thread.currentThread().getContextClassLoader());
context.setLoader(loader);
}
catch (ServletException e)
{
throw new IllegalStateException("Failed to add geoserver", e);
}
return super.getTomcatEmbeddedServletContainer(tomcat);
}
}
Greffe entity:
package fr.app.entity;
import com.bedatadriven.jackson.datatype.jts.serialization.GeometryDeserializer;
import com.bedatadriven.jackson.datatype.jts.serialization.GeometrySerializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.vividsolutions.jts.geom.Geometry;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Greffe
{
#Id
private String id;
#JsonSerialize(using = GeometrySerializer.class)
#JsonDeserialize(using = GeometryDeserializer.class)
private Geometry coord;
// getters...
}
And dao is a classic CrudRepository<Greffe,String> extends.
So is it possible to really embed GeoServer in the same tomcat that the spring-boot application's tomcat? (it's possible because I did so, but is it really a good idea?)
And if so, how can I avoid this kind of trouble?!
Thanks by advance :)
I would not embed GeoServer in the same Tomcat your application is using. Yes, it is possible, yes; but not a really good idea.
Reasoning: During Tomcat startup, GeoServer and your application is not running (yet) - but if your application needs GeoServer during startup it must fail.
Solution: Put GeoServer in a serparat Tomcat that starts up before your application is starting.
And to your trouble (and the ClassCastException):
Make sure to use a postgres and postgis driver version matching each other and the database version you are using.
The error you get seems related to https://github.com/brettwooldridge/HikariCP/issues/1476 - maybe the information there can help you.
I personally never heard of com.bedatadriven.jackson.datatype.jts - make sure the depending JTS version of that library matches the versions used in your GeoServer.
And as you are using HibernateSpatial: make sure to configure HibernateSpatial correct. I did not see a configuration.
Maybe using hibernate-spatial for postgis is a solution:
<groupId>org.hibernatespatial</groupId>
<artifactId>hibernate-spatial-postgis</artifactId>

Mocking an Apache Commons CSV CSVRecord

At some point my code needs to touch a CSVRecord and I can't figure out a way to create a mock version of it.
The class is final so it can't be mocked. The constructor is private so I can't create an instance of it. How does one approach testing code that uses the CSVRecord class?
Right now the only solution that works is parsing a test fixture to get an instance of the object. Is this my best approach?
You can use Powermock. More info: https://github.com/powermock/powermock/wiki/mockfinal
example:
import org.apache.commons.csv.CSVRecord;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest({CSVRecord.class}) // needed to mock final classes and static methods
public class YourTestClass {
#Test
public void testCheckValidNum_null() {
String columnName = "colName";
CSVRecord record = mock(CSVRecord.class);
String contentsOfCol = "hello";
String result;
when(record.get(columnName)).thenReturn(contentsOfCol);
result = record.get(columnName);
assertEquals(contentsOfCol, result);
}
}
Here are my maven includes (there are newer versions of libraries, this is just what I'm using):
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.5</version>
<scope>test</scope>
</dependency>

Adding a .jar file to classpath through maven

I'm having some trouble in getting maven to download a number of .jar files my application depends on. The code in which these dependencies are needed is bellow:
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ServerConfiguration {
public String info = null;
public String idlURL = null;
public String idlContents = null;
public List<ServerInfo> servers = new ArrayList<>();
public final void clear() {
info = null;
idlURL = null;
idlContents = null;
if (servers != null)
servers.clear();
}
private final static ObjectReader jsonReader;
private final static ObjectWriter jsonWriter;
static {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); // <== Error:(52, 15) java: cannot access com.fasterxml.jackson.core.JsonGenerator class file for com.fasterxml.jackson.core.JsonGenerator not found
//mapper.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, true);
jsonWriter = mapper.writer();
jsonReader = mapper.reader(ServerConfiguration.class);
}
public static ServerConfiguration fromJson(String json) throws IOException {
return jsonReader.<ServerConfiguration>readValue(json); // <== Error:(59, 26) java: cannot access com.fasterxml.jackson.core.JsonProcessingException class file for com.fasterxml.jackson.core.JsonProcessingException not found
}
public String toJson() throws IOException {
return jsonWriter.writeValueAsString(this);
}
}
After reading this question, I tried adding the mentioned packages(jackson-databind, jackson-core) to pom.xml:
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>17.0</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.21.Final</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>4.2.2</version>
</dependency>
</dependencies>
How can I add the respective dependencies?
Edit #1:
The errors given are bellow(the lines where they occur are marked in the code above):
Error:(52, 15) java: cannot access com.fasterxml.jackson.core.JsonGenerator
class file for com.fasterxml.jackson.core.JsonGenerator not found
Error:(54, 28) java: cannot access com.fasterxml.jackson.core.ObjectCodec
class file for com.fasterxml.jackson.core.ObjectCodec not found
Error:(55, 28) java: cannot access com.fasterxml.jackson.core.Base64Variant
class file for com.fasterxml.jackson.core.Base64Variant not found
Error:(59, 26) java: cannot access com.fasterxml.jackson.core.JsonProcessingException
class file for com.fasterxml.jackson.core.JsonProcessingException not found
Error:(63, 26) java: cannot access com.fasterxml.jackson.core.Versioned
class file for com.fasterxml.jackson.core.Versioned not found
Edit #2:
I can't seem to add the dependencies:
Can you try with 2.5.4 version as below:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.5.4</version>
</dependency>
In IntelliJ, try to tick a checkbox "export" in your dependencies.
I mean: Project Structure -> Modules -> Dependencies, and there you can see libs included to the module. You should also see a checkbox near each lib in column 'Export'.
For databind you want this:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.3</version>
</dependency>
What is missing? Can you post an error message or stack trace?
Here is a good site to find dependencies for maven.

Categories