diff --git a/order-service/api/pom.xml b/order-service/api/pom.xml
new file mode 100644
index 0000000..48a7c0f
--- /dev/null
+++ b/order-service/api/pom.xml
@@ -0,0 +1,39 @@
+
+
+ 4.0.0
+
+ com.devonfw.app.java
+ order-service
+ 0.0.1-SNAPSHOT
+
+ order-service-api
+ jar
+ ${project.artifactId}
+ API of the server for the order-service application (containing datatypes, transfer-objects, and service interfaces).
+
+
+
+ org.springframework.data
+ spring-data-commons
+
+
+ com.devonfw.java.modules
+ devon4j-rest
+
+
+ com.devonfw.java.modules
+ devon4j-logging
+
+
+ com.devonfw.java.modules
+ devon4j-security
+
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java
new file mode 100644
index 0000000..05fe350
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java
@@ -0,0 +1,11 @@
+package com.devonfw.app.java.order.general.common.api;
+
+import com.devonfw.module.basic.common.api.entity.GenericEntity;
+
+/**
+ * This is the abstract interface for a {@link GenericEntity} of this application. We are using {@link Long} for
+ * all {@link #getId() primary keys}.
+ */
+public abstract interface ApplicationEntity extends GenericEntity {
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/BinaryObject.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/BinaryObject.java
new file mode 100644
index 0000000..3380f53
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/BinaryObject.java
@@ -0,0 +1,32 @@
+package com.devonfw.app.java.order.general.common.api;
+
+/**
+ * This is the interface for a {@link BinaryObject} of the order-service.
+ */
+public interface BinaryObject extends ApplicationEntity {
+
+ /**
+ * @param mimeType is the MIME-Type of this {@link BinaryObject}
+ */
+ void setMimeType(String mimeType);
+
+ /**
+ * Returns MIME-Type of thie {@link BinaryObject}
+ *
+ * @return the MIME-Type, e.g image/jpeg
+ */
+ String getMimeType();
+
+ /**
+ * @return Returns the size in bytes
+ */
+ long getSize();
+
+ /**
+ * Sets the size of bytes
+ *
+ * @param size the size in bytes
+ */
+ void setSize(long size);
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/NlsBundleApplicationRoot.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/NlsBundleApplicationRoot.java
new file mode 100644
index 0000000..55e8fae
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/NlsBundleApplicationRoot.java
@@ -0,0 +1,20 @@
+package com.devonfw.app.java.order.general.common.api;
+
+import net.sf.mmm.util.nls.api.NlsBundle;
+import net.sf.mmm.util.nls.api.NlsBundleMessage;
+import net.sf.mmm.util.nls.api.NlsMessage;
+
+/**
+ * This is the {@link NlsBundle} for this application.
+ */
+public interface NlsBundleApplicationRoot extends NlsBundle {
+
+ /**
+ * @see com.devonfw.app.java.order.general.common.api.exception.NoActiveUserException
+ *
+ * @return the {@link NlsMessage}.
+ */
+ @NlsBundleMessage("There is currently no user logged in")
+ NlsMessage errorNoActiveUser();
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/UserProfile.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/UserProfile.java
new file mode 100644
index 0000000..55a20c1
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/UserProfile.java
@@ -0,0 +1,13 @@
+package com.devonfw.app.java.order.general.common.api;
+
+/**
+ * This is the interface for the profile of a user interacting with this application.
+ */
+public interface UserProfile {
+
+ /**
+ * @return the login of the user.
+ */
+ String getLogin();
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationBusinessException.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationBusinessException.java
new file mode 100644
index 0000000..86d4f3f
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationBusinessException.java
@@ -0,0 +1,35 @@
+package com.devonfw.app.java.order.general.common.api.exception;
+
+import net.sf.mmm.util.nls.api.NlsMessage;
+
+/**
+ * Abstract base class for business exceptions of this application.
+ */
+public abstract class ApplicationBusinessException extends ApplicationException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @param message the error {@link #getNlsMessage() message}.
+ */
+ public ApplicationBusinessException(NlsMessage message) {
+
+ super(message);
+ }
+
+ /**
+ * @param cause the error {@link #getCause() cause}.
+ * @param message the error {@link #getNlsMessage() message}.
+ */
+ public ApplicationBusinessException(Throwable cause, NlsMessage message) {
+
+ super(cause, message);
+ }
+
+ @Override
+ public boolean isTechnical() {
+
+ return false;
+ }
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationException.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationException.java
new file mode 100644
index 0000000..32a0bbb
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationException.java
@@ -0,0 +1,30 @@
+package com.devonfw.app.java.order.general.common.api.exception;
+
+import net.sf.mmm.util.exception.api.NlsRuntimeException;
+import net.sf.mmm.util.nls.api.NlsMessage;
+
+/**
+ * Abstract base class for custom exceptions of this application.
+ */
+public abstract class ApplicationException extends NlsRuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @param message the error {@link #getNlsMessage() message}.
+ */
+ public ApplicationException(NlsMessage message) {
+
+ super(message);
+ }
+
+ /**
+ * @param cause the error {@link #getCause() cause}.
+ * @param message the error {@link #getNlsMessage() message}.
+ */
+ public ApplicationException(Throwable cause, NlsMessage message) {
+
+ super(cause, message);
+ }
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationTechnicalException.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationTechnicalException.java
new file mode 100644
index 0000000..b6e965c
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationTechnicalException.java
@@ -0,0 +1,29 @@
+package com.devonfw.app.java.order.general.common.api.exception;
+
+import net.sf.mmm.util.nls.api.NlsMessage;
+
+/**
+ * Abstract base class for technical custom exceptions of this application.
+ */
+public abstract class ApplicationTechnicalException extends ApplicationException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @param message the error {@link #getNlsMessage() message}.
+ */
+ public ApplicationTechnicalException(NlsMessage message) {
+
+ super(message);
+ }
+
+ /**
+ * @param cause the error {@link #getCause() cause}.
+ * @param message the error {@link #getNlsMessage() message}.
+ */
+ public ApplicationTechnicalException(Throwable cause, NlsMessage message) {
+
+ super(cause, message);
+ }
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/NoActiveUserException.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/NoActiveUserException.java
new file mode 100644
index 0000000..d029b60
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/NoActiveUserException.java
@@ -0,0 +1,31 @@
+package com.devonfw.app.java.order.general.common.api.exception;
+
+import com.devonfw.app.java.order.general.common.api.NlsBundleApplicationRoot;
+
+/**
+ * Thrown when an operation is requested that requires a user to be logged in, but no such user exists.
+ */
+public class NoActiveUserException extends ApplicationBusinessException {
+
+ /** UID for serialization. */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The constructor.
+ */
+ public NoActiveUserException() {
+
+ this(null);
+ }
+
+ /**
+ * The constructor.
+ *
+ * @param cause The root cause of this exception.
+ */
+ public NoActiveUserException(Throwable cause) {
+
+ super(cause, createBundle(NlsBundleApplicationRoot.class).errorNoActiveUser());
+ }
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/to/UserProfileTo.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/to/UserProfileTo.java
new file mode 100644
index 0000000..7da8d78
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/to/UserProfileTo.java
@@ -0,0 +1,37 @@
+package com.devonfw.app.java.order.general.common.api.to;
+
+import com.devonfw.app.java.order.general.common.api.UserProfile;
+import com.devonfw.module.basic.common.api.to.AbstractTo;
+
+/**
+ * Implementation of {@link UserProfile} as {AbstractTo TO}.
+ */
+public class UserProfileTo extends AbstractTo implements UserProfile {
+
+ private static final long serialVersionUID = 1L;
+
+ private String login;
+
+ /**
+ * The constructor.
+ */
+ public UserProfileTo() {
+
+ super();
+ }
+
+ @Override
+ public String getLogin() {
+
+ return this.login;
+ }
+
+ /**
+ * @param login the new {@link #getLogin() login}.
+ */
+ public void setLogin(String login) {
+
+ this.login = login;
+ }
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/logic/api/to/BinaryObjectEto.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/logic/api/to/BinaryObjectEto.java
new file mode 100644
index 0000000..c4696eb
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/logic/api/to/BinaryObjectEto.java
@@ -0,0 +1,50 @@
+package com.devonfw.app.java.order.general.logic.api.to;
+
+import com.devonfw.app.java.order.general.common.api.BinaryObject;
+import com.devonfw.module.basic.common.api.to.AbstractEto;
+
+/**
+ * The {@link com.devonfw.module.basic.common.api.to.AbstractEto ETO} for a {@link BinaryObject}.
+ */
+public class BinaryObjectEto extends AbstractEto implements BinaryObject {
+
+ private static final long serialVersionUID = 1L;
+
+ private String mimeType;
+
+ private long size;
+
+ /**
+ * Constructor.
+ */
+ public BinaryObjectEto() {
+
+ super();
+ }
+
+ @Override
+ public void setMimeType(String mimeType) {
+
+ this.mimeType = mimeType;
+
+ }
+
+ @Override
+ public String getMimeType() {
+
+ return this.mimeType;
+ }
+
+ @Override
+ public long getSize() {
+
+ return this.size;
+ }
+
+ @Override
+ public void setSize(long size) {
+
+ this.size = size;
+ }
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/service/api/rest/SecurityRestService.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/service/api/rest/SecurityRestService.java
new file mode 100644
index 0000000..d3724c1
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/service/api/rest/SecurityRestService.java
@@ -0,0 +1,38 @@
+package com.devonfw.app.java.order.general.service.api.rest;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+
+import org.springframework.security.web.csrf.CsrfToken;
+
+import com.devonfw.module.rest.common.api.RestService;
+
+import com.devonfw.app.java.order.general.common.api.to.UserProfileTo;
+
+/**
+ * The security REST service provides access to the csrf token, the authenticated user's meta-data. Furthermore, it
+ * provides functionality to check permissions and roles of the authenticated user.
+ */
+@Path("/security/v1")
+public interface SecurityRestService extends RestService {
+
+ /**
+ * @param request {@link HttpServletRequest} to retrieve the current session from.
+ * @param response {@link HttpServletResponse} to send additional information.
+ * @return the Spring Security {@link CsrfToken} from the server session.
+ */
+ @GET
+ @Path("/csrftoken/")
+ CsrfToken getCsrfToken(@Context HttpServletRequest request, @Context HttpServletResponse response);
+
+ /**
+ * @return the {@link UserProfileTo} of the currently logged-in user.
+ */
+ @GET
+ @Path("/currentuser/")
+ UserProfileTo getCurrentUser();
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Customer.java b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Customer.java
new file mode 100644
index 0000000..fcc5abf
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Customer.java
@@ -0,0 +1,31 @@
+package com.devonfw.app.java.order.orderservice.common.api;
+
+import com.devonfw.app.java.order.general.common.api.ApplicationEntity;
+
+public interface Customer extends ApplicationEntity {
+
+ /**
+ * @return firstnameId
+ */
+
+ public String getFirstname();
+
+ /**
+ * @param firstname setter for firstname attribute
+ */
+
+ public void setFirstname(String firstname);
+
+ /**
+ * @return lastnameId
+ */
+
+ public String getLastname();
+
+ /**
+ * @param lastname setter for lastname attribute
+ */
+
+ public void setLastname(String lastname);
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Item.java b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Item.java
new file mode 100644
index 0000000..c299cfe
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Item.java
@@ -0,0 +1,37 @@
+package com.devonfw.app.java.order.orderservice.common.api;
+
+import com.devonfw.app.java.order.general.common.api.ApplicationEntity;
+
+public interface Item extends ApplicationEntity {
+
+ /**
+ * @return nameId
+ */
+ public String getName();
+
+ /**
+ * @param name setter for name attribute
+ */
+ public void setName(String name);
+
+ /**
+ * @return descriptionId
+ */
+ public String getDescription();
+
+ /**
+ * @param description setter for description attribute
+ */
+ public void setDescription(String description);
+
+ /**
+ * @return priceId
+ */
+ public Double getPrice();
+
+ /**
+ * @param price setter for price attribute
+ */
+ public void setPrice(Double price);
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Order.java b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Order.java
new file mode 100644
index 0000000..b02da0d
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Order.java
@@ -0,0 +1,59 @@
+package com.devonfw.app.java.order.orderservice.common.api;
+
+import java.time.LocalDate;
+
+import com.devonfw.app.java.order.general.common.api.ApplicationEntity;
+
+public interface Order extends ApplicationEntity {
+
+ /**
+ * @return creationDateId
+ */
+
+ public LocalDate getCreationDate();
+
+ /**
+ * @param creationDate setter for creationDate attribute
+ */
+
+ public void setCreationDate(LocalDate creationDate);
+
+ /**
+ * getter for ownerId attribute
+ *
+ * @return ownerId
+ */
+
+ public Long getOwnerId();
+
+ /**
+ * @param owner setter for owner attribute
+ */
+
+ public void setOwnerId(Long ownerId);
+
+ /**
+ * @return priceId
+ */
+
+ public Double getPrice();
+
+ /**
+ * @param price setter for price attribute
+ */
+
+ public void setPrice(Double price);
+
+ /**
+ * @return statusId
+ */
+
+ public OrderStatus getStatus();
+
+ /**
+ * @param status setter for status attribute
+ */
+
+ public void setStatus(OrderStatus status);
+
+}
diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/OrderStatus.java b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/OrderStatus.java
new file mode 100644
index 0000000..f328396
--- /dev/null
+++ b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/OrderStatus.java
@@ -0,0 +1,9 @@
+package com.devonfw.app.java.order.orderservice.common.api;
+
+/**
+ * @author ARMIKA
+ *
+ */
+public enum OrderStatus {
+ NEW, PREPARING, PREPARED, SERVED, PAID, CANCELLED
+}
diff --git a/order-service/core/pom.xml b/order-service/core/pom.xml
new file mode 100644
index 0000000..162d269
--- /dev/null
+++ b/order-service/core/pom.xml
@@ -0,0 +1,242 @@
+
+
+ 4.0.0
+
+ com.devonfw.app.java
+ order-service
+ 0.0.1-SNAPSHOT
+
+ order-service-core
+ jar
+ ${project.artifactId}
+ Core of the server for the order-service application - a simple example using the Open Application Standard Platform for Java (devon4j).
+
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+
+ ${project.groupId}
+ order-service-api
+ ${project.version}
+
+
+
+
+ com.devonfw.java.modules
+ devon4j-beanmapping
+
+
+
+
+ com.devonfw.java.modules
+ devon4j-security
+
+
+
+ com.devonfw.java.modules
+ devon4j-web
+
+
+
+
+ com.devonfw.java.starters
+ devon4j-starter-cxf-client-rest
+
+
+
+ com.devonfw.java.starters
+ devon4j-starter-cxf-client-ws
+
+
+
+
+ com.devonfw.java.starters
+ devon4j-starter-cxf-server-rest
+
+
+
+ com.devonfw.java.starters
+ devon4j-starter-cxf-server-ws
+
+
+
+
+ com.devonfw.java.starters
+ devon4j-starter-spring-data-jpa
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+
+
+ org.hibernate.javax.persistence
+ hibernate-jpa-2.1-api
+
+
+
+
+ org.hibernate
+ hibernate-entitymanager
+
+
+
+
+ com.querydsl
+ querydsl-jpa
+
+
+ com.querydsl
+ querydsl-apt
+ provided
+
+
+
+
+ org.hibernate.validator
+ hibernate-validator
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+
+
+ javax.el
+ javax.el-api
+
+
+
+
+ org.springframework
+ spring-webmvc
+
+
+
+
+ com.h2database
+ h2
+
+
+
+
+ org.flywaydb
+ flyway-core
+
+
+
+
+ org.apache.cxf
+ cxf-rt-rs-service-description
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+ org.springframework
+ spring-aop
+
+
+
+
+ cglib
+ cglib
+
+
+
+
+ net.logstash.logback
+ logstash-logback-encoder
+
+
+
+
+ com.devonfw.java.modules
+ devon4j-test
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+
+
+ org.skyscreamer
+ jsonassert
+ test
+
+
+
+
+
+ embedded
+
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+
+
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ config/application.properties
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/SpringBootApp.java b/order-service/core/src/main/java/com/devonfw/app/java/order/SpringBootApp.java
new file mode 100644
index 0000000..fd1fb27
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/SpringBootApp.java
@@ -0,0 +1,30 @@
+package com.devonfw.app.java.order;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+
+import com.devonfw.module.jpa.dataaccess.api.AdvancedRevisionEntity;
+import com.devonfw.module.jpa.dataaccess.impl.data.GenericRepositoryFactoryBean;
+
+/**
+ * Main entry point of this {@link SpringBootApplication}. Simply run this class to start this app.
+ */
+@SpringBootApplication
+@EntityScan(basePackages = { "com.devonfw.app.java.order" }, basePackageClasses = { AdvancedRevisionEntity.class })
+@EnableJpaRepositories(repositoryFactoryBeanClass = GenericRepositoryFactoryBean.class)
+@EnableGlobalMethodSecurity(jsr250Enabled = true)
+public class SpringBootApp {
+
+ /**
+ * Entry point for spring-boot based app
+ *
+ * @param args - arguments
+ */
+ public static void main(String[] args) {
+
+ SpringApplication.run(SpringBootApp.class, args);
+ }
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java
new file mode 100644
index 0000000..df20134
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java
@@ -0,0 +1,11 @@
+package com.devonfw.app.java.order.general.common.api;
+
+import com.devonfw.module.basic.common.api.entity.GenericEntity;
+
+/**
+ * This is the abstract interface for a {@link GenericEntity}. We are using {@link Long} for all {@link #getId() primary
+ * keys}.
+ */
+public abstract interface ApplicationEntity extends GenericEntity {
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/security/ApplicationAccessControlConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/security/ApplicationAccessControlConfig.java
new file mode 100644
index 0000000..01cf1ff
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/security/ApplicationAccessControlConfig.java
@@ -0,0 +1,38 @@
+package com.devonfw.app.java.order.general.common.api.security;
+
+import javax.inject.Named;
+
+import com.devonfw.module.security.common.api.accesscontrol.AccessControlGroup;
+import com.devonfw.module.security.common.base.accesscontrol.AccessControlConfig;
+
+/**
+ * Example of {@link AccessControlConfig} that used for testing.
+ */
+@Named
+public class ApplicationAccessControlConfig extends AccessControlConfig {
+
+ public static final String APP_ID = "order-service";
+
+ private static final String PREFIX = APP_ID + ".";
+
+ public static final String PERMISSION_FIND_BINARY_OBJECT = PREFIX + "FindBinaryObject";
+
+ public static final String PERMISSION_SAVE_BINARY_OBJECT = PREFIX + "SaveBinaryObject";
+
+ public static final String PERMISSION_DELETE_BINARY_OBJECT = PREFIX + "DeleteBinaryObject";
+
+ public static final String GROUP_READ_MASTER_DATA = PREFIX + "ReadMasterData";
+
+ public static final String GROUP_ADMIN = PREFIX + "Admin";
+
+ /**
+ * The constructor.
+ */
+ public ApplicationAccessControlConfig() {
+
+ super();
+ AccessControlGroup readMasterData = group(GROUP_READ_MASTER_DATA, PERMISSION_FIND_BINARY_OBJECT);
+ group(GROUP_ADMIN, readMasterData, PERMISSION_SAVE_BINARY_OBJECT, PERMISSION_DELETE_BINARY_OBJECT);
+ }
+
+}
\ No newline at end of file
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/base/AbstractBeanMapperSupport.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/base/AbstractBeanMapperSupport.java
new file mode 100644
index 0000000..db56f8e
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/base/AbstractBeanMapperSupport.java
@@ -0,0 +1,31 @@
+package com.devonfw.app.java.order.general.common.base;
+
+import com.devonfw.module.beanmapping.common.api.BeanMapper;
+
+import javax.inject.Inject;
+
+/**
+ * This abstract class provides {@link #getBeanMapper() access} to the {@link BeanMapper}.
+ */
+public abstract class AbstractBeanMapperSupport {
+
+ private BeanMapper beanMapper;
+
+ /**
+ * @param beanMapper is the {@link BeanMapper} to {@link Inject}
+ */
+ @Inject
+ public void setBeanMapper(BeanMapper beanMapper) {
+
+ this.beanMapper = beanMapper;
+ }
+
+ /**
+ * @return the {@link BeanMapper} instance.
+ */
+ protected BeanMapper getBeanMapper() {
+
+ return this.beanMapper;
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/ApplicationObjectMapperFactory.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/ApplicationObjectMapperFactory.java
new file mode 100644
index 0000000..044053f
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/ApplicationObjectMapperFactory.java
@@ -0,0 +1,33 @@
+package com.devonfw.app.java.order.general.common.impl.config;
+
+import javax.inject.Named;
+
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.web.csrf.CsrfToken;
+
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+import com.devonfw.module.json.common.base.ObjectMapperFactory;
+import com.devonfw.module.json.common.base.type.PageableJsonSerializer;
+import com.devonfw.module.json.common.base.type.PageableJsonDeserializer;
+
+/**
+ * The MappingFactory class to resolve polymorphic conflicts within the order-service application.
+ */
+@Named("ApplicationObjectMapperFactory")
+public class ApplicationObjectMapperFactory extends ObjectMapperFactory {
+
+ /**
+ * The constructor.
+ */
+ public ApplicationObjectMapperFactory() {
+
+ super();
+ // see https://github.com/devonfw-wiki/devon4j/wiki/guide-json#json-and-inheritance
+ SimpleModule module = getExtensionModule();
+ module.addAbstractTypeMapping(CsrfToken.class, CsrfTokenImpl.class);
+ // register spring-data Pageable
+ module.addSerializer(Pageable.class, new PageableJsonSerializer());
+ module.addDeserializer(Pageable.class, new PageableJsonDeserializer());
+ }
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/BeansDozerConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/BeansDozerConfig.java
new file mode 100644
index 0000000..87eda20
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/BeansDozerConfig.java
@@ -0,0 +1,32 @@
+package com.devonfw.app.java.order.general.common.impl.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.dozer.DozerBeanMapper;
+import org.dozer.Mapper;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Java bean configuration for Dozer
+ */
+@Configuration
+@ComponentScan(basePackages = { "com.devonfw.module.beanmapping" })
+public class BeansDozerConfig {
+
+ private static final String DOZER_MAPPING_XML = "config/app/common/dozer-mapping.xml";
+
+ /**
+ * @return the {@link DozerBeanMapper}.
+ */
+ @Bean
+ public Mapper getDozer() {
+
+ List beanMappings = new ArrayList<>();
+ beanMappings.add(DOZER_MAPPING_XML);
+ return new DozerBeanMapper(beanMappings);
+
+ }
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/CsrfTokenImpl.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/CsrfTokenImpl.java
new file mode 100644
index 0000000..057c378
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/CsrfTokenImpl.java
@@ -0,0 +1,60 @@
+package com.devonfw.app.java.order.general.common.impl.config;
+
+import org.springframework.security.web.csrf.CsrfToken;
+
+/**
+ * Implementation of {@link CsrfToken} as Java bean for JSON deserialization.
+ */
+public class CsrfTokenImpl implements CsrfToken {
+
+ private static final long serialVersionUID = 1L;
+
+ private String headerName;
+
+ private String parameterName;
+
+ private String token;
+
+ @Override
+ public String getHeaderName() {
+
+ return this.headerName;
+ }
+
+ @Override
+ public String getParameterName() {
+
+ return this.parameterName;
+ }
+
+ @Override
+ public String getToken() {
+
+ return this.token;
+ }
+
+ /**
+ * @param headerName new value of {@link #getHeaderName()}.
+ */
+ public void setHeaderName(String headerName) {
+
+ this.headerName = headerName;
+ }
+
+ /**
+ * @param parameterName new value of {@link #getParameterName()}.
+ */
+ public void setParameterName(String parameterName) {
+
+ this.parameterName = parameterName;
+ }
+
+ /**
+ * @param token new value of {@link #getToken()}.
+ */
+ public void setToken(String token) {
+
+ this.token = token;
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/BaseUserDetailsService.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/BaseUserDetailsService.java
new file mode 100644
index 0000000..96d80f0
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/BaseUserDetailsService.java
@@ -0,0 +1,123 @@
+package com.devonfw.app.java.order.general.common.impl.security;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+import com.devonfw.module.security.common.api.accesscontrol.AccessControl;
+import com.devonfw.module.security.common.api.accesscontrol.AccessControlProvider;
+import com.devonfw.module.security.common.base.accesscontrol.AccessControlGrantedAuthority;
+
+/**
+ * Custom implementation of {@link UserDetailsService}.
+ *
+ * @see com.devonfw.app.java.order.general.service.impl.config.BaseWebSecurityConfig
+ */
+@Named
+public class BaseUserDetailsService implements UserDetailsService {
+
+ /** Logger instance. */
+ private static final Logger LOG = LoggerFactory.getLogger(BaseUserDetailsService.class);
+
+ private AuthenticationManagerBuilder amBuilder;
+
+ private AccessControlProvider accessControlProvider;
+
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+
+ Set authorities = getAuthorities(username);
+ UserDetails user;
+ try {
+ user = getAmBuilder().getDefaultUserDetailsService().loadUserByUsername(username);
+ User userData = new User(user.getUsername(), user.getPassword(), authorities);
+ return userData;
+ } catch (Exception e) {
+ e.printStackTrace();
+ UsernameNotFoundException exception = new UsernameNotFoundException("Authentication failed.", e);
+ LOG.warn("Failed to get user {}.", username, exception);
+ throw exception;
+ }
+ }
+
+ /**
+ * @param username the login of the user
+ * @return the associated {@link GrantedAuthority}s
+ * @throws AuthenticationException if no principal is retrievable for the given {@code username}
+ */
+ protected Set getAuthorities(String username) throws AuthenticationException {
+
+ Objects.requireNonNull(username, "username");
+ // determine granted authorities for spring-security...
+ Set authorities = new HashSet<>();
+ Collection accessControlIds = getRoles(username);
+ Set accessControlSet = new HashSet<>();
+ for (String id : accessControlIds) {
+ boolean success = this.accessControlProvider.collectAccessControls(id, accessControlSet);
+ if (!success) {
+ LOG.warn("Undefined access control {}.", id);
+ }
+ }
+ for (AccessControl accessControl : accessControlSet) {
+ authorities.add(new AccessControlGrantedAuthority(accessControl));
+ }
+ return authorities;
+ }
+
+ private Collection getRoles(String username) {
+
+ Collection roles = new ArrayList<>();
+ // TODO for a reasonable application you need to retrieve the roles of the user from a central IAM system
+ roles.add(username);
+ return roles;
+ }
+
+ /**
+ * @return amBuilder
+ */
+ public AuthenticationManagerBuilder getAmBuilder() {
+
+ return this.amBuilder;
+ }
+
+ /**
+ * @param amBuilder new value of {@link #getAmBuilder}.
+ */
+ @Inject
+ public void setAmBuilder(AuthenticationManagerBuilder amBuilder) {
+
+ this.amBuilder = amBuilder;
+ }
+
+ /**
+ * @return accessControlProvider
+ */
+ public AccessControlProvider getAccessControlProvider() {
+
+ return this.accessControlProvider;
+ }
+
+ /**
+ * @param accessControlProvider new value of {@link #getAccessControlProvider}.
+ */
+ @Inject
+ public void setAccessControlProvider(AccessControlProvider accessControlProvider) {
+
+ this.accessControlProvider = accessControlProvider;
+ }
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/CsrfRequestMatcher.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/CsrfRequestMatcher.java
new file mode 100644
index 0000000..38eb25f
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/CsrfRequestMatcher.java
@@ -0,0 +1,54 @@
+package com.devonfw.app.java.order.general.common.impl.security;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.security.web.util.matcher.RequestMatcher;
+
+/**
+ * This is the implementation of {@link RequestMatcher}, which decides which {@link HttpServletRequest Requests} require
+ * a correct CSRF token.
+ *
+ * @see Cross-site request forgery
+ */
+public class CsrfRequestMatcher implements RequestMatcher {
+
+ private static final Pattern HTTP_METHOD_PATTERN = Pattern.compile("^GET$");
+
+ private static final String[] PATH_PREFIXES_WITHOUT_CSRF_PROTECTION =
+ { "/login", "/logout", "/services/rest/login", "/websocket" };
+
+ @Override
+ public boolean matches(HttpServletRequest request) {
+
+ // GET requests are read-only and therefore do not need CSRF protection
+ if (HTTP_METHOD_PATTERN.matcher(request.getMethod()).matches()) {
+
+ return false;
+ }
+
+ // There are specific URLs which can not be protected from CSRF. For example, in case of the the login page,
+ // the CSRF token can only be accessed after a successful authentication ("login").
+ String requestPath = getRequestPath(request);
+ for (String path : PATH_PREFIXES_WITHOUT_CSRF_PROTECTION) {
+ if (requestPath.startsWith(path)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private String getRequestPath(HttpServletRequest request) {
+
+ String url = request.getServletPath();
+ String pathInfo = request.getPathInfo();
+
+ if (pathInfo != null) {
+ url += pathInfo;
+ }
+
+ return url;
+ }
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/ApplicationPersistenceEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/ApplicationPersistenceEntity.java
new file mode 100644
index 0000000..4596053
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/ApplicationPersistenceEntity.java
@@ -0,0 +1,86 @@
+package com.devonfw.app.java.order.general.dataaccess.api;
+
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Transient;
+import javax.persistence.Version;
+
+import com.devonfw.app.java.order.general.common.api.ApplicationEntity;
+import com.devonfw.module.basic.common.api.entity.PersistenceEntity;
+
+/**
+ * Abstract base class for all {@link PersistenceEntity persistence entities} with an {@link #getId() id} and a
+ * {@link #getModificationCounter() modificationCounter} (version) field. All persistence entities of this application
+ * should inherit from this class. It is using JPA annotations at the getters what has several advantages but also
+ * implies that you have to annotate transient getter methods with the {@link Transient} annotation.
+ */
+@MappedSuperclass
+public abstract class ApplicationPersistenceEntity implements ApplicationEntity, PersistenceEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ private Long id;
+
+ private int modificationCounter;
+
+ /**
+ * The constructor.
+ */
+ public ApplicationPersistenceEntity() {
+
+ super();
+ }
+
+ @Override
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ public Long getId() {
+
+ return this.id;
+ }
+
+ @Override
+ public void setId(Long id) {
+
+ this.id = id;
+ }
+
+ @Override
+ @Version
+ public int getModificationCounter() {
+
+ return this.modificationCounter;
+ }
+
+ @Override
+ public void setModificationCounter(int version) {
+
+ this.modificationCounter = version;
+ }
+
+ @Override
+ public String toString() {
+
+ StringBuilder buffer = new StringBuilder();
+ toString(buffer);
+ return buffer.toString();
+ }
+
+ /**
+ * Method to extend {@link #toString()} logic.
+ *
+ * @param buffer is the {@link StringBuilder} where to {@link StringBuilder#append(Object) append} the string
+ * representation.
+ */
+ protected void toString(StringBuilder buffer) {
+
+ buffer.append(getClass().getSimpleName());
+ if (this.id != null) {
+ buffer.append("[id=");
+ buffer.append(this.id);
+ buffer.append("]");
+ }
+ }
+}
\ No newline at end of file
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/BinaryObjectEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/BinaryObjectEntity.java
new file mode 100644
index 0000000..7b9f9fb
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/BinaryObjectEntity.java
@@ -0,0 +1,78 @@
+package com.devonfw.app.java.order.general.dataaccess.api;
+
+import java.sql.Blob;
+
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.Table;
+import javax.persistence.Column;
+
+import com.devonfw.app.java.order.general.common.api.BinaryObject;
+
+/**
+ * {@link ApplicationPersistenceEntity Entity} for {@link BinaryObject}. Contains the actual {@link Blob}.
+ */
+@Entity
+@Table(name = "BinaryObject")
+public class BinaryObjectEntity extends ApplicationPersistenceEntity implements BinaryObject {
+
+ private static final long serialVersionUID = 1L;
+
+ private Blob data;
+
+ private String mimeType;
+
+ private long size;
+
+ /**
+ * The constructor.
+ */
+ public BinaryObjectEntity() {
+
+ super();
+ }
+
+ @Override
+ public void setMimeType(String mimeType) {
+
+ this.mimeType = mimeType;
+
+ }
+
+ @Override
+ public String getMimeType() {
+
+ return this.mimeType;
+ }
+
+ /**
+ * @return the {@link Blob} data.
+ */
+ @Lob
+ @Column(name = "content")
+ public Blob getData() {
+
+ return this.data;
+ }
+
+ /**
+ * @param data the data to set
+ */
+ public void setData(Blob data) {
+
+ this.data = data;
+ }
+
+ @Column(name = "filesize")
+ @Override
+ public long getSize() {
+
+ return this.size;
+ }
+
+ @Override
+ public void setSize(long size) {
+
+ this.size = size;
+ }
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/dao/BinaryObjectRepository.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/dao/BinaryObjectRepository.java
new file mode 100644
index 0000000..6c543a9
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/dao/BinaryObjectRepository.java
@@ -0,0 +1,11 @@
+package com.devonfw.app.java.order.general.dataaccess.api.dao;
+
+import com.devonfw.module.jpa.dataaccess.api.data.DefaultRepository;
+import com.devonfw.app.java.order.general.dataaccess.api.BinaryObjectEntity;
+
+/**
+ * {@link DefaultRepository} for {@link BinaryObjectEntity}.
+ */
+public interface BinaryObjectRepository extends DefaultRepository {
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/api/UcManageBinaryObject.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/api/UcManageBinaryObject.java
new file mode 100644
index 0000000..7b0af4f
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/api/UcManageBinaryObject.java
@@ -0,0 +1,37 @@
+package com.devonfw.app.java.order.general.logic.api;
+
+import com.devonfw.app.java.order.general.logic.api.to.BinaryObjectEto;
+
+import java.sql.Blob;
+
+/**
+ * Use case for managing BinaryObject.
+ *
+ */
+public interface UcManageBinaryObject {
+
+ /**
+ * @param data the Blob data to save
+ * @param binaryObjectEto the {@link BinaryObjectEto}
+ * @return {@link BinaryObjectEto}
+ */
+ BinaryObjectEto saveBinaryObject(Blob data, BinaryObjectEto binaryObjectEto);
+
+ /**
+ * @param binaryObjectId the ID of the {@link BinaryObjectEto} that should be deleted
+ */
+ void deleteBinaryObject(Long binaryObjectId);
+
+ /**
+ * @param binaryObjectId the ID of the {@link BinaryObjectEto} to find
+ * @return {@link BinaryObjectEto}
+ */
+ BinaryObjectEto findBinaryObject(Long binaryObjectId);
+
+ /**
+ * @param binaryObjectId the ID of the {@link BinaryObjectEto} the blob corresponds to
+ * @return {@link Blob}
+ */
+ Blob getBinaryObjectBlob(Long binaryObjectId);
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractComponentFacade.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractComponentFacade.java
new file mode 100644
index 0000000..cf43023
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractComponentFacade.java
@@ -0,0 +1,16 @@
+package com.devonfw.app.java.order.general.logic.base;
+
+/**
+ * Abstract base class for any component implementation class in this application.
+ */
+public abstract class AbstractComponentFacade extends AbstractLogic {
+
+ /**
+ * The constructor.
+ */
+ public AbstractComponentFacade() {
+
+ super();
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractLogic.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractLogic.java
new file mode 100644
index 0000000..219ddef
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractLogic.java
@@ -0,0 +1,81 @@
+package com.devonfw.app.java.order.general.logic.base;
+
+import com.devonfw.app.java.order.general.common.base.AbstractBeanMapperSupport;
+
+import com.devonfw.module.basic.common.api.entity.GenericEntity;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract base class for implementations of business logic in this application. Actual implementations need
+ * to be annotated with {@link javax.inject.Named}.
+ *
+ * @see AbstractUc
+ * @see AbstractComponentFacade
+ */
+public abstract class AbstractLogic extends AbstractBeanMapperSupport {
+
+ /**
+ * The constructor.
+ */
+ public AbstractLogic() {
+
+ super();
+ }
+
+ /**
+ * Creates a {@link Map} with all {@link GenericEntity entities} from the given {@link Collection} using their
+ * {@link GenericEntity#getId() ID} as key. All {@link GenericEntity entities} without an
+ * {@link GenericEntity#getId() ID} ({@code null}) will be ignored.
+ *
+ * @param is the generic type of the {@link GenericEntity#getId() ID}.
+ * @param is the generic type of the {@link GenericEntity entity}.
+ * @param entities is the {@link Collection} of {@link GenericEntity entities}.
+ * @return a {@link Map} mapping from {@link GenericEntity#getId() ID} to {@link GenericEntity entity}.
+ */
+ protected static > Map getEntityMap(Collection entities) {
+
+ Map id2EntityMap = new HashMap<>();
+ for (E entity : entities) {
+ ID id = entity.getId();
+ if (id != null) {
+ id2EntityMap.put(id, entity);
+ }
+ }
+ return id2EntityMap;
+ }
+
+ /**
+ * Determines the {@link GenericEntity entities} to delete if currentList is the current list from the
+ * persistence and listToSave is the new list that shall be saved. In other words this method selects the
+ * {@link GenericEntity entities} from currentList that are not contained in listToSave.
+ *
+ * @param is the generic type of the {@link GenericEntity#getId() ID}.
+ * @param is the generic type of the {@link GenericEntity entity}.
+ * @param currentEntities is the {@link Collection} of the {@link GenericEntity entities} currently persisted. We
+ * assume that all objects in this list have an {@link GenericEntity#getId() ID} value (that is not
+ * {@code null}).
+ * @param entitiesToSave is the {@link Collection} that contains the {@link GenericEntity entities} that shall be
+ * saved. It may contain {@link GenericEntity entities} that have no {@link GenericEntity#getId() ID} that
+ * shall be newly created.
+ * @return the {@link List} with the {@link GenericEntity entities} to delete.
+ */
+ protected static > List getEntities2Delete(Collection currentEntities,
+ Collection entitiesToSave) {
+
+ List result = new ArrayList<>(currentEntities.size());
+ Map entityMap = getEntityMap(entitiesToSave);
+ for (E entity : currentEntities) {
+ if (!entityMap.containsKey(entity.getId())) {
+ // entity from currentList is not contained in listToSave...
+ result.add(entity);
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractUc.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractUc.java
new file mode 100644
index 0000000..304aeae
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractUc.java
@@ -0,0 +1,18 @@
+package com.devonfw.app.java.order.general.logic.base;
+
+/**
+ * Abstract base class for any use case in this application. Actual implementations need to be annotated with
+ * {@link javax.inject.Named}.
+ *
+ */
+public abstract class AbstractUc extends AbstractLogic {
+
+ /**
+ * The constructor.
+ */
+ public AbstractUc() {
+
+ super();
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/UcManageBinaryObjectImpl.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/UcManageBinaryObjectImpl.java
new file mode 100644
index 0000000..e473ceb
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/UcManageBinaryObjectImpl.java
@@ -0,0 +1,56 @@
+package com.devonfw.app.java.order.general.logic.impl;
+
+import java.sql.Blob;
+
+import javax.annotation.security.RolesAllowed;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import com.devonfw.app.java.order.general.dataaccess.api.BinaryObjectEntity;
+import com.devonfw.app.java.order.general.dataaccess.api.dao.BinaryObjectRepository;
+import com.devonfw.app.java.order.general.common.api.security.ApplicationAccessControlConfig;
+import com.devonfw.app.java.order.general.logic.api.UcManageBinaryObject;
+import com.devonfw.app.java.order.general.logic.api.to.BinaryObjectEto;
+import com.devonfw.app.java.order.general.logic.base.AbstractUc;
+
+/**
+ * Implementation of {@link UcManageBinaryObject}.
+ */
+@Named
+public class UcManageBinaryObjectImpl extends AbstractUc implements UcManageBinaryObject {
+
+ @Inject
+ private BinaryObjectRepository binaryObjectRepository;
+
+ @Override
+ @RolesAllowed(ApplicationAccessControlConfig.PERMISSION_SAVE_BINARY_OBJECT)
+ public BinaryObjectEto saveBinaryObject(Blob data, BinaryObjectEto binaryObjectEto) {
+
+ BinaryObjectEntity binaryObjectEntity = getBeanMapper().map(binaryObjectEto, BinaryObjectEntity.class);
+ binaryObjectEntity.setData(data);
+ this.binaryObjectRepository.save(binaryObjectEntity);
+ return getBeanMapper().map(binaryObjectEntity, BinaryObjectEto.class);
+ }
+
+ @Override
+ @RolesAllowed(ApplicationAccessControlConfig.PERMISSION_DELETE_BINARY_OBJECT)
+ public void deleteBinaryObject(Long binaryObjectId) {
+
+ this.binaryObjectRepository.deleteById(binaryObjectId);
+ }
+
+ @Override
+ @RolesAllowed(ApplicationAccessControlConfig.PERMISSION_FIND_BINARY_OBJECT)
+ public BinaryObjectEto findBinaryObject(Long binaryObjectId) {
+
+ return getBeanMapper().map(this.binaryObjectRepository.find(binaryObjectId), BinaryObjectEto.class);
+ }
+
+ @Override
+ @RolesAllowed(ApplicationAccessControlConfig.PERMISSION_FIND_BINARY_OBJECT)
+ public Blob getBinaryObjectBlob(Long binaryObjectId) {
+
+ return this.binaryObjectRepository.find(binaryObjectId).getData();
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/config/DefaultRolesPrefixPostProcessor.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/config/DefaultRolesPrefixPostProcessor.java
new file mode 100644
index 0000000..86455b0
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/config/DefaultRolesPrefixPostProcessor.java
@@ -0,0 +1,61 @@
+package com.devonfw.app.java.order.general.logic.impl.config;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.core.PriorityOrdered;
+import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
+import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
+import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
+import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
+
+/**
+ * This is an implementation of {@link BeanPostProcessor} that allows to change the role prefix of spring-security. By
+ * default spring-security is magically adding a strange prefix called "ROLE_" to your granted authorities. In order to
+ * prevent this we use this class with an empty prefix.
+ */
+public class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered {
+
+ private final String rolePrefix;
+
+ /**
+ * Der Konstruktor.
+ *
+ * @param rolePrefix das gewünschte Rollen-Präfix (z.B. der leere String).
+ */
+ public DefaultRolesPrefixPostProcessor(String rolePrefix) {
+ super();
+ this.rolePrefix = rolePrefix;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+
+ // remove this if you are not using JSR-250
+ if (bean instanceof Jsr250MethodSecurityMetadataSource) {
+ ((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix(this.rolePrefix);
+ }
+
+ if (bean instanceof DefaultMethodSecurityExpressionHandler) {
+ ((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix(this.rolePrefix);
+ }
+ if (bean instanceof DefaultWebSecurityExpressionHandler) {
+ ((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix(this.rolePrefix);
+ }
+ if (bean instanceof SecurityContextHolderAwareRequestFilter) {
+ ((SecurityContextHolderAwareRequestFilter) bean).setRolePrefix(this.rolePrefix);
+ }
+ return bean;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+
+ return bean;
+ }
+
+ @Override
+ public int getOrder() {
+
+ return PriorityOrdered.HIGHEST_PRECEDENCE;
+ }
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/LoginController.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/LoginController.java
new file mode 100644
index 0000000..fb7ce32
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/LoginController.java
@@ -0,0 +1,57 @@
+package com.devonfw.app.java.order.general.service.impl;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.ModelAndView;
+
+/**
+ * Controller for Login-Page.
+ */
+@Controller
+public class LoginController {
+
+ /**
+ * Default URL to redirect to after successfully login.
+ */
+ public final static String defaultTargetUrl = "/";
+
+ /**
+ * Builds the model for the login page---mainly focusing on the error message handling.
+ *
+ * @param authentication_failed flag for authentication failed
+ * @param invalid_session flag for invalid session
+ * @param access_denied flag for access denied
+ * @param logout flag for successful logout
+ * @return the view model
+ */
+ @RequestMapping(value = "/login**", method = {RequestMethod.GET,RequestMethod.POST})
+ public ModelAndView login(
+ @RequestParam(value = "authentication_failed", required = false) boolean authentication_failed,
+ @RequestParam(value = "invalid_session", required = false) boolean invalid_session,
+ @RequestParam(value = "access_denied", required = false) boolean access_denied,
+ @RequestParam(value = "logout", required = false) boolean logout) {
+
+ final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ if (!authentication.getPrincipal().equals("anonymousUser")) {
+ return new ModelAndView("redirect:" + defaultTargetUrl);
+ }
+
+ ModelAndView model = new ModelAndView();
+ if (authentication_failed) {
+ model.addObject("error", "Authentication failed!");
+ } else if (invalid_session) {
+ model.addObject("error", "You are currently not logged in!");
+ } else if (access_denied) {
+ model.addObject("error", "You have insufficient permissions to access this page!");
+ } else if (logout) {
+ model.addObject("msg", "Logout successful!");
+ }
+
+ return model;
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/BaseWebSecurityConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/BaseWebSecurityConfig.java
new file mode 100644
index 0000000..0a5a036
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/BaseWebSecurityConfig.java
@@ -0,0 +1,147 @@
+package com.devonfw.app.java.order.general.service.impl.config;
+
+import javax.inject.Inject;
+import javax.servlet.Filter;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.logout.LogoutFilter;
+import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
+import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+import org.springframework.security.web.csrf.CsrfFilter;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import com.devonfw.module.security.common.impl.rest.AuthenticationSuccessHandlerSendingOkHttpStatusCode;
+import com.devonfw.module.security.common.impl.rest.JsonUsernamePasswordAuthenticationFilter;
+import com.devonfw.module.security.common.impl.rest.LogoutSuccessHandlerReturningOkHttpStatusCode;
+
+/**
+ * This type serves as a base class for extensions of the {@code WebSecurityConfigurerAdapter} and provides a default
+ * configuration.
+ * Security configuration is based on {@link WebSecurityConfigurerAdapter}. This configuration is by purpose designed
+ * most simple for two channels of authentication: simple login form and rest-url.
+ */
+public abstract class BaseWebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Value("${security.cors.enabled}")
+ boolean corsEnabled = false;
+
+ @Inject
+ private UserDetailsService userDetailsService;
+
+ @Inject
+ private PasswordEncoder passwordEncoder;
+
+ private CorsFilter getCorsFilter() {
+
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ CorsConfiguration config = new CorsConfiguration();
+ config.setAllowCredentials(true);
+ config.addAllowedOrigin("*");
+ config.addAllowedHeader("*");
+ config.addAllowedMethod("OPTIONS");
+ config.addAllowedMethod("HEAD");
+ config.addAllowedMethod("GET");
+ config.addAllowedMethod("PUT");
+ config.addAllowedMethod("POST");
+ config.addAllowedMethod("DELETE");
+ config.addAllowedMethod("PATCH");
+ source.registerCorsConfiguration("/**", config);
+ return new CorsFilter(source);
+ }
+
+ /**
+ * Configure spring security to enable a simple webform-login + a simple rest login.
+ */
+ @Override
+ public void configure(HttpSecurity http) throws Exception {
+
+ String[] unsecuredResources = new String[] { "/login", "/security/**", "/services/rest/login",
+ "/services/rest/logout" };
+
+ http
+ //
+ .userDetailsService(this.userDetailsService)
+ // define all urls that are not to be secured
+ .authorizeRequests().antMatchers(unsecuredResources).permitAll().anyRequest().authenticated().and()
+
+ // activate crsf check for a selection of urls (but not for login & logout)
+ .csrf().disable().httpBasic().and()
+
+ .headers().frameOptions().sameOrigin().and()
+
+ // configure parameters for simple form login (and logout)
+ .formLogin().successHandler(new SimpleUrlAuthenticationSuccessHandler()).defaultSuccessUrl("/")
+ .failureUrl("/login.html?error").loginProcessingUrl("/j_spring_security_login").usernameParameter("username")
+ .passwordParameter("password").and()
+ // logout via POST is possible
+ .logout().logoutSuccessUrl("/login.html").and()
+
+ // register login and logout filter that handles rest logins
+ .addFilterAfter(getSimpleRestAuthenticationFilter(), BasicAuthenticationFilter.class)
+ .addFilterAfter(getSimpleRestLogoutFilter(), LogoutFilter.class);
+
+ if (this.corsEnabled) {
+ http.addFilterBefore(getCorsFilter(), CsrfFilter.class);
+ }
+ }
+
+ /**
+ * Create a simple filter that allows logout on a REST Url /services/rest/logout and returns a simple HTTP status 200
+ * ok.
+ *
+ * @return the filter.
+ */
+ protected Filter getSimpleRestLogoutFilter() {
+
+ LogoutFilter logoutFilter = new LogoutFilter(new LogoutSuccessHandlerReturningOkHttpStatusCode(),
+ new SecurityContextLogoutHandler());
+
+ // configure logout for rest logouts
+ logoutFilter.setLogoutRequestMatcher(new AntPathRequestMatcher("/services/rest/logout"));
+
+ return logoutFilter;
+ }
+
+ /**
+ * Create a simple authentication filter for REST logins that reads user-credentials from a json-parameter and returns
+ * status 200 instead of redirect after login.
+ *
+ * @return the {@link JsonUsernamePasswordAuthenticationFilter}.
+ * @throws Exception if something goes wrong.
+ */
+ protected JsonUsernamePasswordAuthenticationFilter getSimpleRestAuthenticationFilter() throws Exception {
+
+ JsonUsernamePasswordAuthenticationFilter jsonFilter = new JsonUsernamePasswordAuthenticationFilter(
+ new AntPathRequestMatcher("/services/rest/login"));
+ jsonFilter.setPasswordParameter("j_password");
+ jsonFilter.setUsernameParameter("j_username");
+ jsonFilter.setAuthenticationManager(authenticationManager());
+ // set failurehandler that uses no redirect in case of login failure; just HTTP-status: 401
+ jsonFilter.setAuthenticationManager(authenticationManagerBean());
+ jsonFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
+ // set successhandler that uses no redirect in case of login success; just HTTP-status: 200
+ jsonFilter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandlerSendingOkHttpStatusCode());
+ return jsonFilter;
+ }
+
+ @SuppressWarnings("javadoc")
+ @Inject
+ public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
+
+ auth.inMemoryAuthentication().withUser("waiter").password(this.passwordEncoder.encode("waiter")).roles("Waiter")
+ .and().withUser("cook").password(this.passwordEncoder.encode("cook")).roles("Cook").and().withUser("barkeeper")
+ .password(this.passwordEncoder.encode("barkeeper")).roles("Barkeeper").and().withUser("chief")
+ .password(this.passwordEncoder.encode("chief")).roles("Chief");
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/ServletInitializer.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/ServletInitializer.java
new file mode 100644
index 0000000..96e8033
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/ServletInitializer.java
@@ -0,0 +1,23 @@
+package com.devonfw.app.java.order.general.service.impl.config;
+
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.Configuration;
+
+import com.devonfw.app.java.order.SpringBootApp;
+
+/**
+ * This auto configuration will be used by spring boot to enable traditional deployment to a servlet container. You may
+ * remove this class if you run your application with embedded tomcat only. Tomcat startup will be twice as fast.
+ */
+@Configuration
+@EnableAutoConfiguration
+public class ServletInitializer extends SpringBootServletInitializer {
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+
+ return application.sources(SpringBootApp.class);
+ }
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebConfig.java
new file mode 100644
index 0000000..2aa62f0
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebConfig.java
@@ -0,0 +1,80 @@
+package com.devonfw.app.java.order.general.service.impl.config;
+
+import javax.servlet.Filter;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.filter.CharacterEncodingFilter;
+
+import com.devonfw.module.logging.common.api.DiagnosticContextFacade;
+import com.devonfw.module.logging.common.impl.DiagnosticContextFacadeImpl;
+import com.devonfw.module.logging.common.impl.DiagnosticContextFilter;
+import com.devonfw.module.logging.common.impl.PerformanceLogFilter;
+import com.devonfw.module.service.common.api.constants.ServiceConstants;
+
+/**
+ * Registers a number of filters for web requests.
+ */
+@Configuration
+public class WebConfig {
+
+ private @Autowired AutowireCapableBeanFactory beanFactory;
+
+ /**
+ * @return the {@link FilterRegistrationBean} to register the {@link PerformanceLogFilter} that will log all requests
+ * with their duration and status code.
+ */
+ @Bean
+ public FilterRegistrationBean performanceLogFilter() {
+
+ FilterRegistrationBean registration = new FilterRegistrationBean();
+ Filter performanceLogFilter = new PerformanceLogFilter();
+ this.beanFactory.autowireBean(performanceLogFilter);
+ registration.setFilter(performanceLogFilter);
+ registration.addUrlPatterns("/*");
+ return registration;
+ }
+
+ /**
+ * @return the {@link DiagnosticContextFacade} implementation.
+ */
+ @Bean(name = "DiagnosticContextFacade")
+ public DiagnosticContextFacade diagnosticContextFacade() {
+
+ return new DiagnosticContextFacadeImpl();
+ }
+
+ /**
+ * @return the {@link FilterRegistrationBean} to register the {@link DiagnosticContextFilter} that adds the
+ * correlation id as MDC so it will be included in all associated logs.
+ */
+ @Bean
+ public FilterRegistrationBean diagnosticContextFilter() {
+
+ FilterRegistrationBean registration = new FilterRegistrationBean();
+ Filter diagnosticContextFilter = new DiagnosticContextFilter();
+ this.beanFactory.autowireBean(diagnosticContextFilter);
+ registration.setFilter(diagnosticContextFilter);
+ registration.addUrlPatterns(ServiceConstants.URL_PATH_SERVICES + "/*");
+ return registration;
+ }
+
+ /**
+ * @return the {@link FilterRegistrationBean} to register the {@link CharacterEncodingFilter} to set the encoding.
+ */
+ @Bean
+ public FilterRegistrationBean setCharacterEncodingFilter() {
+
+ FilterRegistrationBean registration = new FilterRegistrationBean();
+ CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
+ characterEncodingFilter.setEncoding("UTF-8");
+ characterEncodingFilter.setForceEncoding(false);
+ this.beanFactory.autowireBean(characterEncodingFilter);
+ registration.setFilter(characterEncodingFilter);
+ registration.addUrlPatterns("/*");
+ return registration;
+ }
+}
\ No newline at end of file
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityBeansConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityBeansConfig.java
new file mode 100644
index 0000000..fbe6e41
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityBeansConfig.java
@@ -0,0 +1,79 @@
+package com.devonfw.app.java.order.general.service.impl.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.csrf.CsrfTokenRepository;
+import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
+
+import com.devonfw.app.java.order.general.logic.impl.config.DefaultRolesPrefixPostProcessor;
+import com.devonfw.module.security.common.api.accesscontrol.AccessControlProvider;
+import com.devonfw.module.security.common.base.accesscontrol.AccessControlSchemaProvider;
+import com.devonfw.module.security.common.impl.accesscontrol.AccessControlProviderImpl;
+import com.devonfw.module.security.common.impl.accesscontrol.AccessControlSchemaProviderImpl;
+
+/**
+ * This configuration class provides factory methods for several Spring security related beans.
+ *
+ */
+@Configuration
+public class WebSecurityBeansConfig {
+
+ /**
+ * This method provides a new instance of {@code AccessControlProvider}
+ *
+ * @return the newly created {@code AccessControlProvider}
+ */
+ @Bean
+ public AccessControlProvider accessControlProvider() {
+
+ return new AccessControlProviderImpl();
+ }
+
+ /**
+ * This method provides a new instance of {@code AccessControlSchemaProvider}
+ *
+ * @return the newly created {@code AccessControlSchemaProvider}
+ */
+ @Bean
+ public AccessControlSchemaProvider accessControlSchemaProvider() {
+
+ return new AccessControlSchemaProviderImpl();
+ }
+
+ /**
+ * This method provides a new instance of {@code CsrfTokenRepository}
+ *
+ * @return the newly created {@code CsrfTokenRepository}
+ */
+ @Bean
+ public CsrfTokenRepository csrfTokenRepository() {
+
+ return new HttpSessionCsrfTokenRepository();
+ }
+
+ /**
+ * This method provides a new instance of {@code DefaultRolesPrefixPostProcessor}
+ *
+ * @return the newly create {@code DefaultRolesPrefixPostProcessor}
+ */
+ @Bean
+ public static DefaultRolesPrefixPostProcessor defaultRolesPrefixPostProcessor() {
+
+ // By default Spring-Security is setting the prefix "ROLE_" for all permissions/authorities.
+ // We disable this undesired behavior here...
+ return new DefaultRolesPrefixPostProcessor("");
+ }
+
+ /**
+ * This method provide a new instance of {@code DelegatingPasswordEncoder}
+ *
+ * @return the newly create {@code DelegatingPasswordEncoder}
+ */
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+
+ return PasswordEncoderFactories.createDelegatingPasswordEncoder();
+ }
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityConfig.java
new file mode 100644
index 0000000..89ca1b6
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityConfig.java
@@ -0,0 +1,21 @@
+package com.devonfw.app.java.order.general.service.impl.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+import com.devonfw.module.basic.common.api.config.SpringProfileConstants;
+
+/**
+ * Security configuration based on {@link WebSecurityConfigurerAdapter}. This configuration is by purpose designed most
+ * simple for two channels of authentication: simple login form and rest-url. (Copied from
+ * {@link com.devonfw.app.java.order.general.service.impl.config.BaseWebSecurityConfig}
+ *
+ */
+@Configuration
+@EnableWebSecurity
+@Profile(SpringProfileConstants.NOT_JUNIT)
+public class WebSecurityConfig extends BaseWebSecurityConfig {
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/ApplicationAccessDeniedHandler.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/ApplicationAccessDeniedHandler.java
new file mode 100644
index 0000000..cf47678
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/ApplicationAccessDeniedHandler.java
@@ -0,0 +1,46 @@
+package com.devonfw.app.java.order.general.service.impl.rest;
+
+import com.devonfw.module.rest.service.impl.RestServiceExceptionFacade;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Response;
+
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.web.access.AccessDeniedHandler;
+
+/**
+ *
+ */
+@Named("ApplicationAccessDeniedHandler")
+public class ApplicationAccessDeniedHandler implements AccessDeniedHandler {
+
+ private RestServiceExceptionFacade exceptionFacade;
+
+ @Override
+ public void handle(HttpServletRequest request, HttpServletResponse response,
+ AccessDeniedException accessDeniedException) throws IOException, ServletException {
+
+ Response restResponse = this.exceptionFacade.toResponse(accessDeniedException);
+ Object entity = restResponse.getEntity();
+ response.setStatus(restResponse.getStatus());
+ if (entity != null) {
+ response.getWriter().write(entity.toString());
+ }
+ }
+
+ /**
+ * @param exceptionFacade the exceptionFacade to set
+ */
+ @Inject
+ public void setExceptionFacade(RestServiceExceptionFacade exceptionFacade) {
+
+ this.exceptionFacade = exceptionFacade;
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImpl.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImpl.java
new file mode 100644
index 0000000..0bd73b0
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImpl.java
@@ -0,0 +1,77 @@
+package com.devonfw.app.java.order.general.service.impl.rest;
+
+import javax.annotation.security.PermitAll;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.transaction.Transactional;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.csrf.CsrfToken;
+import org.springframework.security.web.csrf.CsrfTokenRepository;
+
+import com.devonfw.app.java.order.general.common.api.exception.NoActiveUserException;
+import com.devonfw.app.java.order.general.common.api.to.UserProfileTo;
+import com.devonfw.app.java.order.general.service.api.rest.SecurityRestService;
+
+/**
+ * Implementation of {@link SecurityRestService}.
+ */
+@Named
+@Transactional
+public class SecurityRestServiceImpl implements SecurityRestService {
+
+ /** Logger instance. */
+ private static final Logger LOG = LoggerFactory.getLogger(SecurityRestServiceImpl.class);
+
+ /**
+ * Use {@link CsrfTokenRepository} for CSRF protection.
+ */
+ private CsrfTokenRepository csrfTokenRepository;
+
+ @Override
+ @PermitAll
+ public CsrfToken getCsrfToken(HttpServletRequest request, HttpServletResponse response) {
+
+ CsrfToken token = this.csrfTokenRepository.loadToken(request);
+ if (token == null) {
+ LOG.error("No CsrfToken could be found - instantiating a new Token");
+ token = this.csrfTokenRepository.generateToken(request);
+ this.csrfTokenRepository.saveToken(token, request, response);
+ }
+ return token;
+ }
+
+ @Override
+ @PermitAll
+ public UserProfileTo getCurrentUser() {
+
+ SecurityContext context = SecurityContextHolder.getContext();
+ Authentication authentication = null;
+ if (context != null) {
+ authentication = context.getAuthentication();
+ }
+ if (authentication == null) {
+ throw new NoActiveUserException();
+ }
+ UserDetails user = (UserDetails) authentication.getPrincipal();
+ UserProfileTo profile = new UserProfileTo();
+ profile.setLogin(user.getUsername());
+ return profile;
+ }
+
+ /**
+ * @param csrfTokenRepository the csrfTokenRepository to set
+ */
+ @Inject
+ public void setCsrfTokenRepository(CsrfTokenRepository csrfTokenRepository) {
+
+ this.csrfTokenRepository = csrfTokenRepository;
+ }
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/CustomerEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/CustomerEntity.java
new file mode 100644
index 0000000..3222e46
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/CustomerEntity.java
@@ -0,0 +1,83 @@
+package com.devonfw.app.java.order.orderservice.dataaccess.api;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.validation.constraints.NotNull;
+
+import com.devonfw.app.java.order.general.dataaccess.api.ApplicationPersistenceEntity;
+import com.devonfw.app.java.order.orderservice.common.api.Customer;
+
+/**
+ * @author ARMIKA
+ */
+@Entity(name = "Customer")
+public class CustomerEntity extends ApplicationPersistenceEntity implements Customer {
+
+ private String firstname;
+
+ private String lastname;
+
+ private Set orders;
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @return orders
+ */
+ @OneToMany(mappedBy = "owner")
+ public Set getOrders() {
+
+ return Collections.unmodifiableSet(this.orders);
+ }
+
+ /**
+ * @param orders new value of {@link #getorders}.
+ */
+ public void setOrders(@NotNull Set orders) {
+
+ this.orders.clear();
+ this.orders.addAll(orders);
+ }
+
+ /**
+ * @return firstname
+ */
+ @Override
+ public String getFirstname() {
+
+ return this.firstname;
+ }
+
+ /**
+ * @param firstname new value of {@link #getfirstname}.
+ */
+ @SuppressWarnings("javadoc")
+ @Override
+ public void setFirstname(String firstname) {
+
+ this.firstname = firstname;
+ }
+
+ /**
+ * @return lastname
+ */
+ @Override
+ public String getLastname() {
+
+ return this.lastname;
+ }
+
+ /**
+ * @param lastname new value of {@link #getlastname}.
+ */
+ @SuppressWarnings("javadoc")
+ @Override
+ public void setLastname(String lastname) {
+
+ this.lastname = lastname;
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/ItemEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/ItemEntity.java
new file mode 100644
index 0000000..8a86a4a
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/ItemEntity.java
@@ -0,0 +1,78 @@
+package com.devonfw.app.java.order.orderservice.dataaccess.api;
+
+import javax.persistence.Entity;
+
+import com.devonfw.app.java.order.general.dataaccess.api.ApplicationPersistenceEntity;
+import com.devonfw.app.java.order.orderservice.common.api.Item;
+
+/**
+ * @author ARMIKA Class representing Item Entity
+ */
+@Entity(name = "Item")
+public class ItemEntity extends ApplicationPersistenceEntity implements Item {
+
+ private String name;
+
+ private String description;
+
+ private Double price;
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @return name
+ */
+ @Override
+ public String getName() {
+
+ return this.name;
+ }
+
+ /**
+ * @param name new value of {@link #getname}.
+ */
+ @SuppressWarnings("javadoc")
+ @Override
+ public void setName(String name) {
+
+ this.name = name;
+ }
+
+ /**
+ * @return description
+ */
+ @Override
+ public String getDescription() {
+
+ return this.description;
+ }
+
+ /**
+ * @param description new value of {@link #getdescription}.
+ */
+ @SuppressWarnings("javadoc")
+ @Override
+ public void setDescription(String description) {
+
+ this.description = description;
+ }
+
+ /**
+ * @return price
+ */
+ @Override
+ public Double getPrice() {
+
+ return this.price;
+ }
+
+ /**
+ * @param price new value of {@link #getprice}.
+ */
+ @Override
+ public void setPrice(Double price) {
+
+ this.price = price;
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/OrderEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/OrderEntity.java
new file mode 100644
index 0000000..39b31a0
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/OrderEntity.java
@@ -0,0 +1,145 @@
+package com.devonfw.app.java.order.orderservice.dataaccess.api;
+
+import java.time.LocalDate;
+import java.util.Set;
+
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.Transient;
+
+import com.devonfw.app.java.order.general.dataaccess.api.ApplicationPersistenceEntity;
+import com.devonfw.app.java.order.orderservice.common.api.Order;
+import com.devonfw.app.java.order.orderservice.common.api.OrderStatus;
+
+/**
+ * @author ARMIKA
+ */
+@Entity(name = "Ordersummary")
+public class OrderEntity extends ApplicationPersistenceEntity implements Order {
+
+ private LocalDate creationDate;
+
+ private Set orderPositions;
+
+ private CustomerEntity owner;
+
+ private Double price;
+
+ private OrderStatus status;
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @return creationDate
+ */
+ @Override
+ public LocalDate getCreationDate() {
+
+ return this.creationDate;
+ }
+
+ /**
+ * @param creationDate new value of {@link #getcreationDate}.
+ */
+ @Override
+ public void setCreationDate(LocalDate creationDate) {
+
+ this.creationDate = creationDate;
+ }
+
+ /**
+ * @return orderPositions
+ */
+ @ManyToMany
+ @JoinTable(name = "OrderPosition", joinColumns = @JoinColumn(name = "orderId", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "itemId", referencedColumnName = "id"))
+ public Set getOrderPositions() {
+
+ return this.orderPositions;
+ }
+
+ /**
+ * @param orderPositions new value of {@link #getorderPositions}.
+ */
+ public void setOrderPositions(Set orderPositions) {
+
+ this.orderPositions = orderPositions;
+ }
+
+ /**
+ * @return owner
+ */
+ @ManyToOne
+ @JoinColumn(name = "ownerId")
+ public CustomerEntity getOwner() {
+
+ return this.owner;
+ }
+
+ /**
+ * @param owner new value of {@link #getowner}.
+ */
+ public void setOwner(CustomerEntity owner) {
+
+ this.owner = owner;
+ }
+
+ /**
+ * @return price
+ */
+ @Override
+ public Double getPrice() {
+
+ return this.price;
+ }
+
+ /**
+ * @param price new value of {@link #getprice}.
+ */
+ @Override
+ public void setPrice(Double price) {
+
+ this.price = price;
+ }
+
+ /**
+ * @return status
+ */
+ @Override
+ @Enumerated(EnumType.STRING)
+ public OrderStatus getStatus() {
+
+ return this.status;
+ }
+
+ /**
+ * @param status new value of {@link #getstatus}.
+ */
+ @Override
+ public void setStatus(OrderStatus status) {
+
+ this.status = status;
+ }
+
+ @Override
+ @Transient
+ public Long getOwnerId() {
+
+ if (getOwner() != null)
+ return getOwner().getId();
+ return null;
+ }
+
+ @Override
+ public void setOwnerId(Long ownerId) {
+
+ CustomerEntity e = new CustomerEntity();
+ e.setId(ownerId);
+ setOwner(e);
+ }
+
+}
diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/repo/ItemRepository.java b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/repo/ItemRepository.java
new file mode 100644
index 0000000..cbe5499
--- /dev/null
+++ b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/repo/ItemRepository.java
@@ -0,0 +1,12 @@
+package com.devonfw.app.java.order.orderservice.dataaccess.api.repo;
+
+import com.devonfw.app.java.order.orderservice.dataaccess.api.ItemEntity;
+import com.devonfw.module.jpa.dataaccess.api.data.DefaultRepository;
+
+/**
+ * @author ARMIKA
+ *
+ */
+public interface ItemRepository extends DefaultRepository {
+
+}
diff --git a/order-service/core/src/main/resources/META-INF/cxf/org.apache.cxf.Logger b/order-service/core/src/main/resources/META-INF/cxf/org.apache.cxf.Logger
new file mode 100644
index 0000000..27dd788
--- /dev/null
+++ b/order-service/core/src/main/resources/META-INF/cxf/org.apache.cxf.Logger
@@ -0,0 +1 @@
+org.apache.cxf.common.logging.Slf4jLogger
\ No newline at end of file
diff --git a/order-service/core/src/main/resources/META-INF/orm.xml b/order-service/core/src/main/resources/META-INF/orm.xml
new file mode 100644
index 0000000..55d30ec
--- /dev/null
+++ b/order-service/core/src/main/resources/META-INF/orm.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/order-service/core/src/main/resources/application.properties b/order-service/core/src/main/resources/application.properties
new file mode 100644
index 0000000..c3bc26b
--- /dev/null
+++ b/order-service/core/src/main/resources/application.properties
@@ -0,0 +1,32 @@
+# This is the configuration file shipped with the application that contains reasonable defaults.
+# Environment specific configurations are configured in config/application.properties.
+# If you are running in a servlet container you may add this to lib/config/application.properties in case you do not
+# want to touch the WAR file.
+
+# server.port=8080
+
+spring.application.name=order-service
+server.servlet.context-path=/
+
+security.expose.error.details=false
+security.cors.enabled=false
+spring.jpa.hibernate.ddl-auto=validate
+
+# Datasource for accessing the database
+# https://github.com/spring-projects/spring-boot/blob/d3c34ee3d1bfd3db4a98678c524e145ef9bca51c/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/DatabaseDriver.java
+spring.jpa.database=h2
+# spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+# spring.datasource.driver-class-name=org.h2.Driver
+spring.datasource.username=sa
+
+# Hibernate NamingStrategy has been deprecated and then removed in favor of two step naming strategy ImplicitNamingStrategy and PhysicalNamingStrategy
+spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
+spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+
+# to prevent that Spring Boot launches batch jobs on startup
+# might otherwise lead to errors if job parameters are needed (or lead to unwanted modifications and longer startup times)
+# see http://stackoverflow.com/questions/22318907/how-to-stop-spring-batch-scheduled-jobs-from-running-at-first-time-when-executin
+spring.batch.job.enabled=false
+
+# Flyway for Database Setup and Migrations
+spring.flyway.locations=classpath:db/migration,classpath:db/type/h2
diff --git a/order-service/core/src/main/resources/config/app/common/dozer-mapping.xml b/order-service/core/src/main/resources/config/app/common/dozer-mapping.xml
new file mode 100644
index 0000000..3d459e3
--- /dev/null
+++ b/order-service/core/src/main/resources/config/app/common/dozer-mapping.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ true
+
+
+
+ java.lang.Long
+ java.lang.Integer
+ java.lang.Number
+
+
+
+
+
+
+
+
+ com.devonfw.app.java.order.general.dataaccess.api.ApplicationPersistenceEntity
+ com.devonfw.module.basic.common.api.to.AbstractEto
+
+ this
+ persistentEntity
+
+
+
diff --git a/order-service/core/src/main/resources/config/app/dataaccess/NamedQueries.xml b/order-service/core/src/main/resources/config/app/dataaccess/NamedQueries.xml
new file mode 100644
index 0000000..ee83c8c
--- /dev/null
+++ b/order-service/core/src/main/resources/config/app/dataaccess/NamedQueries.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/order-service/core/src/main/resources/config/app/security/access-control-schema.xml b/order-service/core/src/main/resources/config/app/security/access-control-schema.xml
new file mode 100644
index 0000000..418f6eb
--- /dev/null
+++ b/order-service/core/src/main/resources/config/app/security/access-control-schema.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/order-service/core/src/main/resources/config/application.properties b/order-service/core/src/main/resources/config/application.properties
new file mode 100644
index 0000000..08e3c5c
--- /dev/null
+++ b/order-service/core/src/main/resources/config/application.properties
@@ -0,0 +1,23 @@
+# This is the spring boot configuration file for development. It will not be included into the application.
+# In order to set specific configurations in a regular installed environment create an according file
+# config/application.properties in the server. If you are deploying the application to a servlet container as untouched
+# WAR file you can locate this config folder in ${symbol_dollar}{CATALINA_BASE}/lib. If you want to deploy multiple applications to
+# the same container (not recommended by default) you need to ensure the WARs are extracted in webapps folder and locate
+# the config folder inside the WEB-INF/classes folder of the webapplication.
+
+server.port=8081
+server.servlet.context-path=/order-service
+
+# Datasource for accessing the database
+# See https://github.com/devonfw-wiki/devon4j/wiki/guide-configuration#security-configuration
+#jasypt.encryptor.password=none
+#spring.datasource.password=ENC(7CnHiadYc0Wh2FnWADNjJg==)
+spring.datasource.password=
+spring.datasource.url=jdbc:h2:mem:order-service
+
+# Enable JSON pretty printing
+spring.jackson.serialization.INDENT_OUTPUT=true
+
+# Flyway for Database Setup and Migrations
+spring.flyway.enabled=true
+spring.flyway.clean-on-validation-error=true
diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0004__Add_blob_data.sql b/order-service/core/src/main/resources/db/migration/1.0/V0004__Add_blob_data.sql
new file mode 100644
index 0000000..574db16
--- /dev/null
+++ b/order-service/core/src/main/resources/db/migration/1.0/V0004__Add_blob_data.sql
@@ -0,0 +1 @@
+INSERT INTO BinaryObject(id, ModificationCounter, filesize, content, mimeType) VALUES (10, 0, 1150 ,'00000100010010100000010020006804000016000000280000001000000020000000010020000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000ECBD48EFECBD48FFECBD48FFECBD48FFECBD48FFECBD48F90000000000000000000000000000000000000000000000000000000000000000ECBD48FAECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FF0000000000000000000000000000000000000000ECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FF000000000000000000000000ECBD48FAECBD48FFFFFFFFFFECBD48FFFFFFFFFFFFFFFFFFEEC35EFFFFFEFDFFFFFFFFFFFFFFFFFFFFFFFFFFEFC86BFFECBD48FFECBD48FF0000000000000000ECBD48FFECBD48FFF4D696FFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FF00000000ECBD48EFECBD48FFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1CB7FFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFFFFFFFFFECBD48FFECBD48FFECBD48F9ECBD48FFECBD48FFECBD48FFFFFFFFFFFFFFFFFFFDF8EFFFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFFFFFFFFFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFECBD48FFECBD48FFECBD4AFFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFFFFFFFFFFEF9F3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFECBD48FFECBD48F9ECBD48FFECBD48FFFFFFFFFFEEC45FFFECBD48FFF1CD7EFFFFFFFFFFFFFFFFFFFFFFFFFFEDC156FFFFFFFFFFFFFFFFFFECBD48FFECBD48FFECBD48EF00000000ECBD48FFECBD48FFFFFFFFFFFEF9F2FFECBD48FFFCF5E8FFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFFFFFFFFFFFFFFFFFECBD48FFECBD48FF0000000000000000ECBD48FFECBD48FFEFC769FFFFFFFFFFFFFFFFFFFFFFFFFFEEC35EFFEEC25CFFFFFFFFFFFFFFFFFFFFFFFFFFEFC86BFFECBD48FFECBD48FA000000000000000000000000ECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FF0000000000000000000000000000000000000000ECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FA0000000000000000000000000000000000000000000000000000000000000000ECBD48F9ECBD48FFECBD48FFECBD48FFECBD48FFECBD48EF0000000000000000000000000000000000000000F81F0000E0070000C003000080010000800100000000000000000000000000000000000000000000000000008001000080010000C0030000E0070000F81F0000', 'image/vnd.microsoft.icon');
diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0005__CreateOrderServiceSchema.sql b/order-service/core/src/main/resources/db/migration/1.0/V0005__CreateOrderServiceSchema.sql
new file mode 100644
index 0000000..c7a81c3
--- /dev/null
+++ b/order-service/core/src/main/resources/db/migration/1.0/V0005__CreateOrderServiceSchema.sql
@@ -0,0 +1,3 @@
+CREATE SCHEMA order_service AUTHORIZATION sa;
+
+set schema order_service;
\ No newline at end of file
diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0006__CreateItemCustomerOrderSummaryTables.sql b/order-service/core/src/main/resources/db/migration/1.0/V0006__CreateItemCustomerOrderSummaryTables.sql
new file mode 100644
index 0000000..a26020c
--- /dev/null
+++ b/order-service/core/src/main/resources/db/migration/1.0/V0006__CreateItemCustomerOrderSummaryTables.sql
@@ -0,0 +1,28 @@
+CREATE TABLE Item (
+ id BIGINT NOT NULL AUTO_INCREMENT,
+ modificationCounter INTEGER NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ description VARCHAR(255),
+ price DOUBLE NOT NULL,
+ PRIMARY KEY (ID),
+ CONSTRAINT UC_Item_name UNIQUE(name)
+);
+
+CREATE TABLE Customer (
+ id BIGINT NOT NULL AUTO_INCREMENT,
+ modificationCounter INTEGER NOT NULL,
+ firstname VARCHAR(255) NOT NULL,
+ lastname VARCHAR(255) NOT NULL,
+ PRIMARY KEY (ID),
+);
+
+CREATE TABLE Ordersummary (
+ id BIGINT NOT NULL AUTO_INCREMENT,
+ modificationCounter INTEGER NOT NULL,
+ price DOUBLE,
+ ownerId BIGINT NOT NULL,
+ creationDate DATE NOT NULL,
+ status VARCHAR(255) NOT NULL,
+ PRIMARY KEY (ID),
+ CONSTRAINT FK_OrderSummary_owner FOREIGN KEY(ownerId) REFERENCES Customer(id)
+);
\ No newline at end of file
diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0007__CreateOrderPositionTable .sql b/order-service/core/src/main/resources/db/migration/1.0/V0007__CreateOrderPositionTable .sql
new file mode 100644
index 0000000..e96e0e8
--- /dev/null
+++ b/order-service/core/src/main/resources/db/migration/1.0/V0007__CreateOrderPositionTable .sql
@@ -0,0 +1,6 @@
+CREATE TABLE OrderPosition (
+ orderId BIGINT NOT NULL,
+ itemId BIGINT NOT NULL,
+ CONSTRAINT FK_OrderPosition_order FOREIGN KEY(orderId) REFERENCES OrderSummary(id),
+ CONSTRAINT FK_OrderPosition_item FOREIGN KEY(itemId) REFERENCES Item(id)
+);
\ No newline at end of file
diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0008__InsertItemData.sql b/order-service/core/src/main/resources/db/migration/1.0/V0008__InsertItemData.sql
new file mode 100644
index 0000000..522a6a6
--- /dev/null
+++ b/order-service/core/src/main/resources/db/migration/1.0/V0008__InsertItemData.sql
@@ -0,0 +1,2 @@
+INSERT INTO Item(id, ModificationCounter, name, description, price)
+VALUES (21, 0, 'spaghetti bolognese', 'Italy', 250);
\ No newline at end of file
diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0009__InsertCustomerData.sql b/order-service/core/src/main/resources/db/migration/1.0/V0009__InsertCustomerData.sql
new file mode 100644
index 0000000..01ab156
--- /dev/null
+++ b/order-service/core/src/main/resources/db/migration/1.0/V0009__InsertCustomerData.sql
@@ -0,0 +1,2 @@
+INSERT INTO Customer(id, ModificationCounter, firstname, lastname)
+VALUES (31, 0, 'John', 'Travolta');
diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0010__InsertOrderSummaryData.sql b/order-service/core/src/main/resources/db/migration/1.0/V0010__InsertOrderSummaryData.sql
new file mode 100644
index 0000000..f9ca6f8
--- /dev/null
+++ b/order-service/core/src/main/resources/db/migration/1.0/V0010__InsertOrderSummaryData.sql
@@ -0,0 +1,2 @@
+INSERT INTO OrderSummary(id, ModificationCounter, price, ownerId, creationDate, status)
+VALUES (41, 0, 671.10, 31, '2019-03-15', 'SERVED');
diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0011__InsertOrderPositionData.sql b/order-service/core/src/main/resources/db/migration/1.0/V0011__InsertOrderPositionData.sql
new file mode 100644
index 0000000..5969c6f
--- /dev/null
+++ b/order-service/core/src/main/resources/db/migration/1.0/V0011__InsertOrderPositionData.sql
@@ -0,0 +1,2 @@
+INSERT INTO OrderPosition(orderId, itemId)
+VALUES (41, 21);
\ No newline at end of file
diff --git a/order-service/core/src/main/resources/db/type/h2/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/h2/V0001__Create_Sequence.sql
new file mode 100644
index 0000000..2210438
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/h2/V0001__Create_Sequence.sql
@@ -0,0 +1,2 @@
+-- Leave a large ID space reserved for master-data and test-data
+CREATE SEQUENCE HIBERNATE_SEQUENCE START WITH 1000000;
diff --git a/order-service/core/src/main/resources/db/type/h2/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/h2/V0002__Create_RevInfo.sql
new file mode 100644
index 0000000..8a41c6f
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/h2/V0002__Create_RevInfo.sql
@@ -0,0 +1,6 @@
+-- *** RevInfo (Commit log for envers audit trail) ***
+CREATE TABLE RevInfo(
+ id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
+ "timestamp" BIGINT NOT NULL,
+ userLogin VARCHAR(255)
+);
\ No newline at end of file
diff --git a/order-service/core/src/main/resources/db/type/h2/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/h2/V0003__Create_BinaryObject.sql
new file mode 100644
index 0000000..d91838c
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/h2/V0003__Create_BinaryObject.sql
@@ -0,0 +1,9 @@
+-- *** BinaryObject (BLOBs) ***
+CREATE TABLE BinaryObject (
+ id BIGINT NOT NULL AUTO_INCREMENT,
+ modificationCounter INTEGER NOT NULL,
+ content BLOB(2147483647),
+ filesize BIGINT NOT NULL,
+ mimeType VARCHAR(255),
+ PRIMARY KEY (ID)
+);
diff --git a/order-service/core/src/main/resources/db/type/hana/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/hana/V0001__Create_Sequence.sql
new file mode 100644
index 0000000..49953d2
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/hana/V0001__Create_Sequence.sql
@@ -0,0 +1,18 @@
+-- Leave a large ID space reserved for master-data and test-data
+CREATE SEQUENCE HIBERNATE_SEQUENCE START WITH 1000000;
+
+-- hana does not support Dateadd function out of the box so we add it here to be able to use it for master-data SQLs
+CREATE FUNCTION DATEADD(IN DATETYPE NVARCHAR(256), IN NUMBER INTEGER, IN TS TIMESTAMP)
+RETURNS TSADD TIMESTAMP
+AS
+BEGIN
+ IF :DATETYPE = 'DAY'
+ THEN
+ TSADD = ADD_DAYS(:TS, :NUMBER);
+ ELSEIF :DATETYPE = 'HOUR'
+ THEN
+ TSADD = ADD_SECONDS(:TS, :NUMBER * 3600);
+ ELSE
+ SIGNAL SQL_ERROR_CODE 10000 SET MESSAGE_TEXT = 'Unsupported date type: ' || :DATETYPE;
+ END IF;
+END;
\ No newline at end of file
diff --git a/order-service/core/src/main/resources/db/type/hana/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/hana/V0002__Create_RevInfo.sql
new file mode 100644
index 0000000..38b650b
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/hana/V0002__Create_RevInfo.sql
@@ -0,0 +1,7 @@
+-- *** RevInfo (Commit log for envers audit trail) ***
+CREATE COLUMN TABLE RevInfo(
+ id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
+ "timestamp" BIGINT NOT NULL,
+ userLogin VARCHAR(255),
+ PRIMARY KEY (ID)
+);
diff --git a/order-service/core/src/main/resources/db/type/hana/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/hana/V0003__Create_BinaryObject.sql
new file mode 100644
index 0000000..6463189
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/hana/V0003__Create_BinaryObject.sql
@@ -0,0 +1,9 @@
+-- *** BinaryObject (BLOBs) ***
+CREATE COLUMN TABLE BinaryObject (
+ id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ modificationCounter INTEGER NOT NULL,
+ content BLOB,
+ filesize BIGINT NOT NULL,
+ mimeType VARCHAR(255),
+ CONSTRAINT PK_BinaryObject_id PRIMARY KEY(ID)
+);
diff --git a/order-service/core/src/main/resources/db/type/mssql/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/mssql/V0001__Create_Sequence.sql
new file mode 100644
index 0000000..80704fa
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/mssql/V0001__Create_Sequence.sql
@@ -0,0 +1 @@
+-- no sequences are used in MS-SQL Server instead use IDENTITY(«seed»,1) for every ID
diff --git a/order-service/core/src/main/resources/db/type/mssql/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/mssql/V0002__Create_RevInfo.sql
new file mode 100644
index 0000000..ceae13d
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/mssql/V0002__Create_RevInfo.sql
@@ -0,0 +1,6 @@
+-- *** RevInfo (Commit log for envers audit trail) ***
+CREATE TABLE REVINFO(
+ id BIGINT NOT NULL IDENTITY(1,1),
+ timestamp BIGINT NOT NULL,
+ userLogin VARCHAR(255)
+);
diff --git a/order-service/core/src/main/resources/db/type/mssql/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/mssql/V0003__Create_BinaryObject.sql
new file mode 100644
index 0000000..4a454be
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/mssql/V0003__Create_BinaryObject.sql
@@ -0,0 +1,9 @@
+-- *** BinaryObject (BLOBs) ***
+CREATE TABLE BINARYOBJECT (
+ id BIGINT NOT NULL IDENTITY(10,1),
+ modificationCounter INTEGER NOT NULL,
+ content varbinary(max),
+ filesize BIGINT NOT NULL,
+ mimeType VARCHAR(255),
+ PRIMARY KEY (ID)
+);
diff --git a/order-service/core/src/main/resources/db/type/mysql/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/mysql/V0001__Create_Sequence.sql
new file mode 100644
index 0000000..c5da8cf
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/mysql/V0001__Create_Sequence.sql
@@ -0,0 +1 @@
+-- no sequences are used in MySQL/MariaDB instead use AUTO_INCREMENT for every ID
diff --git a/order-service/core/src/main/resources/db/type/mysql/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/mysql/V0002__Create_RevInfo.sql
new file mode 100644
index 0000000..a9590a2
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/mysql/V0002__Create_RevInfo.sql
@@ -0,0 +1,6 @@
+-- *** RevInfo (Commit log for envers audit trail) ***
+CREATE TABLE REVINFO(
+ id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ timestamp BIGINT NOT NULL,
+ userLogin VARCHAR(255)
+);
diff --git a/order-service/core/src/main/resources/db/type/mysql/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/mysql/V0003__Create_BinaryObject.sql
new file mode 100644
index 0000000..ff7ca5f
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/mysql/V0003__Create_BinaryObject.sql
@@ -0,0 +1,8 @@
+-- *** BinaryObject (BLOBs) ***
+CREATE TABLE BINARYOBJECT (
+ id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ modificationCounter INT NOT NULL,
+ content LONGBLOB,
+ filesize BIGINT NOT NULL,
+ mimeType VARCHAR(255)
+);
diff --git a/order-service/core/src/main/resources/db/type/oracle/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/oracle/V0001__Create_Sequence.sql
new file mode 100644
index 0000000..2210438
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/oracle/V0001__Create_Sequence.sql
@@ -0,0 +1,2 @@
+-- Leave a large ID space reserved for master-data and test-data
+CREATE SEQUENCE HIBERNATE_SEQUENCE START WITH 1000000;
diff --git a/order-service/core/src/main/resources/db/type/oracle/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/oracle/V0002__Create_RevInfo.sql
new file mode 100644
index 0000000..a459bf2
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/oracle/V0002__Create_RevInfo.sql
@@ -0,0 +1,7 @@
+-- *** RevInfo (Commit log for envers audit trail) ***
+CREATE TABLE RevInfo (
+ id NUMBER(19),
+ "timestamp" NUMBER(19,0),
+ userLogin VARCHAR2(255 CHAR),
+ CONSTRAINT PK_RevInfo_id PRIMARY KEY (id)
+);
\ No newline at end of file
diff --git a/order-service/core/src/main/resources/db/type/oracle/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/oracle/V0003__Create_BinaryObject.sql
new file mode 100644
index 0000000..e5635f1
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/oracle/V0003__Create_BinaryObject.sql
@@ -0,0 +1,9 @@
+-- *** BinaryObject (BLOBs) ***
+CREATE TABLE BinaryObject (
+ id NUMBER(19),
+ modificationCounter NUMBER(10, 0) NOT NULL,
+ content BLOB,
+ filesize NUMBER(10, 0) NOT NULL,
+ mimeType VARCHAR(255),
+ CONSTRAINT PK_BinaryObject_id PRIMARY KEY (ID)
+);
diff --git a/order-service/core/src/main/resources/db/type/postgresql/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/postgresql/V0001__Create_Sequence.sql
new file mode 100644
index 0000000..2210438
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/postgresql/V0001__Create_Sequence.sql
@@ -0,0 +1,2 @@
+-- Leave a large ID space reserved for master-data and test-data
+CREATE SEQUENCE HIBERNATE_SEQUENCE START WITH 1000000;
diff --git a/order-service/core/src/main/resources/db/type/postgresql/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/postgresql/V0002__Create_RevInfo.sql
new file mode 100644
index 0000000..b6c8663
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/postgresql/V0002__Create_RevInfo.sql
@@ -0,0 +1,6 @@
+-- *** RevInfo (Commit log for envers audit trail) ***
+CREATE TABLE REVINFO(
+ id BIGINT NOT NULL,
+ timestamp BIGINT NOT NULL,
+ userLogin VARCHAR(255)
+);
diff --git a/order-service/core/src/main/resources/db/type/postgresql/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/postgresql/V0003__Create_BinaryObject.sql
new file mode 100644
index 0000000..54bb161
--- /dev/null
+++ b/order-service/core/src/main/resources/db/type/postgresql/V0003__Create_BinaryObject.sql
@@ -0,0 +1,9 @@
+-- *** BinaryObject (BLOBs) ***
+CREATE TABLE BINARYOBJECT (
+ id BIGSERIAL NOT NULL,
+ modificationCounter INTEGER NOT NULL,
+ content BYTEA,
+ filesize BIGINT NOT NULL,
+ mimeType VARCHAR(255),
+ PRIMARY KEY (ID)
+);
diff --git a/order-service/core/src/main/resources/static/index.html b/order-service/core/src/main/resources/static/index.html
new file mode 100644
index 0000000..88b7063
--- /dev/null
+++ b/order-service/core/src/main/resources/static/index.html
@@ -0,0 +1,18 @@
+
+
+
+Welcome
+
+
+ Welcome
+ This is a test!
+
+ Services Overview (CXF)
+
+
+
+
+
+
\ No newline at end of file
diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/Devon4jPackageCheckTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/Devon4jPackageCheckTest.java
new file mode 100644
index 0000000..a2d2e27
--- /dev/null
+++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/Devon4jPackageCheckTest.java
@@ -0,0 +1,66 @@
+package com.devonfw.app.java.order.general.common.base;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import net.sf.mmm.util.reflect.api.ReflectionUtil;
+import net.sf.mmm.util.reflect.base.ReflectionUtilImpl;
+
+import org.assertj.core.api.SoftAssertions;
+import org.junit.Test;
+
+import com.devonfw.module.basic.common.api.reflect.Devon4jPackage;
+import com.devonfw.module.test.common.base.ModuleTest;
+
+/**
+ * This test verifies that the entire code of your code-base is located in {@link Devon4jPackage#isValid() valid Devon4j
+ * packages}.
+ */
+public class Devon4jPackageCheckTest extends ModuleTest {
+
+ /**
+ * Scans all the packages of this application root pacakge namespace. Will verify that these are
+ * {@link Devon4jPackage#isValid() valid Devon4j packages}.
+ */
+ @Test
+ public void testPackages() {
+
+ Devon4jPackage pkg = Devon4jPackage.of(Devon4jPackageCheckTest.class);
+ assertThat(pkg.isValid()).isTrue();
+
+ ReflectionUtil ru = ReflectionUtilImpl.getInstance();
+ Set classNames = ru.findClassNames(getRootPackage2Scan(pkg), true);
+ String appPackage = pkg.getRoot() + "." + pkg.getApplication();
+ Set packages = new HashSet<>(128);
+ packages.add(appPackage); // allow SpringBootApp, etc. in application package
+ SoftAssertions assertion = new SoftAssertions();
+ for (String className : classNames) {
+ int lastDot = className.lastIndexOf('.');
+ if (lastDot > 0) {
+ String packageName = className.substring(0, lastDot);
+ boolean added = packages.add(packageName);
+ if (added) {
+ pkg = Devon4jPackage.of(packageName);
+ if (!pkg.isValid()) {
+ assertion.assertThat(pkg.isValid())
+ .as("package " + packageName + " is invalid (component: " + pkg.getComponent() + ", layer: "
+ + pkg.getLayer() + ", scope: " + pkg.getScope() + "). Hint contains e.g. "
+ + className.substring(lastDot + 1))
+ .isTrue();
+ }
+ }
+ }
+ }
+ assertion.assertAll();
+ }
+
+ /**
+ * @param pkg the {@link Devon4jPackage} of this test.
+ * @return the root package to scan for {@link Class}es to get the actual packages to check.
+ */
+ protected String getRootPackage2Scan(Devon4jPackage pkg) {
+
+ return pkg.getRoot() + "." + pkg.getApplication();
+ }
+
+}
diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/PermissionCheckTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/PermissionCheckTest.java
new file mode 100644
index 0000000..38b62ae
--- /dev/null
+++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/PermissionCheckTest.java
@@ -0,0 +1,67 @@
+package com.devonfw.app.java.order.general.common.base;
+
+import com.devonfw.module.test.common.base.ModuleTest;
+
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import javax.annotation.security.DenyAll;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+
+import net.sf.mmm.util.filter.api.Filter;
+import net.sf.mmm.util.reflect.api.ReflectionUtil;
+import net.sf.mmm.util.reflect.base.ReflectionUtilImpl;
+
+import org.assertj.core.api.SoftAssertions;
+import org.junit.Test;
+
+/**
+ * Tests the permission check in logic layer.
+ */
+public class PermissionCheckTest extends ModuleTest {
+
+ /**
+ * Check if all relevant methods in use case implementations have permission checks i.e. {@link RolesAllowed},
+ * {@link DenyAll} or {@link PermitAll} annotation is applied. This is only checked for methods that are declared in
+ * the corresponding interface and thus have the {@link Override} annotations applied.
+ */
+ @Test
+ public void permissionCheckAnnotationPresent() {
+
+ String packageName = "com.devonfw.app.java.order";
+ Filter filter = new Filter() {
+
+ @Override
+ public boolean accept(String value) {
+
+ return value.contains(".logic.impl.usecase.Uc") && value.endsWith("Impl");
+ }
+
+ };
+ ReflectionUtil ru = ReflectionUtilImpl.getInstance();
+ Set classNames = ru.findClassNames(packageName, true, filter);
+ Set> classes = ru.loadClasses(classNames);
+ SoftAssertions assertions = new SoftAssertions();
+ for (Class> clazz : classes) {
+ Method[] methods = clazz.getDeclaredMethods();
+ for (Method method : methods) {
+ Method parentMethod = ru.getParentMethod(method);
+ if (parentMethod != null) {
+ Class> declaringClass = parentMethod.getDeclaringClass();
+ if (declaringClass.isInterface() && declaringClass.getSimpleName().startsWith("Uc")) {
+ boolean hasAnnotation = false;
+ if (method.getAnnotation(RolesAllowed.class) != null || method.getAnnotation(DenyAll.class) != null
+ || method.getAnnotation(PermitAll.class) != null) {
+ hasAnnotation = true;
+ }
+ assertions.assertThat(hasAnnotation)
+ .as("Method " + method.getName() + " in Class " + clazz.getSimpleName() + " is missing access control")
+ .isTrue();
+ }
+ }
+ }
+ }
+ assertions.assertAll();
+ }
+}
diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/AccessControlSchemaXmlValidationTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/AccessControlSchemaXmlValidationTest.java
new file mode 100644
index 0000000..fdaa9c5
--- /dev/null
+++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/AccessControlSchemaXmlValidationTest.java
@@ -0,0 +1,57 @@
+package com.devonfw.app.java.order.general.common.base.test;
+
+import com.devonfw.module.test.common.base.ModuleTest;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+/**
+ * Class for XML Validation Tests.
+ *
+ */
+public class AccessControlSchemaXmlValidationTest extends ModuleTest {
+
+ /**
+ * Tests if the access-control-schema.xml is valid.
+ *
+ * @throws ParserConfigurationException If a DocumentBuilder cannot be created which satisfies the configuration
+ * requested.
+ * @throws IOException If any IO errors occur.
+ * @throws SAXException If an error occurs during the validation.
+ */
+ @Test
+ public void validateAccessControllSchema() throws ParserConfigurationException, SAXException, IOException {
+
+ // parse an XML document into a DOM tree
+ DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ String xmlPath = getClass().getResource("/config/app/security/access-control-schema.xml").getPath();
+ Document document = parser.parse(new File(xmlPath));
+
+ // create a SchemaFactory capable of understanding WXS schemas
+ SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+
+ // load a WXS schema, represented by a Schema instance
+ URL schemaPath = getClass().getResource("/com/devonfw/module/security/access-control-schema.xsd");
+ Schema schema = factory.newSchema(schemaPath);
+
+ // create a Validator instance, which can be used to validate an instance document
+ Validator validator = schema.newValidator();
+
+ // validate the DOM tree
+ validator.validate(new DOMSource(document));
+ }
+}
diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationComponentTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationComponentTest.java
new file mode 100644
index 0000000..1814c47
--- /dev/null
+++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationComponentTest.java
@@ -0,0 +1,22 @@
+package com.devonfw.app.java.order.general.common.base.test;
+
+import com.devonfw.module.test.common.base.ComponentTest;
+
+import com.devonfw.app.java.order.SpringBootApp;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+
+/**
+ * Abstract base class for {@link ComponentTest}s of this application.
+ */
+@SpringBootTest(classes = { SpringBootApp.class }, webEnvironment = WebEnvironment.NONE)
+public abstract class ApplicationComponentTest extends ComponentTest {
+
+ @Override
+ protected void doTearDown() {
+ super.doTearDown();
+ TestUtil.logout();
+ }
+
+}
diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationSubsystemTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationSubsystemTest.java
new file mode 100644
index 0000000..b927f87
--- /dev/null
+++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationSubsystemTest.java
@@ -0,0 +1,16 @@
+package com.devonfw.app.java.order.general.common.base.test;
+
+import com.devonfw.module.test.common.base.SubsystemTest;
+
+import com.devonfw.app.java.order.SpringBootApp;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+
+/**
+ * Abstract base class for {@link SubsystemTest}s of this application.
+ */
+@SpringBootTest(classes = { SpringBootApp.class }, webEnvironment = WebEnvironment.RANDOM_PORT)
+public abstract class ApplicationSubsystemTest extends SubsystemTest {
+
+}
diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/DbTestHelper.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/DbTestHelper.java
new file mode 100644
index 0000000..270b50e
--- /dev/null
+++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/DbTestHelper.java
@@ -0,0 +1,60 @@
+package com.devonfw.app.java.order.general.common.base.test;
+
+import javax.inject.Named;
+
+import org.flywaydb.core.Flyway;
+import org.flywaydb.core.api.MigrationVersion;
+
+/**
+ * This class provides methods for handling the database during testing where resets (and other operations) may be
+ * necessary.
+ */
+@Named
+public class DbTestHelper {
+
+ private Flyway flyway;
+
+ private MigrationVersion migrationVersion;
+
+ /**
+ * The constructor.
+ *
+ * @param flyway an instance of type {@link Flyway}.
+ */
+ public DbTestHelper(Flyway flyway) {
+ super();
+ this.flyway = flyway;
+ }
+
+ /**
+ * Drops the whole database.
+ */
+ public void dropDatabase() {
+
+ this.flyway.clean();
+ }
+
+ /**
+ * Calls {@link #dropDatabase()} internally, and migrates to the highest available migration (default) or to the
+ * {@code migrationVersion} specified by {@link #setMigrationVersion(String)}.
+ */
+ public void resetDatabase() {
+
+ dropDatabase();
+ if (this.migrationVersion != null) {
+ this.flyway.setTarget(this.migrationVersion);
+ }
+ this.flyway.migrate();
+ }
+
+ /**
+ * This method sets the internal value of the {@code migrationVersion}.
+ *
+ * @param migrationVersion new {@code String} value of {@code migrationVersion}. Must not be null
+ */
+ public void setMigrationVersion(String migrationVersion) {
+
+ this.migrationVersion = MigrationVersion.fromVersion(migrationVersion);
+ }
+
+}
diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java
new file mode 100644
index 0000000..4e0419d
--- /dev/null
+++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java
@@ -0,0 +1,29 @@
+package com.devonfw.app.java.order.general.common.base.test;
+
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ * This is a utility for testing. It allows to simulate authentication for component testing.
+ */
+public class TestUtil {
+
+ /**
+ * @param login the id of the user to run the test as.
+ * @param permissions the permissions for the test.
+ */
+ public static void login(String login, String... permissions) {
+
+ Authentication authentication = new TestingAuthenticationToken(login, login, permissions);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+
+ /**
+ * Logs off any previously logged on user.
+ */
+ public static void logout() {
+
+ SecurityContextHolder.getContext().setAuthentication(null);
+ }
+}
diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/impl/config/TestWebSecurityConfig.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/impl/config/TestWebSecurityConfig.java
new file mode 100644
index 0000000..23871ad
--- /dev/null
+++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/impl/config/TestWebSecurityConfig.java
@@ -0,0 +1,54 @@
+package com.devonfw.app.java.order.general.common.impl.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
+import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+
+import com.devonfw.app.java.order.general.service.impl.config.BaseWebSecurityConfig;
+import com.devonfw.module.basic.common.api.config.SpringProfileConstants;
+
+/**
+ * This type provides web security configuration for testing purposes.
+ */
+@Configuration
+@EnableWebSecurity
+@Profile(SpringProfileConstants.JUNIT)
+public class TestWebSecurityConfig extends BaseWebSecurityConfig {
+ private static Logger LOG = LoggerFactory.getLogger(TestWebSecurityConfig.class);
+
+ /**
+ * Configure spring security to enable a simple webform-login + a simple rest login.
+ */
+ @Override
+ public void configure(HttpSecurity http) throws Exception {
+
+ super.configure(http);
+ http.addFilterBefore(basicAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
+
+ // Disable CSRF protection in tests for simpler testing of REST services
+ http.csrf().disable();
+ LOG.debug("*** CSRF disabled - this config should only be used in development environment ***");
+ }
+
+ /**
+ * @return {@link BasicAuthenticationFilter}.
+ * @throws Exception on initialization error.
+ */
+ @Bean
+ protected BasicAuthenticationFilter basicAuthenticationFilter() throws Exception {
+
+ AuthenticationEntryPoint authenticationEntryPoint = new BasicAuthenticationEntryPoint();
+ BasicAuthenticationFilter basicAuthenticationFilter =
+ new BasicAuthenticationFilter(authenticationManagerBean(), authenticationEntryPoint);
+ return basicAuthenticationFilter;
+ }
+
+}
diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/base/test/RestServiceTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/base/test/RestServiceTest.java
new file mode 100644
index 0000000..367f495
--- /dev/null
+++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/base/test/RestServiceTest.java
@@ -0,0 +1,66 @@
+package com.devonfw.app.java.order.general.service.base.test;
+
+import javax.inject.Inject;
+
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+
+import com.devonfw.app.java.order.SpringBootApp;
+import com.devonfw.app.java.order.general.common.base.test.DbTestHelper;
+import com.devonfw.app.java.order.general.common.base.test.TestUtil;
+
+import com.devonfw.module.test.common.base.SubsystemTest;
+import com.devonfw.module.service.common.api.client.ServiceClientFactory;
+
+/**
+ * Abstract base class for {@link SubsystemTest}s which runs the tests within a local server.
+ *
+ * The local server's port is randomly assigned.
+ */
+@SpringBootTest(classes = { SpringBootApp.class }, webEnvironment = WebEnvironment.RANDOM_PORT)
+public abstract class RestServiceTest extends SubsystemTest {
+
+ /**
+ * The port of the web server during the test.
+ */
+ @LocalServerPort
+ protected int port;
+
+ @Inject
+ private ServiceClientFactory serviceClientFactory;
+
+ @Inject
+ private DbTestHelper dbTestHelper;
+
+ @Override
+ protected void doSetUp() {
+
+ super.doSetUp();
+ }
+
+ @Override
+ protected void doTearDown() {
+
+ super.doTearDown();
+ TestUtil.logout();
+ }
+
+ /**
+ * @return the {@link DbTestHelper}
+ */
+ protected DbTestHelper getDbTestHelper() {
+
+ return this.dbTestHelper;
+ }
+
+
+ /**
+ * @return the {@link ServiceClientFactory}
+ */
+ protected ServiceClientFactory getServiceClientFactory() {
+
+ return this.serviceClientFactory;
+ }
+
+}
diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImplTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImplTest.java
new file mode 100644
index 0000000..06cf4ba
--- /dev/null
+++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImplTest.java
@@ -0,0 +1,96 @@
+package com.devonfw.app.java.order.general.service.impl.rest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.web.csrf.CsrfToken;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestTemplate;
+
+import com.devonfw.module.service.common.api.client.config.ServiceClientConfigBuilder;
+
+import com.devonfw.app.java.order.general.common.api.to.UserProfileTo;
+import com.devonfw.app.java.order.general.service.api.rest.SecurityRestService;
+import com.devonfw.app.java.order.general.service.base.test.RestServiceTest;
+
+/**
+ * This class tests the login functionality of {@link SecurityRestServiceImpl}.
+ */
+@RunWith(SpringRunner.class)
+public class SecurityRestServiceImplTest extends RestServiceTest {
+
+ /** Logger instance. */
+ private static final Logger LOG = LoggerFactory.getLogger(SecurityRestServiceImplTest.class);
+
+ /**
+ * Test the login functionality as it will be used from a JavaScript client.
+ */
+ @Test
+ public void testLogin() {
+
+ String login = "waiter";
+ String password = "waiter";
+
+ ResponseEntity postResponse = login(login, password);
+ LOG.debug("Body: " + postResponse.getBody());
+ assertThat(postResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(postResponse.getHeaders().containsKey(HttpHeaders.SET_COOKIE)).isTrue();
+ }
+
+ /**
+ * Test of {@code SecurityRestService.getCsrfToken()}.
+ */
+ @Test
+ public void testGetCsrfToken() {
+
+ String login = "waiter";
+ String password = "waiter";
+ SecurityRestService securityService = getServiceClientFactory().create(SecurityRestService.class,
+ new ServiceClientConfigBuilder().host("localhost").authBasic().userLogin(login).userPassword(password)
+ .buildMap());
+ CsrfToken csrfToken = securityService.getCsrfToken(null, null);
+ assertThat(csrfToken.getHeaderName()).isEqualTo("X-CSRF-TOKEN");
+ assertThat(csrfToken.getParameterName()).isEqualTo("_csrf");
+ assertThat(csrfToken.getToken()).isNotNull();
+ LOG.debug("Csrf Token: {}", csrfToken.getToken());
+ }
+
+ /**
+ * Test of {@link SecurityRestService#getCurrentUser()}.
+ */
+ @Test
+ public void testGetCurrentUser() {
+ String login = "waiter";
+ String password = "waiter";
+ SecurityRestService securityService = getServiceClientFactory().create(SecurityRestService.class,
+ new ServiceClientConfigBuilder().host("localhost").authBasic().userLogin(login).userPassword(password)
+ .buildMap());
+ UserProfileTo userProfile = securityService.getCurrentUser();
+ assertThat(userProfile.getLogin()).isEqualTo(login);
+ }
+
+ /**
+ * Performs the login as required by a JavaScript client.
+ *
+ * @param userName the username of the user
+ * @param tmpPassword the password of the user
+ * @return @ {@link ResponseEntity} containing containing a cookie in its header.
+ */
+ private ResponseEntity login(String userName, String tmpPassword) {
+
+ String tmpUrl = "http://localhost:" + String.valueOf(this.port) + "/services/rest/login";
+
+ HttpEntity postRequest = new HttpEntity<>(
+ "{\"j_username\": \"" + userName + "\", \"j_password\": \"" + tmpPassword + "\"}", new HttpHeaders());
+
+ ResponseEntity postResponse = new RestTemplate().exchange(tmpUrl, HttpMethod.POST, postRequest, String.class);
+ return postResponse;
+ }
+
+}
diff --git a/order-service/core/src/test/resources/config/application.properties b/order-service/core/src/test/resources/config/application.properties
new file mode 100644
index 0000000..47b72ef
--- /dev/null
+++ b/order-service/core/src/test/resources/config/application.properties
@@ -0,0 +1,13 @@
+# This is the spring boot configuration file for JUnit tests. It will only affect JUnits and is not included into the application.
+spring.profiles.active=junit
+
+# Database and JPA
+spring.jpa.database=h2
+spring.datasource.url=jdbc:h2:mem:app;
+spring.datasource.password=
+spring.datasource.username=sa
+spring.jpa.hibernate.ddl-auto=none
+
+# Flyway for Database Setup and Migrations
+spring.flyway.enabled=true
+spring.flyway.locations=classpath:db/migration,classpath:db/type/h2
diff --git a/order-service/pom.xml b/order-service/pom.xml
new file mode 100644
index 0000000..c484507
--- /dev/null
+++ b/order-service/pom.xml
@@ -0,0 +1,475 @@
+
+
+ 4.0.0
+ order-service
+ com.devonfw.app.java
+ 0.0.1-SNAPSHOT
+ pom
+ ${project.artifactId}
+ Application based on the Open Application Standard Platform for Java (devon4j).
+
+
+ 2.0.4.RELEASE
+ 3.0.1
+ 1.8
+ UTF-8
+ UTF-8
+ com.devonfw.module.test.common.api.category.CategorySystemTest
+
+
+
+ api
+ core
+ server
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring.boot.version}
+ pom
+ import
+
+
+
+ com.devonfw.java.boms
+ devon4j-bom
+ ${devon4j.version}
+
+ pom
+ import
+
+
+
+
+
+
+ junit
+ junit
+ test
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${project.build.sourceEncoding}
+ ${java.version}
+ ${java.version}
+
+
+
+
+ maven-surefire-plugin
+
+ ${devonfw.test.excluded.groups}
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ default-prepare-agent
+
+ prepare-agent
+
+
+
+ default-report
+
+ report
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 3.0.2
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.7.0
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+ 2.5.2
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+
+ org.apache.maven.plugins
+ maven-clean-plugin
+ 3.0.0
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.0.2
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.0.1
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+ 3.7
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ 3.0.0
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+ 1.8
+
+
+ org.apache.maven.plugins
+ maven-project-info-reports-plugin
+ 2.9
+
+
+ org.apache.maven.plugins
+ maven-jxr-plugin
+ 2.5
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.0.0
+
+
+
+ javax.interceptor
+ javax.interceptor-api
+ 1.2
+
+
+
+
+ private
+ ${project.reporting.outputEncoding}
+ ${project.build.sourceEncoding}
+ true
+
+ http://docs.oracle.com/javase/8/docs/api/
+
+ JavaDocs for ${project.name}
+ JavaDocs for ${project.name}
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.20.1
+
+
+ ${basedir}
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-report-plugin
+ 2.20.1
+
+
+ org.apache.maven.plugins
+ maven-pmd-plugin
+ 3.9.0
+
+ ${java.version}
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 3.2.0
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.6
+
+
+ org.apache.maven.plugins
+ maven-help-plugin
+ 2.2
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ 2.7
+
+
+ org.codehaus.mojo
+ sonar-maven-plugin
+ 3.4.0.905
+
+
+ org.codehaus.mojo
+ findbugs-maven-plugin
+ 3.0.5
+
+
+ org.codehaus.mojo
+ taglist-maven-plugin
+ 2.4
+
+
+ org.codehaus.mojo
+ flatten-maven-plugin
+ 1.0.1
+
+
+ org.codehaus.mojo
+ servicedocgen-maven-plugin
+ 1.0.0-beta-3
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.0
+
+
+ org.owasp
+ dependency-check-maven
+ 3.1.1
+
+
+ org.codehaus.mojo
+ license-maven-plugin
+ 1.14
+
+ ${project.build.directory}/generated-resources
+ true
+ true
+ true
+ true
+
+ Apache Software License, Version 2.0|The Apache Software License, Version 2.0|Apache
+ 2.0|Apache License, Version 2.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.0.4.RELEASE
+
+
+
+
+
+
+
+ moduletest
+
+ com.devonfw.module.test.common.api.category.CategoryComponentTest,com.devonfw.module.test.common.api.category.CategorySubsystemTest,com.devonfw.module.test.common.api.category.CategorySystemTest
+
+
+
+ componenttest
+
+ com.devonfw.module.test.common.api.category.CategorySubsystemTest,com.devonfw.module.test.common.api.category.CategorySystemTest
+
+
+
+ subsystemtest
+
+ com.devonfw.module.test.common.api.category.CategorySystemTest
+
+
+
+ systemtest
+
+
+
+
+
+ doclint-disabled
+
+ [1.8,)
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+ -Xdoclint:none
+
+
+
+
+
+
+
+
+ security
+
+
+
+ org.owasp
+ dependency-check-maven
+
+ 8
+
+
+
+
+ check
+
+
+
+
+
+
+
+
+ licenses
+
+
+
+ org.codehaus.mojo
+ license-maven-plugin
+
+
+ aggregate-add-third-party
+ generate-resources
+
+ aggregate-add-third-party
+
+
+
+
+ aggregate-download-licenses
+ generate-resources
+
+ aggregate-download-licenses
+
+
+
+
+
+
+
+
+
+ eclipse
+
+
+ eclipse.application
+
+
+
+ eclipse-target
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-project-info-reports-plugin
+
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-jxr-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ org.codehaus.mojo
+ taglist-maven-plugin
+
+
+ TODO
+ @todo
+ FIXME
+ @deprecated
+ REVIEW
+
+
+
+
+ org.owasp
+ dependency-check-maven
+
+
+
+ aggregate
+ check
+
+
+
+
+
+ org.codehaus.mojo
+ servicedocgen-maven-plugin
+
+
+
+ ${servicedoc.info.title}
+ ${servicedoc.info.description}
+
+ ${servicedoc.host}
+ ${servicedoc.port}
+ ${servicedoc.basePath}
+
+ http
+
+
+
+
+
+ org.codehaus.mojo
+ license-maven-plugin
+
+
+
+ third-party-report
+ aggregate-third-party-report
+
+
+
+
+
+
+
+
diff --git a/order-service/server/pom.xml b/order-service/server/pom.xml
new file mode 100644
index 0000000..324ff87
--- /dev/null
+++ b/order-service/server/pom.xml
@@ -0,0 +1,143 @@
+
+
+ 4.0.0
+
+ com.devonfw.app.java
+ order-service
+ 0.0.1-SNAPSHOT
+
+ order-service-server
+ war
+ ${project.artifactId}
+ Server for the order-service application - a simple example using the Open Application Standard Platform for Java (devon4j).
+
+
+ 1.8
+
+
+
+
+ ${project.groupId}
+ order-service-core
+ ${project.version}
+
+
+
+
+
+ jsclient
+
+
+ false
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+
+ npm-install
+ generate-sources
+
+ exec
+
+
+ npm
+
+ install
+
+ ${js.client.dir}
+
+
+
+ gulp-clean
+ generate-sources
+
+ exec
+
+
+ gulp
+
+ clean
+
+ ${js.client.dir}
+
+
+
+ gulp-build
+ generate-sources
+
+ exec
+
+
+ gulp
+
+ build:dist
+
+ ${js.client.dir}
+
+
+
+ gulp-test
+ test
+
+ exec
+
+
+ gulp
+
+ test
+
+ ${js.client.dir}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+ WEB-INF/classes/config/application.properties
+ ${project.artifactId}
+ false
+
+
+
+
+
+
+
+
+
+
+ ${project.basedir}/src/main/resources
+
+
+ ${js.client.dir}/dist
+ static
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ com.devonfw.app.java.order.SpringBootApp
+ bootified
+ ${project.artifactId}
+
+
+
+
+ repackage
+
+
+
+
+
+
+
+
+
diff --git a/order-service/server/src/main/resources/logback.xml b/order-service/server/src/main/resources/logback.xml
new file mode 100644
index 0000000..77032ea
--- /dev/null
+++ b/order-service/server/src/main/resources/logback.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+ false
+ correlationId
+
+ timestamp
+ [ignore]
+ [ignore]
+
+ {"appname":"order-service"}
+ UTC
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/order-service/src/main/java/com/devonfw/app/java/order/SpringBootBatchApp.java b/order-service/src/main/java/com/devonfw/app/java/order/SpringBootBatchApp.java
new file mode 100644
index 0000000..3d35838
--- /dev/null
+++ b/order-service/src/main/java/com/devonfw/app/java/order/SpringBootBatchApp.java
@@ -0,0 +1,31 @@
+package com.devonfw.app.java.order;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+
+import com.devonfw.module.jpa.dataaccess.api.AdvancedRevisionEntity;
+
+/**
+ * {@link SpringBootApplication} for running this batch.
+ */
+@SpringBootApplication(exclude = { EndpointAutoConfiguration.class, SecurityAutoConfiguration.class,
+SecurityFilterAutoConfiguration.class, })
+@EntityScan(basePackages = { "com.devonfw.app.java.order" }, basePackageClasses = { AdvancedRevisionEntity.class })
+@EnableGlobalMethodSecurity(jsr250Enabled = false)
+public class SpringBootBatchApp {
+
+ /**
+ * Entry point for spring-boot based app
+ *
+ * @param args - arguments
+ */
+ public static void main(String[] args) {
+
+ SpringApplication.run(SpringBootBatchApp.class, args);
+ }
+}
diff --git a/order-service/src/main/java/com/devonfw/app/java/order/general/batch/impl/config/BeansBatchConfig.java b/order-service/src/main/java/com/devonfw/app/java/order/general/batch/impl/config/BeansBatchConfig.java
new file mode 100644
index 0000000..385822e
--- /dev/null
+++ b/order-service/src/main/java/com/devonfw/app/java/order/general/batch/impl/config/BeansBatchConfig.java
@@ -0,0 +1,193 @@
+package com.devonfw.app.java.order.general.batch.impl.config;
+
+import javax.inject.Inject;
+import javax.sql.DataSource;
+
+import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
+import org.springframework.batch.core.configuration.support.MapJobRegistry;
+import org.springframework.batch.core.explore.support.JobExplorerFactoryBean;
+import org.springframework.batch.core.launch.support.RunIdIncrementer;
+import org.springframework.batch.core.launch.support.SimpleJobOperator;
+import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.transaction.PlatformTransactionManager;
+
+/**
+ * This class contains the configuration like jobLauncher,Jobrepository etc.
+ */
+import com.devonfw.module.batch.common.impl.JobLauncherWithAdditionalRestartCapabilities;
+
+/**
+ * This class contains configuration of batch beans.
+ */
+@Configuration
+public class BeansBatchConfig {
+
+ private JobRepositoryFactoryBean jobRepository;
+
+ private MapJobRegistry jobRegistry;
+
+ private JobLauncherWithAdditionalRestartCapabilities jobLauncher;
+
+ private JobExplorerFactoryBean jobExplorer;
+
+ private DataSource dataSource;
+
+ private PlatformTransactionManager transactionManager;
+
+ @Value("ISOLATION_DEFAULT")
+ private String isolationLevelForCreate;
+
+ /**
+ * This method is creating joboperator bean
+ *
+ * @return SimpleJobOperator
+ */
+ @Bean
+ @DependsOn({ "jobRepository", "jobExplorer", "jobRegistry", "jobLauncher" })
+ public SimpleJobOperator jobOperator() {
+
+ SimpleJobOperator jobOperator = new SimpleJobOperator();
+ try {
+ jobOperator.setJobExplorer(this.jobExplorer.getObject());
+ } catch (Exception e) {
+ throw new BeanCreationException("Could not create BatchJobOperator", e);
+ }
+
+ jobOperator.setJobLauncher(this.jobLauncher);
+ jobOperator.setJobRegistry(this.jobRegistry);
+
+ try {
+ jobOperator.setJobRepository(this.jobRepository.getObject());
+ } catch (Exception e) {
+ throw new BeanCreationException("Could not create BatchJobOperator", e);
+ }
+
+ return jobOperator;
+ }
+
+ /**
+ * This method is creating jobrepository
+ *
+ * @return JobRepositoryFactoryBean
+ */
+ @Bean(name = "jobRepository")
+ public JobRepositoryFactoryBean jobRepository() {
+
+ this.jobRepository = new JobRepositoryFactoryBean();
+ this.jobRepository.setDataSource(this.dataSource);
+ this.jobRepository.setTransactionManager(this.transactionManager);
+ this.jobRepository.setIsolationLevelForCreate(this.isolationLevelForCreate);
+ return this.jobRepository;
+ }
+
+ /**
+ * This method is creating jobLauncher bean
+ *
+ * @return SimpleJobLauncher
+ */
+ @Bean
+ @DependsOn("jobRepository")
+ public JobLauncherWithAdditionalRestartCapabilities jobLauncher() {
+
+ this.jobLauncher = new JobLauncherWithAdditionalRestartCapabilities();
+
+ try {
+ this.jobLauncher.setJobRepository(this.jobRepository.getObject());
+ } catch (Exception e) {
+ throw new BeanCreationException("Could not create BatchJobOperator", e);
+ }
+
+ return this.jobLauncher;
+ }
+
+ /**
+ * This method is creating jobExplorer bean
+ *
+ * @return JobExplorerFactoryBean
+ */
+ @Bean
+ @DependsOn("dataSource")
+ public JobExplorerFactoryBean jobExplorer() {
+
+ this.jobExplorer = new JobExplorerFactoryBean();
+ this.jobExplorer.setDataSource(this.dataSource);
+ return this.jobExplorer;
+ }
+
+ /**
+ * This method is creating jobRegistry bean
+ *
+ * @return MapJobRegistry
+ */
+ @Bean
+ public MapJobRegistry jobRegistry() {
+
+ this.jobRegistry = new MapJobRegistry();
+ return this.jobRegistry;
+ }
+
+ /**
+ * This method is creating JobRegistryBeanPostProcessor
+ *
+ * @return JobRegistryBeanPostProcessor
+ */
+ @Bean
+ @DependsOn("jobRegistry")
+ public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor() {
+
+ JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
+ postProcessor.setJobRegistry(this.jobRegistry);
+ return postProcessor;
+ }
+
+ /**
+ * This method is creating incrementer
+ *
+ * @return RunIdIncrementer
+ */
+ @Bean
+ public RunIdIncrementer incrementer() {
+
+ return new RunIdIncrementer();
+ }
+
+ /**
+ * @return datasource
+ */
+ public DataSource getDataSource() {
+
+ return this.dataSource;
+ }
+
+ /**
+ * @param datasource the datasource to set
+ */
+ @Inject
+ public void setDataSource(DataSource datasource) {
+
+ this.dataSource = datasource;
+ }
+
+ /**
+ * @return transactionManager
+ */
+ public PlatformTransactionManager getTransactionManager() {
+
+ return this.transactionManager;
+ }
+
+ /**
+ * @param transactionManager the transactionManager to set
+ */
+ @Inject
+ public void setTransactionManager(PlatformTransactionManager transactionManager) {
+
+ this.transactionManager = transactionManager;
+ }
+
+}
diff --git a/order-service/src/main/resources/db/migration/h2/V0002__Add_batch_tables.sql b/order-service/src/main/resources/db/migration/h2/V0002__Add_batch_tables.sql
new file mode 100644
index 0000000..980d37d
--- /dev/null
+++ b/order-service/src/main/resources/db/migration/h2/V0002__Add_batch_tables.sql
@@ -0,0 +1,81 @@
+-- Autogenerated: do not edit this file
+
+CREATE TABLE BATCH_JOB_INSTANCE (
+ JOB_INSTANCE_ID BIGINT IDENTITY NOT NULL PRIMARY KEY ,
+ VERSION BIGINT ,
+ JOB_NAME VARCHAR(100) NOT NULL,
+ JOB_KEY VARCHAR(32) NOT NULL,
+ constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
+) ;
+
+CREATE TABLE BATCH_JOB_EXECUTION (
+ JOB_EXECUTION_ID BIGINT IDENTITY NOT NULL PRIMARY KEY ,
+ VERSION BIGINT ,
+ JOB_INSTANCE_ID BIGINT NOT NULL,
+ CREATE_TIME TIMESTAMP NOT NULL,
+ START_TIME TIMESTAMP DEFAULT NULL ,
+ END_TIME TIMESTAMP DEFAULT NULL ,
+ STATUS VARCHAR(10) ,
+ EXIT_CODE VARCHAR(2500) ,
+ EXIT_MESSAGE VARCHAR(2500) ,
+ LAST_UPDATED TIMESTAMP,
+ JOB_CONFIGURATION_LOCATION VARCHAR(2500) NULL,
+ constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
+ references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
+) ;
+
+CREATE TABLE BATCH_JOB_EXECUTION_PARAMS (
+ JOB_EXECUTION_ID BIGINT NOT NULL ,
+ TYPE_CD VARCHAR(6) NOT NULL ,
+ KEY_NAME VARCHAR(100) NOT NULL ,
+ STRING_VAL VARCHAR(250) ,
+ DATE_VAL TIMESTAMP DEFAULT NULL ,
+ LONG_VAL BIGINT ,
+ DOUBLE_VAL DOUBLE PRECISION ,
+ IDENTIFYING CHAR(1) NOT NULL ,
+ constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
+ references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
+) ;
+
+CREATE TABLE BATCH_STEP_EXECUTION (
+ STEP_EXECUTION_ID BIGINT IDENTITY NOT NULL PRIMARY KEY ,
+ VERSION BIGINT NOT NULL,
+ STEP_NAME VARCHAR(100) NOT NULL,
+ JOB_EXECUTION_ID BIGINT NOT NULL,
+ START_TIME TIMESTAMP NOT NULL ,
+ END_TIME TIMESTAMP DEFAULT NULL ,
+ STATUS VARCHAR(10) ,
+ COMMIT_COUNT BIGINT ,
+ READ_COUNT BIGINT ,
+ FILTER_COUNT BIGINT ,
+ WRITE_COUNT BIGINT ,
+ READ_SKIP_COUNT BIGINT ,
+ WRITE_SKIP_COUNT BIGINT ,
+ PROCESS_SKIP_COUNT BIGINT ,
+ ROLLBACK_COUNT BIGINT ,
+ EXIT_CODE VARCHAR(2500) ,
+ EXIT_MESSAGE VARCHAR(2500) ,
+ LAST_UPDATED TIMESTAMP,
+ constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID)
+ references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
+) ;
+
+CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT (
+ STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
+ SHORT_CONTEXT VARCHAR(2500) NOT NULL,
+ SERIALIZED_CONTEXT LONGVARCHAR ,
+ constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
+ references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
+) ;
+
+CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT (
+ JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
+ SHORT_CONTEXT VARCHAR(2500) NOT NULL,
+ SERIALIZED_CONTEXT LONGVARCHAR ,
+ constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
+ references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
+) ;
+
+CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ;
+CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ;
+CREATE SEQUENCE BATCH_JOB_SEQ;
diff --git a/order-service/src/test/java/com/devonfw/app/java/order/general/batch/base/test/SpringBatchIntegrationTest.java b/order-service/src/test/java/com/devonfw/app/java/order/general/batch/base/test/SpringBatchIntegrationTest.java
new file mode 100644
index 0000000..1348412
--- /dev/null
+++ b/order-service/src/test/java/com/devonfw/app/java/order/general/batch/base/test/SpringBatchIntegrationTest.java
@@ -0,0 +1,53 @@
+package com.devonfw.app.java.order.general.batch.base.test;
+
+import javax.inject.Inject;
+
+import org.flywaydb.core.Flyway;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.launch.JobLauncher;
+import org.springframework.batch.test.JobLauncherTestUtils;
+
+import com.devonfw.app.java.order.general.common.base.test.TestUtil;
+import com.devonfw.module.test.common.base.ComponentTest;
+
+/**
+ * Base class for all spring batch integration tests. It helps to do End-to-End job tests.
+ */
+public abstract class SpringBatchIntegrationTest extends ComponentTest {
+
+ @Inject
+ private JobLauncher jobLauncher;
+
+ @Inject
+ private Flyway flyway;
+
+ @Override
+ protected void doSetUp() {
+
+ super.doSetUp();
+ this.flyway.clean();
+ this.flyway.migrate();
+ }
+
+ @Override
+ protected void doTearDown() {
+
+ super.doTearDown();
+ TestUtil.logout();
+ }
+
+ /**
+ * @param job job to configure
+ * @return jobLauncherTestUtils
+ */
+ public JobLauncherTestUtils getJobLauncherTestUtils(Job job) {
+
+ JobLauncherTestUtils jobLauncherTestUtils = new JobLauncherTestUtils();
+ jobLauncherTestUtils.setJob(job);
+ jobLauncherTestUtils.setJobLauncher(this.jobLauncher);
+
+ return jobLauncherTestUtils;
+ }
+}
diff --git a/order-service/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java b/order-service/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java
new file mode 100644
index 0000000..4e0419d
--- /dev/null
+++ b/order-service/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java
@@ -0,0 +1,29 @@
+package com.devonfw.app.java.order.general.common.base.test;
+
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ * This is a utility for testing. It allows to simulate authentication for component testing.
+ */
+public class TestUtil {
+
+ /**
+ * @param login the id of the user to run the test as.
+ * @param permissions the permissions for the test.
+ */
+ public static void login(String login, String... permissions) {
+
+ Authentication authentication = new TestingAuthenticationToken(login, login, permissions);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+
+ /**
+ * Logs off any previously logged on user.
+ */
+ public static void logout() {
+
+ SecurityContextHolder.getContext().setAuthentication(null);
+ }
+}