Bael 5973 - RestExpress example (#13195)

* BAEL-5973 - Restful Microservice with Rest Express

* BAEL-5973 - Restful Microservice with Rest Express

* BAEL-5973 - pom correction

* BAEL-5973 - pom correction

* BAEL-5973 - moving rest-express to microservices modules

* BAEL-5973 - formatting changes

* BAEL-5973 - pom corrections
This commit is contained in:
Abhinav Pandey 2023-01-11 19:30:25 +05:30 committed by GitHub
parent 86aedea66b
commit 7db81e56ea
29 changed files with 1099 additions and 0 deletions

View File

@ -19,6 +19,7 @@
<module>microprofile</module>
<module>msf4j</module>
<module>open-liberty</module>
<module>rest-express</module>
</modules>
</project>

View File

@ -0,0 +1,5 @@
## RestExpress
This module contains articles about RestExpress.
### Relevant articles

View File

@ -0,0 +1,153 @@
<?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">
<parent>
<artifactId>microservices-modules</artifactId>
<groupId>com.baeldung</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<name>rest-express</name>
<!--
To run the project: mvn clean package exec:java
* mongod must be running.
To create a project deployable assembly (zip file):
mvn clean package
mvn assembly:single
-->
<description>A Basic, MongoDB-backed Service Suite</description>
<url>https://github.com/RestExpress/RestExpress-Scaffold</url>
<version>1.0.0-SNAPSHOT</version>
<artifactId>rest-express</artifactId>
<packaging>jar</packaging>
<properties>
<RestExpress.plugin.version>0.3.3</RestExpress.plugin.version>
<metrics-graphite.version>3.1.2</metrics-graphite.version>
<HyperExpressPlugin.version>2.6</HyperExpressPlugin.version>
<RestExpress.version>0.11.3</RestExpress.version>
<Syntaxe.version>1.0</Syntaxe.version>
<repoexpress-mongodb.version>0.4.8</repoexpress-mongodb.version>
<junit4.version>4.11</junit4.version>
</properties>
<dependencies>
<dependency>
<groupId>com.strategicgains</groupId>
<artifactId>RestExpress</artifactId>
<version>${RestExpress.version}</version>
</dependency>
<dependency>
<groupId>com.strategicgains</groupId>
<artifactId>Syntaxe</artifactId>
<version>${Syntaxe.version}</version>
</dependency>
<dependency>
<groupId>com.strategicgains.repoexpress</groupId>
<artifactId>repoexpress-mongodb</artifactId>
<version>${repoexpress-mongodb.version}</version>
</dependency>
<dependency>
<groupId>com.strategicgains.plugin-express</groupId>
<artifactId>CacheControlPlugin</artifactId>
<version>${RestExpress.plugin.version}</version>
</dependency>
<dependency>
<groupId>com.strategicgains</groupId>
<artifactId>HyperExpressPlugin</artifactId>
<version>${HyperExpressPlugin.version}</version>
</dependency>
<dependency>
<groupId>com.strategicgains.plugin-express</groupId>
<artifactId>MetricsPlugin</artifactId>
<version>${RestExpress.plugin.version}</version>
</dependency>
<dependency>
<groupId>com.strategicgains.plugin-express</groupId>
<artifactId>SwaggerPlugin</artifactId>
<version>${RestExpress.plugin.version}</version>
</dependency>
<dependency>
<groupId>com.strategicgains.plugin-express</groupId>
<artifactId>CORSPlugin</artifactId>
<version>${RestExpress.plugin.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-graphite</artifactId>
<version>${metrics-graphite.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit4.version}</version>
<type>jar</type>
<scope>test</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>com.baeldung.restexpress.Main</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.baeldung.restexpress.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>2.0</version>
</plugin>
</plugins>
</reporting>
</project>

View File

@ -0,0 +1,75 @@
package com.baeldung.restexpress;
import com.baeldung.restexpress.objectid.SampleOidEntityController;
import com.baeldung.restexpress.objectid.SampleOidEntityRepository;
import com.baeldung.restexpress.objectid.SampleOidEntityService;
import com.baeldung.restexpress.uuid.SampleUuidEntityController;
import com.baeldung.restexpress.uuid.SampleUuidEntityRepository;
import com.baeldung.restexpress.uuid.SampleUuidEntityService;
import com.strategicgains.repoexpress.mongodb.MongoConfig;
import com.strategicgains.restexpress.plugin.metrics.MetricsConfig;
import org.restexpress.RestExpress;
import org.restexpress.util.Environment;
import java.util.Properties;
public class Configuration
extends Environment {
private static final String DEFAULT_EXECUTOR_THREAD_POOL_SIZE = "20";
private static final String PORT_PROPERTY = "port";
private static final String BASE_URL_PROPERTY = "base.url";
private static final String EXECUTOR_THREAD_POOL_SIZE = "executor.threadPool.size";
private int port;
private String baseUrl;
private int executorThreadPoolSize;
private MetricsConfig metricsSettings;
private SampleUuidEntityController sampleUuidController;
private SampleOidEntityController sampleOidController;
@Override
protected void fillValues(Properties p) {
this.port = Integer.parseInt(p.getProperty(PORT_PROPERTY, String.valueOf(RestExpress.DEFAULT_PORT)));
this.baseUrl = p.getProperty(BASE_URL_PROPERTY, "http://localhost:" + String.valueOf(port));
this.executorThreadPoolSize = Integer.parseInt(p.getProperty(EXECUTOR_THREAD_POOL_SIZE, DEFAULT_EXECUTOR_THREAD_POOL_SIZE));
this.metricsSettings = new MetricsConfig(p);
MongoConfig mongo = new MongoConfig(p);
initialize(mongo);
}
private void initialize(MongoConfig mongo) {
SampleUuidEntityRepository samplesUuidRepository = new SampleUuidEntityRepository(mongo.getClient(), mongo.getDbName());
SampleUuidEntityService sampleUuidService = new SampleUuidEntityService(samplesUuidRepository);
sampleUuidController = new SampleUuidEntityController(sampleUuidService);
SampleOidEntityRepository samplesOidRepository = new SampleOidEntityRepository(mongo.getClient(), mongo.getDbName());
SampleOidEntityService sampleOidService = new SampleOidEntityService(samplesOidRepository);
sampleOidController = new SampleOidEntityController(sampleOidService);
}
public int getPort() {
return port;
}
public String getBaseUrl() {
return baseUrl;
}
public int getExecutorThreadPoolSize() {
return executorThreadPoolSize;
}
public MetricsConfig getMetricsConfig() {
return metricsSettings;
}
public SampleUuidEntityController getSampleUuidEntityController() {
return sampleUuidController;
}
public SampleOidEntityController getSampleOidEntityController() {
return sampleOidController;
}
}

View File

@ -0,0 +1,23 @@
package com.baeldung.restexpress;
public class Constants {
/**
* These define the URL parmaeters used in the route definition strings (e.g. '{userId}').
*/
public class Url {
//TODO: Your URL parameter names here...
public static final String SAMPLE_ID = "uuid";
}
/**
* These define the route names used in naming each route definitions. These names are used
* to retrieve URL patterns within the controllers by name to create links in responses.
*/
public class Routes {
//TODO: Your Route names here...
public static final String SINGLE_UUID_SAMPLE = "sample.single.route.uuid";
public static final String SAMPLE_UUID_COLLECTION = "sample.collection.route.uuid";
public static final String SINGLE_OID_SAMPLE = "sample.single.route.oid";
public static final String SAMPLE_OID_COLLECTION = "sample.collection.route.oid";
}
}

View File

@ -0,0 +1,33 @@
package com.baeldung.restexpress;
import com.strategicgains.repoexpress.domain.Timestamped;
import com.strategicgains.util.date.DateAdapter;
import com.strategicgains.util.date.HttpHeaderTimestampAdapter;
import org.restexpress.Request;
import org.restexpress.Response;
import org.restexpress.pipeline.Postprocessor;
import static io.netty.handler.codec.http.HttpHeaders.Names.LAST_MODIFIED;
/**
* Assigns the Last-Modified HTTP header on the response for GET responses, if applicable.
*
* @author toddf
* @since May 15, 2012
*/
public class LastModifiedHeaderPostprocessor
implements Postprocessor {
DateAdapter fmt = new HttpHeaderTimestampAdapter();
@Override
public void process(Request request, Response response) {
if (!request.isMethodGet()) return;
if (!response.hasBody()) return;
Object body = response.getBody();
if (!response.hasHeader(LAST_MODIFIED) && body.getClass().isAssignableFrom(Timestamped.class)) {
response.addHeader(LAST_MODIFIED, fmt.format(((Timestamped) body).getUpdatedAt()));
}
}
}

View File

@ -0,0 +1,11 @@
package com.baeldung.restexpress;
import org.restexpress.util.Environment;
public class Main {
public static void main(String[] args) throws Exception {
Configuration config = Environment.load(args, Configuration.class);
Server server = new Server(config);
server.start().awaitShutdown();
}
}

View File

@ -0,0 +1,55 @@
package com.baeldung.restexpress;
import com.baeldung.restexpress.objectid.SampleOidEntity;
import com.baeldung.restexpress.uuid.SampleUuidEntity;
import com.strategicgains.hyperexpress.HyperExpress;
import com.strategicgains.hyperexpress.RelTypes;
import org.restexpress.RestExpress;
import org.restexpress.common.exception.ConfigurationException;
import java.util.Map;
public abstract class Relationships {
private static Map<String, String> ROUTES;
public static void define(RestExpress server) {
ROUTES = server.getRouteUrlsByName();
HyperExpress.relationships()
.forCollectionOf(SampleUuidEntity.class)
.rel(RelTypes.SELF, href(Constants.Routes.SAMPLE_UUID_COLLECTION))
.withQuery("limit={limit}")
.withQuery("offset={offset}")
.rel(RelTypes.NEXT, href(Constants.Routes.SAMPLE_UUID_COLLECTION) + "?offset={nextOffset}")
.withQuery("limit={limit}")
.optional()
.rel(RelTypes.PREV, href(Constants.Routes.SAMPLE_UUID_COLLECTION) + "?offset={prevOffset}")
.withQuery("limit={limit}")
.optional()
.forClass(SampleUuidEntity.class)
.rel(RelTypes.SELF, href(Constants.Routes.SINGLE_UUID_SAMPLE))
.rel(RelTypes.UP, href(Constants.Routes.SAMPLE_UUID_COLLECTION))
.forCollectionOf(SampleOidEntity.class)
.rel(RelTypes.SELF, href(Constants.Routes.SAMPLE_OID_COLLECTION))
.withQuery("limit={limit}")
.withQuery("offset={offset}")
.rel(RelTypes.NEXT, href(Constants.Routes.SAMPLE_OID_COLLECTION) + "?offset={nextOffset}")
.withQuery("limit={limit}")
.optional()
.rel(RelTypes.PREV, href(Constants.Routes.SAMPLE_OID_COLLECTION) + "?offset={prevOffset}")
.withQuery("limit={limit}")
.optional()
.forClass(SampleOidEntity.class)
.rel(RelTypes.SELF, href(Constants.Routes.SINGLE_OID_SAMPLE))
.rel(RelTypes.UP, href(Constants.Routes.SAMPLE_OID_COLLECTION));
}
private static String href(String name) {
String href = ROUTES.get(name);
if (href == null) throw new ConfigurationException("Route name not found: " + name);
return href;
}
}

View File

@ -0,0 +1,30 @@
package com.baeldung.restexpress;
import io.netty.handler.codec.http.HttpMethod;
import org.restexpress.RestExpress;
public abstract class Routes {
public static void define(Configuration config, RestExpress server) {
// TODO: Your routes here...
server.uri("/samples/uuid/{uuid}.{format}", config.getSampleUuidEntityController())
.method(HttpMethod.GET, HttpMethod.PUT, HttpMethod.DELETE)
.name(Constants.Routes.SINGLE_UUID_SAMPLE);
server.uri("/samples/uuid.{format}", config.getSampleUuidEntityController())
.action("readAll", HttpMethod.GET)
.method(HttpMethod.POST)
.name(Constants.Routes.SAMPLE_UUID_COLLECTION);
server.uri("/samples/oid/{uuid}.{format}", config.getSampleOidEntityController())
.method(HttpMethod.GET, HttpMethod.PUT, HttpMethod.DELETE)
.name(Constants.Routes.SINGLE_OID_SAMPLE);
server.uri("/samples/oid.{format}", config.getSampleOidEntityController())
.action("readAll", HttpMethod.GET)
.method(HttpMethod.POST)
.name(Constants.Routes.SAMPLE_OID_COLLECTION);
// or REGEX matching routes...
// server.regex("/some.regex", config.getRouteController());
}
}

View File

@ -0,0 +1,129 @@
package com.baeldung.restexpress;
import com.baeldung.restexpress.serialization.SerializationProvider;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.graphite.Graphite;
import com.codahale.metrics.graphite.GraphiteReporter;
import com.strategicgains.repoexpress.adapter.Identifiers;
import com.strategicgains.repoexpress.exception.DuplicateItemException;
import com.strategicgains.repoexpress.exception.InvalidObjectIdException;
import com.strategicgains.repoexpress.exception.ItemNotFoundException;
import com.strategicgains.restexpress.plugin.cache.CacheControlPlugin;
import com.strategicgains.restexpress.plugin.cors.CorsHeaderPlugin;
import com.strategicgains.restexpress.plugin.metrics.MetricsConfig;
import com.strategicgains.restexpress.plugin.metrics.MetricsPlugin;
import com.strategicgains.restexpress.plugin.swagger.SwaggerPlugin;
import com.strategicgains.syntaxe.ValidationException;
import org.restexpress.Flags;
import org.restexpress.RestExpress;
import org.restexpress.exception.BadRequestException;
import org.restexpress.exception.ConflictException;
import org.restexpress.exception.NotFoundException;
import org.restexpress.pipeline.SimpleConsoleLogMessageObserver;
import org.restexpress.plugin.hyperexpress.HyperExpressPlugin;
import org.restexpress.plugin.hyperexpress.Linkable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
import static org.restexpress.Flags.Auth.PUBLIC_ROUTE;
public class Server {
private static final String SERVICE_NAME = "TODO: Enter service name";
private static final Logger LOG = LoggerFactory.getLogger(SERVICE_NAME);
private RestExpress server;
private Configuration config;
private boolean isStarted = false;
public Server(Configuration config) {
this.config = config;
RestExpress.setDefaultSerializationProvider(new SerializationProvider());
Identifiers.UUID.useShortUUID(true);
this.server = new RestExpress()
.setName(SERVICE_NAME)
.setBaseUrl(config.getBaseUrl())
.setExecutorThreadCount(config.getExecutorThreadPoolSize())
.addMessageObserver(new SimpleConsoleLogMessageObserver());
Routes.define(config, server);
Relationships.define(server);
configurePlugins(config, server);
mapExceptions(server);
}
public Server start() {
if (!isStarted) {
server.bind(config.getPort());
isStarted = true;
}
return this;
}
public void awaitShutdown() {
if (isStarted) server.awaitShutdown();
}
public void shutdown() {
if (isStarted) server.shutdown();
}
private void configurePlugins(Configuration config, RestExpress server) {
configureMetrics(config, server);
new SwaggerPlugin()
.flag(Flags.Auth.PUBLIC_ROUTE)
.register(server);
new CacheControlPlugin()
.register(server);
new HyperExpressPlugin(Linkable.class)
.register(server);
new CorsHeaderPlugin("*")
.flag(PUBLIC_ROUTE)
.allowHeaders(CONTENT_TYPE, ACCEPT, AUTHORIZATION, REFERER, LOCATION)
.exposeHeaders(LOCATION)
.register(server);
}
private void configureMetrics(Configuration config, RestExpress server) {
MetricsConfig mc = config.getMetricsConfig();
if (mc.isEnabled()) {
MetricRegistry registry = new MetricRegistry();
new MetricsPlugin(registry)
.register(server);
if (mc.isGraphiteEnabled()) {
final Graphite graphite = new Graphite(new InetSocketAddress(mc.getGraphiteHost(), mc.getGraphitePort()));
final GraphiteReporter reporter = GraphiteReporter.forRegistry(registry)
.prefixedWith(mc.getPrefix())
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.filter(MetricFilter.ALL)
.build(graphite);
reporter.start(mc.getPublishSeconds(), TimeUnit.SECONDS);
} else {
LOG.warn("*** Graphite Metrics Publishing is Disabled ***");
}
} else {
LOG.warn("*** Metrics Generation is Disabled ***");
}
}
private void mapExceptions(RestExpress server) {
server
.mapException(ItemNotFoundException.class, NotFoundException.class)
.mapException(DuplicateItemException.class, ConflictException.class)
.mapException(ValidationException.class, BadRequestException.class)
.mapException(InvalidObjectIdException.class, BadRequestException.class);
}
}

View File

@ -0,0 +1,32 @@
package com.baeldung.restexpress.objectid;
import com.baeldung.restexpress.Constants;
import com.strategicgains.hyperexpress.annotation.BindToken;
import com.strategicgains.hyperexpress.annotation.TokenBindings;
import com.strategicgains.repoexpress.mongodb.AbstractMongodbEntity;
import org.restexpress.plugin.hyperexpress.Linkable;
/**
* This is a sample entity identified by a MongoDB ObjectID (instead of a UUID).
* It also contains createdAt and updatedAt properties that are automatically maintained
* by the persistence layer (SampleOidEntityRepository).
*/
@TokenBindings({
@BindToken(value = Constants.Url.SAMPLE_ID, field = "id")
})
public class SampleOidEntity
extends AbstractMongodbEntity
implements Linkable {
private String name;
public SampleOidEntity() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,80 @@
package com.baeldung.restexpress.objectid;
import com.baeldung.restexpress.Constants;
import com.strategicgains.hyperexpress.builder.DefaultTokenResolver;
import com.strategicgains.hyperexpress.builder.DefaultUrlBuilder;
import com.strategicgains.hyperexpress.builder.UrlBuilder;
import com.strategicgains.repoexpress.mongodb.Identifiers;
import io.netty.handler.codec.http.HttpMethod;
import org.restexpress.Request;
import org.restexpress.Response;
import org.restexpress.common.query.QueryFilter;
import org.restexpress.common.query.QueryOrder;
import org.restexpress.common.query.QueryRange;
import org.restexpress.query.QueryFilters;
import org.restexpress.query.QueryOrders;
import org.restexpress.query.QueryRanges;
import java.util.List;
/**
* This is the 'controller' layer, where HTTP details are converted to domain concepts and passed to the service layer.
* Then service layer response information is enhanced with HTTP details, if applicable, for the response.
* <p/>
* This controller demonstrates how to process an entity that is identified by a MongoDB ObjectId.
*/
public class SampleOidEntityController {
private static final UrlBuilder LOCATION_BUILDER = new DefaultUrlBuilder();
private SampleOidEntityService service;
public SampleOidEntityController(SampleOidEntityService sampleService) {
super();
this.service = sampleService;
}
public SampleOidEntity create(Request request, Response response) {
SampleOidEntity entity = request.getBodyAs(SampleOidEntity.class, "Resource details not provided");
SampleOidEntity saved = service.create(entity);
// Construct the response for create...
response.setResponseCreated();
// Include the Location header...
String locationPattern = request.getNamedUrl(HttpMethod.GET, Constants.Routes.SINGLE_OID_SAMPLE);
response.addLocationHeader(LOCATION_BUILDER.build(locationPattern, new DefaultTokenResolver()));
// Return the newly-created resource...
return saved;
}
public SampleOidEntity read(Request request, Response response) {
String id = request.getHeader(Constants.Url.SAMPLE_ID, "No resource ID supplied");
SampleOidEntity entity = service.read(Identifiers.MONGOID.parse(id));
return entity;
}
public List<SampleOidEntity> readAll(Request request, Response response) {
QueryFilter filter = QueryFilters.parseFrom(request);
QueryOrder order = QueryOrders.parseFrom(request);
QueryRange range = QueryRanges.parseFrom(request, 20);
List<SampleOidEntity> entities = service.readAll(filter, range, order);
long count = service.count(filter);
response.setCollectionResponse(range, entities.size(), count);
return entities;
}
public void update(Request request, Response response) {
String id = request.getHeader(Constants.Url.SAMPLE_ID, "No resource ID supplied");
SampleOidEntity entity = request.getBodyAs(SampleOidEntity.class, "Resource details not provided");
entity.setId(Identifiers.MONGOID.parse(id));
service.update(entity);
response.setResponseNoContent();
}
public void delete(Request request, Response response) {
String id = request.getHeader(Constants.Url.SAMPLE_ID, "No resource ID supplied");
service.delete(Identifiers.MONGOID.parse(id));
response.setResponseNoContent();
}
}

View File

@ -0,0 +1,12 @@
package com.baeldung.restexpress.objectid;
import com.mongodb.MongoClient;
import com.strategicgains.repoexpress.mongodb.MongodbEntityRepository;
public class SampleOidEntityRepository
extends MongodbEntityRepository<SampleOidEntity> {
@SuppressWarnings("unchecked")
public SampleOidEntityRepository(MongoClient mongo, String dbName) {
super(mongo, dbName, SampleOidEntity.class);
}
}

View File

@ -0,0 +1,48 @@
package com.baeldung.restexpress.objectid;
import com.strategicgains.repoexpress.domain.Identifier;
import com.strategicgains.syntaxe.ValidationEngine;
import org.restexpress.common.query.QueryFilter;
import org.restexpress.common.query.QueryOrder;
import org.restexpress.common.query.QueryRange;
import java.util.List;
/**
* This is the 'service' or 'business logic' layer, where business logic, syntactic and semantic
* domain validation occurs, along with calls to the persistence layer.
*/
public class SampleOidEntityService {
private SampleOidEntityRepository samples;
public SampleOidEntityService(SampleOidEntityRepository samplesRepository) {
super();
this.samples = samplesRepository;
}
public SampleOidEntity create(SampleOidEntity entity) {
ValidationEngine.validateAndThrow(entity);
return samples.create(entity);
}
public SampleOidEntity read(Identifier id) {
return samples.read(id);
}
public void update(SampleOidEntity entity) {
ValidationEngine.validateAndThrow(entity);
samples.update(entity);
}
public void delete(Identifier id) {
samples.delete(id);
}
public List<SampleOidEntity> readAll(QueryFilter filter, QueryRange range, QueryOrder order) {
return samples.readAll(filter, range, order);
}
public long count(QueryFilter filter) {
return samples.count(filter);
}
}

View File

@ -0,0 +1,35 @@
package com.baeldung.restexpress.serialization;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.strategicgains.hyperexpress.domain.hal.HalResource;
import com.strategicgains.hyperexpress.serialization.jackson.HalResourceDeserializer;
import com.strategicgains.hyperexpress.serialization.jackson.HalResourceSerializer;
import org.bson.types.ObjectId;
import org.restexpress.ContentType;
import org.restexpress.serialization.json.JacksonJsonProcessor;
import java.util.UUID;
public class JsonSerializationProcessor
extends JacksonJsonProcessor {
public JsonSerializationProcessor() {
super();
addSupportedMediaTypes(ContentType.HAL_JSON);
}
@Override
protected void initializeModule(SimpleModule module) {
super.initializeModule(module);
// For UUID as entity identifiers...
module.addDeserializer(UUID.class, new UuidDeserializer());
module.addSerializer(UUID.class, new UuidSerializer());
// For MongoDB ObjectId as entity identifiers...
module.addDeserializer(ObjectId.class, new ObjectIdDeserializer());
module.addSerializer(ObjectId.class, new ObjectIdSerializer());
// Support HalResource (de)serialization.
module.addDeserializer(HalResource.class, new HalResourceDeserializer());
module.addSerializer(HalResource.class, new HalResourceSerializer());
}
}

View File

@ -0,0 +1,19 @@
package com.baeldung.restexpress.serialization;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.strategicgains.repoexpress.mongodb.Identifiers;
import org.bson.types.ObjectId;
import java.io.IOException;
public class ObjectIdDeserializer
extends JsonDeserializer<ObjectId> {
@Override
public ObjectId deserialize(JsonParser json, DeserializationContext context)
throws IOException, JsonProcessingException {
return (ObjectId) Identifiers.MONGOID.parse(json.getText()).primaryKey();
}
}

View File

@ -0,0 +1,18 @@
package com.baeldung.restexpress.serialization;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.bson.types.ObjectId;
import java.io.IOException;
public class ObjectIdSerializer
extends JsonSerializer<ObjectId> {
@Override
public void serialize(ObjectId objectId, JsonGenerator json, SerializerProvider provider)
throws IOException, JsonProcessingException {
json.writeString(objectId.toString());
}
}

View File

@ -0,0 +1,29 @@
package com.baeldung.restexpress.serialization;
import org.restexpress.response.ErrorResponseWrapper;
import org.restexpress.response.ResponseWrapper;
import org.restexpress.serialization.AbstractSerializationProvider;
import org.restexpress.serialization.SerializationProcessor;
public class SerializationProvider
extends AbstractSerializationProvider {
// SECTION: CONSTANTS
private static final SerializationProcessor JSON_SERIALIZER = new JsonSerializationProcessor();
private static final SerializationProcessor XML_SERIALIZER = new XmlSerializationProcessor();
private static final ResponseWrapper RESPONSE_WRAPPER = new ErrorResponseWrapper();
public SerializationProvider() {
super();
add(JSON_SERIALIZER, RESPONSE_WRAPPER, true);
add(XML_SERIALIZER, RESPONSE_WRAPPER);
}
public static SerializationProcessor json() {
return JSON_SERIALIZER;
}
public static SerializationProcessor xml() {
return XML_SERIALIZER;
}
}

View File

@ -0,0 +1,19 @@
package com.baeldung.restexpress.serialization;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.strategicgains.repoexpress.util.UuidConverter;
import java.io.IOException;
import java.util.UUID;
public class UuidDeserializer
extends JsonDeserializer<UUID> {
@Override
public UUID deserialize(JsonParser json, DeserializationContext context)
throws IOException, JsonProcessingException {
return UuidConverter.parse(json.getText());
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.restexpress.serialization;
import com.strategicgains.hyperexpress.annotation.TokenFormatter;
import com.strategicgains.repoexpress.util.UuidConverter;
import java.util.UUID;
public class UuidFormatter
implements TokenFormatter {
@Override
public String format(Object field) {
return UuidConverter.format((UUID) field);
}
}

View File

@ -0,0 +1,19 @@
package com.baeldung.restexpress.serialization;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.strategicgains.repoexpress.util.UuidConverter;
import java.io.IOException;
import java.util.UUID;
public class UuidSerializer
extends JsonSerializer<UUID> {
@Override
public void serialize(UUID objectId, JsonGenerator json, SerializerProvider provider)
throws IOException, JsonProcessingException {
json.writeString(UuidConverter.format(objectId));
}
}

View File

@ -0,0 +1,18 @@
package com.baeldung.restexpress.serialization;
import com.baeldung.restexpress.uuid.SampleUuidEntity;
import org.restexpress.serialization.xml.XstreamXmlProcessor;
public class XmlSerializationProcessor
extends XstreamXmlProcessor {
public XmlSerializationProcessor() {
super();
alias("sample", SampleUuidEntity.class);
// alias("element_name", Element.class);
// alias("element_name", Element.class);
// alias("element_name", Element.class);
// alias("element_name", Element.class);
registerConverter(new XstreamUuidConverter());
registerConverter(new XstreamOidConverter());
}
}

View File

@ -0,0 +1,28 @@
package com.baeldung.restexpress.serialization;
import com.strategicgains.repoexpress.mongodb.Identifiers;
import com.thoughtworks.xstream.converters.SingleValueConverter;
import org.bson.types.ObjectId;
/**
* @author toddf
* @since Feb 16, 2011
*/
public class XstreamOidConverter
implements SingleValueConverter {
@SuppressWarnings("rawtypes")
@Override
public boolean canConvert(Class aClass) {
return ObjectId.class.isAssignableFrom(aClass);
}
@Override
public Object fromString(String value) {
return (ObjectId) Identifiers.MONGOID.parse(value).primaryKey();
}
@Override
public String toString(Object objectId) {
return ((ObjectId) objectId).toString();
}
}

View File

@ -0,0 +1,29 @@
package com.baeldung.restexpress.serialization;
import com.strategicgains.repoexpress.util.UuidConverter;
import com.thoughtworks.xstream.converters.SingleValueConverter;
import java.util.UUID;
/**
* @author toddf
* @since Feb 16, 2011
*/
public class XstreamUuidConverter
implements SingleValueConverter {
@SuppressWarnings("rawtypes")
@Override
public boolean canConvert(Class aClass) {
return UUID.class.isAssignableFrom(aClass);
}
@Override
public Object fromString(String value) {
return UuidConverter.parse(value);
}
@Override
public String toString(Object objectId) {
return UuidConverter.format((UUID) objectId);
}
}

View File

@ -0,0 +1,23 @@
package com.baeldung.restexpress.uuid;
import com.baeldung.restexpress.Constants;
import com.baeldung.restexpress.serialization.UuidFormatter;
import com.strategicgains.hyperexpress.annotation.BindToken;
import com.strategicgains.hyperexpress.annotation.TokenBindings;
import com.strategicgains.repoexpress.mongodb.AbstractUuidMongodbEntity;
import org.restexpress.plugin.hyperexpress.Linkable;
/**
* This is a sample entity identified by a UUID (instead of a MongoDB ObjectID).
* It also contains createdAt and updatedAt properties that are automatically maintained
* by the persistence layer (SampleUuidEntityRepository).
*/
@TokenBindings({
@BindToken(value = Constants.Url.SAMPLE_ID, field = "id", formatter = UuidFormatter.class)
})
public class SampleUuidEntity
extends AbstractUuidMongodbEntity
implements Linkable {
public SampleUuidEntity() {
}
}

View File

@ -0,0 +1,79 @@
package com.baeldung.restexpress.uuid;
import com.baeldung.restexpress.Constants;
import com.strategicgains.hyperexpress.builder.DefaultTokenResolver;
import com.strategicgains.hyperexpress.builder.DefaultUrlBuilder;
import com.strategicgains.hyperexpress.builder.UrlBuilder;
import com.strategicgains.repoexpress.adapter.Identifiers;
import io.netty.handler.codec.http.HttpMethod;
import org.restexpress.Request;
import org.restexpress.Response;
import org.restexpress.common.query.QueryFilter;
import org.restexpress.common.query.QueryOrder;
import org.restexpress.common.query.QueryRange;
import org.restexpress.query.QueryFilters;
import org.restexpress.query.QueryOrders;
import org.restexpress.query.QueryRanges;
import java.util.List;
/**
* This is the 'controller' layer, where HTTP details are converted to domain concepts and passed to the service layer.
* Then service layer response information is enhanced with HTTP details, if applicable, for the response.
* <p/>
* This controller demonstrates how to process a MongoDB entity that is identified by a UUID.
*/
public class SampleUuidEntityController {
private static final UrlBuilder LOCATION_BUILDER = new DefaultUrlBuilder();
private SampleUuidEntityService service;
public SampleUuidEntityController(SampleUuidEntityService sampleService) {
super();
this.service = sampleService;
}
public SampleUuidEntity create(Request request, Response response) {
SampleUuidEntity entity = request.getBodyAs(SampleUuidEntity.class, "Resource details not provided");
SampleUuidEntity saved = service.create(entity);
// Construct the response for create...
response.setResponseCreated();
// Include the Location header...
String locationPattern = request.getNamedUrl(HttpMethod.GET, Constants.Routes.SINGLE_UUID_SAMPLE);
response.addLocationHeader(LOCATION_BUILDER.build(locationPattern, new DefaultTokenResolver()));
// Return the newly-created resource...
return saved;
}
public SampleUuidEntity read(Request request, Response response) {
String id = request.getHeader(Constants.Url.SAMPLE_ID, "No resource ID supplied");
SampleUuidEntity entity = service.read(Identifiers.UUID.parse(id));
return entity;
}
public List<SampleUuidEntity> readAll(Request request, Response response) {
QueryFilter filter = QueryFilters.parseFrom(request);
QueryOrder order = QueryOrders.parseFrom(request);
QueryRange range = QueryRanges.parseFrom(request, 20);
List<SampleUuidEntity> entities = service.readAll(filter, range, order);
long count = service.count(filter);
response.setCollectionResponse(range, entities.size(), count);
return entities;
}
public void update(Request request, Response response) {
String id = request.getHeader(Constants.Url.SAMPLE_ID, "No resource ID supplied");
SampleUuidEntity entity = request.getBodyAs(SampleUuidEntity.class, "Resource details not provided");
entity.setId(Identifiers.UUID.parse(id));
service.update(entity);
response.setResponseNoContent();
}
public void delete(Request request, Response response) {
String id = request.getHeader(Constants.Url.SAMPLE_ID, "No resource ID supplied");
service.delete(Identifiers.UUID.parse(id));
response.setResponseNoContent();
}
}

View File

@ -0,0 +1,12 @@
package com.baeldung.restexpress.uuid;
import com.mongodb.MongoClient;
import com.strategicgains.repoexpress.mongodb.MongodbUuidEntityRepository;
public class SampleUuidEntityRepository
extends MongodbUuidEntityRepository<SampleUuidEntity> {
@SuppressWarnings("unchecked")
public SampleUuidEntityRepository(MongoClient mongo, String dbName) {
super(mongo, dbName, SampleUuidEntity.class);
}
}

View File

@ -0,0 +1,48 @@
package com.baeldung.restexpress.uuid;
import com.strategicgains.repoexpress.domain.Identifier;
import com.strategicgains.syntaxe.ValidationEngine;
import org.restexpress.common.query.QueryFilter;
import org.restexpress.common.query.QueryOrder;
import org.restexpress.common.query.QueryRange;
import java.util.List;
/**
* This is the 'service' or 'business logic' layer, where business logic, syntactic and semantic
* domain validation occurs, along with calls to the persistence layer.
*/
public class SampleUuidEntityService {
private SampleUuidEntityRepository samples;
public SampleUuidEntityService(SampleUuidEntityRepository samplesRepository) {
super();
this.samples = samplesRepository;
}
public SampleUuidEntity create(SampleUuidEntity entity) {
ValidationEngine.validateAndThrow(entity);
return samples.create(entity);
}
public SampleUuidEntity read(Identifier id) {
return samples.read(id);
}
public void update(SampleUuidEntity entity) {
ValidationEngine.validateAndThrow(entity);
samples.update(entity);
}
public void delete(Identifier id) {
samples.delete(id);
}
public List<SampleUuidEntity> readAll(QueryFilter filter, QueryRange range, QueryOrder order) {
return samples.readAll(filter, range, order);
}
public long count(QueryFilter filter) {
return samples.count(filter);
}
}

View File

@ -0,0 +1,22 @@
# Default is 8081
port = 8081
# The size of the executor thread pool (that can handle blocking back-end processing).
executor.threadPool.size = 20
# A MongoDB URI/Connection string
# see: http://docs.mongodb.org/manual/reference/connection-string/
mongodb.uri = mongodb://localhost:27017/scaffolding_mongodb
# The base URL, used as a prefix for links returned in data
# default is http://localhost:<port>
#base.url = http://localhost:8081
#Configuration for the MetricsPlugin/Graphite
metrics.isEnabled = true
#metrics.machineName =
metrics.prefix = web1.example.com
metrics.graphite.isEnabled = false
metrics.graphite.host = graphite.example.com
metrics.graphite.port = 2003
metrics.graphite.publishSeconds = 60