Compare commits

..

12 Commits

6
.gitignore vendored

@ -1,4 +1,8 @@
/target/
/.classpath
/.project
/.settings
/.settings
/conf/
/conf2/
/bruno/
/backup/

@ -1,9 +0,0 @@
{
"version": "1",
"name": "camel-springboot-xml-example",
"type": "collection",
"ignore": [
"node_modules",
".git"
]
}

@ -1,8 +0,0 @@
auth {
mode: basic
}
auth:basic {
username:
password:
}

@ -1,20 +0,0 @@
meta {
name: findUsers
type: http
seq: 2
}
get {
url: http://localhost:9090/services/api/user
body: none
auth: basic
}
headers {
operationName: findUsers
}
auth:basic {
username: cxfrs
password: password
}

@ -1,28 +0,0 @@
meta {
name: updateUser
type: http
seq: 3
}
post {
url: http://localhost:9090/services/api/user
body: json
auth: basic
}
headers {
Content-Type: application/json
X-MethodName: updateUser
}
auth:basic {
username: cxfrs
password: password
}
body:json {
{
"id": 3,
"name": "Bruce Wayne"
}
}

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5level %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/server.log</file>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/server.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>3</maxHistory>
<totalSizeCap>300MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-5level %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.security.ldap.userdetails" level="TRACE" />
<logger name="org.springframework.security" level="DEBUG" />
<logger name="com.example.camel" level="DEBUG" />
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

@ -1,19 +0,0 @@
server:
port: 9090
camel:
springboot:
main-run-controller: true
spring:
activemq:
broker-url: "tcp://localhost:61616"
ldap:
urls: ldap://localhost:10389
base: dc=example,dc=com
username: uid=admin,ou=system
password: secret
app:
queue-name: "UserServiceQueue"

Binary file not shown.

@ -0,0 +1,105 @@
2025-03-08 12:36:27.115360+08:00 jdbc[3]: exception
org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "MERGE INTO [*]USER (?) VALUES (?, ?)"; expected "identifier"; SQL statement:
MERGE INTO USER (?) VALUES (?, ?) [42001-224]
2025-03-08 12:41:38.334685+08:00 jdbc[3]: exception
org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "CREATE TABLE IF NOT EXISTS public.[*]user (id BIGINT not null, name CHARACTER VARYING not null, PRIMARY KEY (id))"; expected "identifier"; SQL statement:
CREATE TABLE IF NOT EXISTS public.user (id BIGINT not null, name CHARACTER VARYING not null, PRIMARY KEY (id)) [42001-224]
2025-03-08 12:45:24.184967+08:00 jdbc[3]: exception
org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "MERGE INTO public.""user"" ([*]?) VALUES (?, ?)"; expected "identifier"; SQL statement:
MERGE INTO public."user" (?) VALUES (?, ?) [42001-224]
2025-03-08 15:26:55.471296+08:00 database: flush
org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: C:/Users/Yan/eclipse-workspace/camel-springboot-activemq6-example/db/appdb.mv.db [2.2.224/7]" [50000-224]
at org.h2.message.DbException.get(DbException.java:212)
at org.h2.message.DbException.convert(DbException.java:407)
at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122)
at org.h2.mvstore.MVStore.handleException(MVStore.java:1546)
at org.h2.mvstore.MVStore.panic(MVStore.java:371)
at org.h2.mvstore.MVStore.<init>(MVStore.java:291)
at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2035)
at org.h2.mvstore.db.Store.<init>(Store.java:133)
at org.h2.engine.Database.<init>(Database.java:326)
at org.h2.engine.Engine.openSession(Engine.java:92)
at org.h2.engine.Engine.openSession(Engine.java:222)
at org.h2.engine.Engine.createSession(Engine.java:201)
at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:343)
at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:125)
at org.h2.Driver.connect(Driver.java:59)
at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137)
at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360)
at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202)
at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461)
at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:550)
at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:98)
at com.zaxxer.hikari.HikariDataSource.<init>(HikariDataSource.java:80)
at com.example.sbcamel.DatabaseConfig.dataSource(DatabaseConfig.java:69)
at com.example.sbcamel.DatabaseConfig$$SpringCGLIB$$0.CGLIB$dataSource$0(<generated>)
at com.example.sbcamel.DatabaseConfig$$SpringCGLIB$$FastClass$$1.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:372)
at com.example.sbcamel.DatabaseConfig$$SpringCGLIB$$0.dataSource(<generated>)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:146)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1448)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1358)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:542)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:313)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1448)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1358)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:785)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:768)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:509)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1439)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:971)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352)
at com.example.sbcamel.Boot.main(Boot.java:27)
Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: C:/Users/Yan/eclipse-workspace/camel-springboot-activemq6-example/db/appdb.mv.db [2.2.224/7]" [50000-224]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:566)
at org.h2.message.DbException.getJdbcSQLException(DbException.java:489)
... 83 more
Caused by: org.h2.mvstore.MVStoreException: The file is locked: C:/Users/Yan/eclipse-workspace/camel-springboot-activemq6-example/db/appdb.mv.db [2.2.224/7]
at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996)
at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143)
at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117)
at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81)
at org.h2.mvstore.MVStore.<init>(MVStore.java:286)
... 77 more

@ -4,19 +4,22 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>camel-springboot-activemq6-example</artifactId>
<version>2.0.1</version>
<version>3.3.0</version>
<name>camel-springboot-activemq6-example</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.8</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<camel.version>4.8.3</camel.version>
<springboot.version>${project.parent.version}</springboot.version>
<camel.version>4.8.4</camel.version>
<spring-boot-dependencies.version>3.4.4</spring-boot-dependencies.version>
<spring-cloud-dependencies.version>2024.0.1</spring-cloud-dependencies.version>
<mybatis.version>3.5.19</mybatis.version>
<mybatis-spring.version>3.0.4</mybatis-spring.version>
<guava.version>33.1.0-jre</guava.version>
<maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
<jsoup.version>1.19.1</jsoup.version>
<java.mail.version>1.6.2</java.mail.version>
</properties>
<dependencyManagement>
@ -31,7 +34,14 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<version>${spring-boot-dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud-dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -56,6 +66,14 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<!-- Camel -->
<dependency>
<groupId>org.apache.camel.springboot</groupId>
@ -81,16 +99,74 @@
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jackson-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-mybatis-starter</artifactId>
</dependency>
<!-- Other -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>org.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jakarta.rs</groupId>
<artifactId>jackson-jakarta-rs-json-provider</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>javax.mail-api</artifactId>
<version>${java.mail.version}</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>${java.mail.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
@ -99,8 +175,9 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot-dependencies.version}</version>
<configuration>
<mainClass>com.example.camel.Boot</mainClass>
<mainClass>com.example.sbcamel.Boot</mainClass>
</configuration>
<executions>
<execution>

@ -1,13 +1,19 @@
**Please start the following services before testing**
## Setup
1. ApacheDS Server 2.0.0.AM27
1. Please start the following services before testing :
2. Apache ActiveMQ 6.1.5
```
ApacheDS Server 2.0.0.AM27
Apache ActiveMQ 6.1.5
H2 Database Version 2.3.232
```
2. Please see conf/springboot.yml for LDAP/ActiveMQ Address
3. For LDAP group and user setup, see misc/ldapdb.ldif
**Startup screens**
## Startup screens
1. ApacheDS Server 2.0.0.AM27
**1. ApacheDS Server 2.0.0.AM27**
```
C:\Users\XXX\apacheds-2.0.0.AM28-SNAPSHOT\bin>apacheds.bat default start
@ -41,7 +47,7 @@ Starting ApacheDS instance 'default'...
```
2. Apache ActiveMQ 6.1.5
**2. Apache ActiveMQ 6.1.5**
```
C:\Users\XXX\apache-activemq-6.1.5\bin>activemq start
@ -89,10 +95,16 @@ Loading message broker from: xbean:activemq.xml
INFO | ActiveMQ Jolokia REST API available at http://127.0.0.1:8161/api/jolokia/
```
**Setup**
**3. H2 Database Version 2.3.232**
* Please see conf/springboot.yml for LDAP/ActiveMQ Address
```
C:\Users\XXX\Desktop>SET JAVA_HOME=C:\Users\XXX\graalvm-jdk-17.0.14+8.1
* For LDAP group and user setup, see misc/ldapdb.ldif
C:\Users\XXX\Desktop>SET PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Program Files\PuTTY\;C:\Users\XXX\AppData\Local\Microsoft\WindowsApps;C:\Users\XXX\graalvm-jdk-17.0.14+8.1\bin;;C:\Users\XXX\graalvm-jdk-17.0.14+8.1\bin
C:\Users\XXX\Desktop>cd C:\Users\XXX\h2\bin
C:\Users\XXX\h2\bin>h2.bat -tcp -web
Web Console server running at http://127.0.0.1:8082?key=8c2f1bfb9fb08604f1e3a7849e8b338514438c2c729b87ecc98108b6102818c6 (only local connections)
TCP server running at tcp://127.0.0.1:9092 (only local connections)
```

@ -1,34 +0,0 @@
package com.example.camel;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.bean.validator.BeanValidationException;
import org.apache.camel.model.dataformat.JsonLibrary;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import jakarta.ws.rs.core.Response;
@Component
public class CamelRouter extends RouteBuilder {
@Value("${app.queue-name}")
private String queueName;
@Override
public void configure() throws Exception {
// very raw way, just to handle the validation responses
onException(BeanValidationException.class).handled(true)
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(Response.Status.BAD_REQUEST.getStatusCode()))
.setBody(simple("${exchangeProperty.CamelExceptionCaught.getMessage()}"));
from("cxfrs:/api?resourceClasses=" + UserService.class.getName() + "&bindingStyle=SimpleConsumer"
+ "&providers=jaxrsProvider&loggingFeatureEnabled=true").to("log:cxfrs-log?showAll=true")
.setHeader(Exchange.BEAN_METHOD_NAME, simple("${header.operationName}"))
.to("activemq6:queue:" + queueName);
from("activemq6:queue:" + queueName).to("bean-validator:user").bean(UserServiceImpl.class).marshal()
.json(JsonLibrary.Jackson);
}
}

@ -1,54 +0,0 @@
package com.example.camel;
import java.io.Serializable;
import java.util.StringJoiner;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
/**
* User entity
*
*/
@SuppressWarnings("serial")
public class User implements Serializable {
@NotNull(message = "custom message")
private Integer id;
@NotNull
@Size(min = 3, max = 20)
private String name;
public User() {
}
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return new StringJoiner(", ", User.class.getSimpleName() + "[", "]")
.add("id=" + id)
.add("name='" + name + "'")
.toString();
}
}

@ -1,52 +0,0 @@
package com.example.camel;
import java.util.Collection;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
/**
* Service interface for managing users.
*/
public interface UserService {
/**
* Find a user by the given ID
*
* @param id
* the ID of the user
* @return the user, or <code>null</code> if user not found.
*/
@GET
@Path("/user/{id}")
@Produces(MediaType.APPLICATION_JSON)
User findUser(@PathParam("id") Integer id);
/**
* Find all users
*
* @return a collection of all users
*/
@GET
@Path("/user")
@Produces(MediaType.APPLICATION_JSON)
Collection<User> findUsers();
/**
* Update the given user
*
* @param user
* the user
*/
@POST
@Path("/user")
@Consumes(MediaType.APPLICATION_JSON)
User updateUser(@Valid User user);
}

@ -1,33 +0,0 @@
package com.example.camel;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;
public class UserServiceImpl implements UserService {
private final Map<Integer, User> users = new TreeMap<>();
public UserServiceImpl() {
users.put(1, new User(1, "John Coltrane"));
users.put(2, new User(2, "Miles Davis"));
users.put(3, new User(3, "Sonny Rollins"));
}
@Override
public User findUser(Integer id) {
return users.get(id);
}
@Override
public Collection<User> findUsers() {
return users.values();
}
@Override
public User updateUser(User user) {
users.put(user.getId(), user);
return user;
}
}

@ -1,4 +1,4 @@
package com.example.camel;
package com.example.sbcamel;
import java.io.File;
@ -6,8 +6,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class Boot {
private static final Logger logger = LoggerFactory.getLogger(Boot.class);
@ -18,12 +20,12 @@ public class Boot {
configDirectory = args[0];
}
logger.info("config directory: {}", configDirectory);
if (new File(configDirectory).exists() && new File(configDirectory).isDirectory()) {
System.setProperty("spring.config.location", configDirectory + "/springboot.yml");
System.setProperty("logging.config", configDirectory + "/logback.xml");
}
System.setProperty("org.apache.activemq.SERIALIZABLE_PACKAGES","*");
System.setProperty("org.apache.activemq.SERIALIZABLE_PACKAGES", "*");
SpringApplication.run(Boot.class, args);
}

@ -0,0 +1,64 @@
package com.example.sbcamel.init;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import com.example.sbcamel.processor.InspectionProcessor;
import com.example.sbcamel.processor.TestProcessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider;
@Configuration
@ImportResource("classpath:schedule.xml")
public class AppConfig {
@Value("${app.inspection-client.javamail-config}")
private String javamailCfg;
@Bean
public Properties javaMailProperties() throws IOException {
Properties javaMailProperties = new Properties();
FileInputStream fis = new FileInputStream(javamailCfg);
javaMailProperties.load(fis);
fis.close();
return javaMailProperties;
}
@Bean
public CloseableHttpClient httpClient() {
return HttpClients.createDefault();
}
@Bean("objectMapper")
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.registerModule(new Jdk8Module());
return mapper;
}
@Bean
public JacksonJsonProvider jaxrsProvider() {
return new JacksonJsonProvider();
}
@Bean
public InspectionProcessor inspectionProcessor() {
return new InspectionProcessor();
}
@Bean
public TestProcessor testProcessor() {
return new TestProcessor();
}
}

@ -0,0 +1,87 @@
package com.example.sbcamel.init;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.bean.BeanConstants;
import org.apache.camel.component.cxf.common.message.CxfConstants;
import org.apache.camel.model.dataformat.JsonLibrary;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import com.example.sbcamel.mapper.SessionMapper;
import com.example.sbcamel.processor.InspectionProcessor;
import com.example.sbcamel.processor.TestProcessor;
import com.example.sbcamel.service.SessionService;
import jakarta.annotation.PostConstruct;
import jakarta.ws.rs.core.Response;
@Component
public class CamelRouter extends RouteBuilder {
private static final String createSessionSql = "CREATE TABLE IF NOT EXISTS public.session (sessionId UUID not null, "
+ "username CHARACTER VARYING not null, password CHARACTER VARYING not null, PRIMARY KEY (sessionId))";
private static final String addSelector1Sql = "ALTER TABLE IF EXISTS public.session ADD COLUMN IF NOT EXISTS selector1 CHARACTER VARYING NULL";
private static final String addSelector2Sql = "ALTER TABLE IF EXISTS public.session ADD COLUMN IF NOT EXISTS selector2 CHARACTER VARYING NULL";
private static final String addSelector3Sql = "ALTER TABLE IF EXISTS public.session ADD COLUMN IF NOT EXISTS selector3 CHARACTER VARYING NULL";
@Value("${app.queue-name}")
private String queueName;
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
protected InspectionProcessor inspectionProcessor;
@Autowired
protected TestProcessor testProcessor;
@PostConstruct
private void init() {
org.apache.ibatis.logging.LogFactory.useSlf4jLogging();
jdbcTemplate.execute(createSessionSql);
jdbcTemplate.execute(addSelector1Sql);
jdbcTemplate.execute(addSelector2Sql);
jdbcTemplate.execute(addSelector3Sql);
}
@Override
public void configure() throws Exception {
onException(jakarta.ws.rs.NotFoundException.class).handled(true)
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(Response.Status.NOT_FOUND.getStatusCode()))
.setBody(simple("${exchangeProperty.CamelExceptionCaught.getMessage()}"));
from("cxfrs:/api?resourceClasses=" + SessionService.class.getName() + "&bindingStyle=Default"
+ "&providers=jaxrsProvider&loggingFeatureEnabled=true").to("log:cxfrs?showAll=true")
.to("activemq6:queue:" + queueName).process(exchange -> {
if (exchange.getMessage().getBody() != null && exchange.getMessage().getBody() instanceof byte[]
&& new String((byte[]) exchange.getMessage().getBody()).equals("null")) {
if (exchange.getIn().getHeader(Exchange.HTTP_METHOD).equals("GET")) {
throw new jakarta.ws.rs.NotFoundException();
}
}
});
from("activemq6:queue:" + queueName)
.process(exchange -> exchange.getIn().setHeader(BeanConstants.BEAN_METHOD_NAME,
exchange.getIn().getHeader(CxfConstants.OPERATION_NAME.toLowerCase())))
.to("log:activemq?showAll=true").transacted("propagationRequired").choice()
.when(header(BeanConstants.BEAN_METHOD_NAME).isEqualTo("updateSession"))
.to("mybatis:" + SessionMapper.class.getName()
+ ".updateSession?statementType=Update&outputHeader=X-UpdateResult")
.process(exchange -> {
exchange.getMessage().setBody(exchange.getIn().getBody());
}).when(header(BeanConstants.BEAN_METHOD_NAME).isEqualTo("findSessions"))
.to("mybatis:" + SessionMapper.class.getName() + ".findSessions?statementType=SelectList")
.when(header(BeanConstants.BEAN_METHOD_NAME).isEqualTo("findSession"))
.to("mybatis:" + SessionMapper.class.getName() + ".findSession?statementType=SelectOne")
.when(header(BeanConstants.BEAN_METHOD_NAME).isEqualTo("inspect"))
.to("mybatis:" + SessionMapper.class.getName() + ".findSession?statementType=SelectOne")
.process(inspectionProcessor).when(header(BeanConstants.BEAN_METHOD_NAME).isEqualTo("test"))
.process(testProcessor).end().marshal().json(JsonLibrary.Jackson);
}
}

@ -0,0 +1,120 @@
package com.example.sbcamel.init;
import java.io.IOException;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import org.apache.camel.component.mybatis.MyBatisComponent;
import org.apache.camel.spring.spi.SpringTransactionPolicy;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.transaction.PlatformTransactionManager;
import com.google.common.reflect.ClassPath;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@Configuration
public class DatabaseConfig {
private static final Logger logger = LoggerFactory.getLogger(DatabaseConfig.class);
public static final String CFG_APP_DATABASE_URL = "${app.database.url}";
public static final String CFG_APP_DATABASE_USERNAME = "${app.database.username:}";
public static final String CFG_APP_DATABASE_PASSWORD = "${app.database.password:}";
public static final String CFG_APP_DATABASE_MAPPER_PACKAGE = "${app.database.mapper-package}";
public static final String CFG_APP_DATABASE_SPRINGBOOT_CLASS_PREFIX = "${app.database.springboot-class-prefix:BOOT-INF.classes.}";
@Value(CFG_APP_DATABASE_URL)
private String dbUrl;
@Value(CFG_APP_DATABASE_USERNAME)
private String dbUsername;
@Value(CFG_APP_DATABASE_PASSWORD)
private String dbPassword;
@Value(CFG_APP_DATABASE_MAPPER_PACKAGE)
private String mapperPackage;
@Value(CFG_APP_DATABASE_SPRINGBOOT_CLASS_PREFIX)
private String springBootClassPrefix;
@Bean
HikariConfig hikariConfig() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(dbUrl);
config.setUsername(dbUsername);
config.setPassword(dbPassword);
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
return config;
}
@Bean
DataSource dataSource(HikariConfig hikariConfig) {
return new TransactionAwareDataSourceProxy(new HikariDataSource(hikariConfig));
}
@Bean
PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
Environment environment = new Environment("development", new JdbcTransactionFactory(), dataSource);
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(
environment);
Set<Class<?>> mapperClasses = findAllClassesUsingGuava(mapperPackage);
mapperClasses.forEach(configuration::addMapper);
return new SqlSessionFactoryBuilder().build(configuration);
}
@Bean
MyBatisComponent mybatis(SqlSessionFactory sqlSessionFactory) {
MyBatisComponent mybatis = new MyBatisComponent();
mybatis.setSqlSessionFactory(sqlSessionFactory);
return mybatis;
}
@Bean
SpringTransactionPolicy propagationRequired(PlatformTransactionManager txManager) {
SpringTransactionPolicy propagationRequired = new SpringTransactionPolicy();
propagationRequired.setTransactionManager(txManager);
propagationRequired.setPropagationBehaviorName("PROPAGATION_REQUIRED");
return propagationRequired;
}
private Set<Class<?>> findAllClassesUsingGuava(String packageName) throws IOException {
logger.trace("Orm mappers packageName: {}", packageName);
return ClassPath.from(ClassLoader.getSystemClassLoader()).getAllClasses().stream()
.peek(clazz -> logger.trace("candidate class: {}, package name: {}", clazz, clazz.getPackageName()))
.filter(clazz -> clazz.getPackageName().endsWith(packageName))
.peek(clazz -> logger.info("accepted class: {}, package name: {}", clazz, clazz.getPackageName()))
.map(clazz -> {
try {
if (clazz.getName().startsWith(springBootClassPrefix)) {
return Class.forName(clazz.getName().replace(springBootClassPrefix, ""));
}
return Class.forName(clazz.getName());
} catch (ClassNotFoundException e) {
logger.error("cannot convert this class name to class: {}", clazz.getName());
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toSet());
}
}

@ -1,4 +1,4 @@
package com.example.camel;
package com.example.sbcamel.init;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@ -13,8 +13,6 @@ import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopul
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.web.SecurityFilterChain;
import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider;
@Configuration
public class SecurityConfig {
@ -35,10 +33,10 @@ public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(
(authorize) -> authorize.requestMatchers(HttpMethod.GET, "/**").hasAuthority(ROLE_BACKEND)
.requestMatchers(HttpMethod.POST, "/**").hasAuthority(ROLE_SERVER))
.httpBasic(Customizer.withDefaults()).csrf(csrf -> csrf.disable());
http.authorizeHttpRequests((authorize) -> authorize.requestMatchers(HttpMethod.GET, "/**")
.hasAuthority(ROLE_BACKEND).requestMatchers(HttpMethod.POST, "/**").hasAuthority(ROLE_SERVER)
.requestMatchers(HttpMethod.PUT, "/**").permitAll()).httpBasic(Customizer.withDefaults())
.csrf(csrf -> csrf.disable());
return http.build();
}
@ -59,9 +57,4 @@ public class SecurityConfig {
factory.setLdapAuthoritiesPopulator(authorities);
return factory.createAuthenticationManager();
}
@Bean
public JacksonJsonProvider jaxrsProvider() {
return new JacksonJsonProvider();
}
}

@ -0,0 +1,45 @@
package com.example.sbcamel.mapper;
import java.util.Collection;
import java.util.UUID;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.type.JdbcType;
import com.example.sbcamel.service.Session;
import com.example.sbcamel.service.UuidTypeHandler;
public interface SessionMapper {
@Insert({ "<script>",
"MERGE INTO public.session (sessionId, username, password, selector1, selector2, selector3) KEY (sessionId) VALUES (#{sessionId, javaType=java.util.UUID, "
+ "jdbcType=OTHER, typeHandler=com.example.sbcamel.service.UuidTypeHandler}, #{username}, #{password}, #{selector1}, #{selector2}, #{selector3}) ",
"</script>" })
int updateSession(Session session);
@Results({
@Result(property = "sessionId", column = "sessionId", javaType = UUID.class, jdbcType = JdbcType.OTHER, typeHandler = UuidTypeHandler.class),
@Result(property = "username", column = "username"), @Result(property = "password", column = "password"),
@Result(property = "selector1", column = "selector1"),
@Result(property = "selector2", column = "selector2"),
@Result(property = "selector3", column = "selector3") })
@Select({ "<script>",
"select sessionId, username, password, selector1, selector2, selector3 from public.session where sessionId=#{list[0], javaType=java.util.UUID, "
+ "jdbcType=OTHER, typeHandler=com.example.sbcamel.service.UuidTypeHandler}",
"</script>" })
Session findSession(UUID sessionId);
@Results({
@Result(property = "sessionId", column = "sessionId", javaType = UUID.class, jdbcType = JdbcType.OTHER, typeHandler = UuidTypeHandler.class),
@Result(property = "username", column = "username"), @Result(property = "password", column = "password"),
@Result(property = "selector1", column = "selector1"),
@Result(property = "selector2", column = "selector2"),
@Result(property = "selector3", column = "selector3") })
@Select({ "<script>",
"select sessionId, username, password, selector1, selector2, selector3 from public.session order by sessionId",
"</script>" })
Collection<Session> findSessions();
}

@ -0,0 +1,142 @@
package com.example.sbcamel.processor;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.htmlunit.FailingHttpStatusCodeException;
import org.htmlunit.WebClient;
import org.htmlunit.html.HtmlButton;
import org.htmlunit.html.HtmlForm;
import org.htmlunit.html.HtmlPage;
import org.htmlunit.html.HtmlPasswordInput;
import org.htmlunit.html.HtmlTextInput;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import com.example.sbcamel.service.Session;
public class InspectionProcessor implements Processor {
private static final Logger logger = LoggerFactory.getLogger(InspectionProcessor.class);
@Value("${app.htmlunit.login-url}")
private String loginUrl;
@Value("${app.htmlunit.login-btn-selector}")
private String loginBtnSelector;
@Value("${app.htmlunit.login-wait-millis:3000}")
private Long loginWaitMillis;
@Value("${app.htmlunit.user-page-url}")
private String userPageUrl;
@Value("${app.htmlunit.vip11-page-url}")
private String vip11PageUrl;
@Value("${app.htmlunit.vip12-page-url}")
private String vip12PageUrl;
@Value("${app.htmlunit.vip13-page-url}")
private String vip13PageUrl;
@Value("${app.htmlunit.vip21-page-url}")
private String vip21PageUrl;
@Value("${app.htmlunit.vip22-page-url}")
private String vip22PageUrl;
@Value("${app.htmlunit.vip23-page-url}")
private String vip23PageUrl;
@Value("${app.htmlunit.vip31-page-url}")
private String vip31PageUrl;
@Value("${app.htmlunit.vip32-page-url}")
private String vip32PageUrl;
@Value("${app.htmlunit.vip33-page-url}")
private String vip33PageUrl;
@Value("${app.htmlunit.logout-url}")
private String logoutUrl;
@Override
public void process(Exchange exchange) throws Exception {
Session session = (Session) exchange.getMessage().getBody();
logger.debug("session: {}", session);
try (final WebClient webClient = new WebClient()) {
final HtmlPage loginPage = webClient.getPage(loginUrl);
logger.debug("loginPage: {}", loginPage.asXml());
final HtmlForm form = loginPage.getForms().get(0);
final HtmlTextInput usernameField = form.getInputByName("username");
usernameField.type(session.getUsername());
logger.debug("entered username: {}", session.getUsername());
final HtmlPasswordInput passwordField = form.getInputByName("password");
passwordField.type(session.getPassword());
logger.debug("entered password: {}", session.getPassword());
HtmlButton loginBtn = loginPage.querySelector(loginBtnSelector);
logger.debug("loginBtn type: {}", loginBtn.getType());
loginBtn.click();
Thread.sleep(loginWaitMillis);
HtmlPage userPage = webClient.getPage(userPageUrl);
logger.debug("userPage hashcode: {}", userPage.hashCode());
Map<String, List<String>> reply11 = fillResponseMap(webClient, session, vip11PageUrl);
Map<String, List<String>> reply12 = fillResponseMap(webClient, session, vip12PageUrl);
Map<String, List<String>> reply13 = fillResponseMap(webClient, session, vip13PageUrl);
Map<String, List<String>> reply21 = fillResponseMap(webClient, session, vip21PageUrl);
Map<String, List<String>> reply22 = fillResponseMap(webClient, session, vip22PageUrl);
Map<String, List<String>> reply23 = fillResponseMap(webClient, session, vip23PageUrl);
Map<String, List<String>> reply31 = fillResponseMap(webClient, session, vip31PageUrl);
Map<String, List<String>> reply32 = fillResponseMap(webClient, session, vip32PageUrl);
Map<String, List<String>> reply33 = fillResponseMap(webClient, session, vip33PageUrl);
webClient.getPage(logoutUrl);
exchange.getMessage().setBody(Map.of("vip11Page", reply11, "vip12Page", reply12, "vip13Page", reply13,
"vip21Page", reply21, "vip22Page", reply22, "vip23Page", reply23,
"vip31Page", reply31, "vip32Page", reply32, "vip33Page", reply33));
} catch (Exception e) {
logger.error("LoginProcessor process error!", e);
}
}
private Map<String, List<String>> fillResponseMap(WebClient webClient, Session session, String url)
throws FailingHttpStatusCodeException, MalformedURLException, IOException {
Map<String, List<String>> reply = new HashMap<>();
HtmlPage vipPage = webClient.getPage(url);
Document doc = Jsoup.parse(vipPage.asXml());
for (String selector : List.of(session.getSelector1(), session.getSelector2(), session.getSelector3())) {
Elements elements = doc.select(selector);
List<String> selectedList = new ArrayList<>();
for (Element element : elements) {
logger.debug("[fillResponseMap] {} -> {}", selector, element.html());
selectedList.add(element.html());
}
reply.put(selector, selectedList);
}
return reply;
}
}

@ -0,0 +1,54 @@
package com.example.sbcamel.processor;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.commons.io.FileUtils;
import org.apache.cxf.message.MessageContentsList;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
public class TestProcessor implements Processor {
private static final Logger logger = LoggerFactory.getLogger(TestProcessor.class);
@Value("${app.jsoup.html-file}")
private String htmlFilePath;
@SuppressWarnings("unchecked")
@Override
public void process(Exchange exchange) throws Exception {
logger.info("in class: {}", exchange.getIn().getBody().getClass().getName());
MessageContentsList mcl = (MessageContentsList) exchange.getIn().getBody();
List<String> selectors = (List<String>) mcl.get(0);
logger.info("selectors: {}", selectors);
File htmlFile = new File(htmlFilePath);
String html = FileUtils.readFileToString(htmlFile, StandardCharsets.UTF_8);
Document doc = Jsoup.parse(html);
Map<String, List<String>> reply = new HashMap<>();
for (String selector : selectors) {
Elements elements = doc.select(selector);
List<String> selectedList = new ArrayList<>();
for (Element element : elements) {
logger.info("{} -> {}", selector, element.html());
selectedList.add(element.html());
}
reply.put(selector, selectedList);
}
exchange.getMessage().setBody(reply);
}
}

@ -0,0 +1,70 @@
package com.example.sbcamel.service;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
private static final Logger logger = LoggerFactory.getLogger(EmailService.class);
@Autowired
private Properties javaMailProperties;
public boolean sendEmail(String fromAddress, String smtpPassword, String[] toAddresses, String subject,
String content) {
try {
logger.debug("[sendEmail] fromAddress: {}, toAddresses: {}", fromAddress, toAddresses);
if (StringUtils.isEmpty(subject) || StringUtils.isEmpty(smtpPassword) || StringUtils.isEmpty(fromAddress)
|| toAddresses.length == 0) {
logger.warn("[sendEmail] subject, smtp-password, source-address or target-address is not set");
return false;
}
String emailHost = javaMailProperties.getProperty("mail.smtp.host");
logger.debug("[sendEmail] {} -> {}, mail.smtp.host: {}", fromAddress, List.of(toAddresses), emailHost);
if (StringUtils.isNotBlank(emailHost) && StringUtils.isNotBlank(fromAddress) && toAddresses.length > 0) {
logger.debug("[sendEmail] starting to send message from: {}", fromAddress);
Session session = Session.getInstance(javaMailProperties);
MimeMessage message = new MimeMessage(session);
message.setContent(content, "text/html; charset=utf-8");
message.setFrom(new InternetAddress(fromAddress));
Arrays.asList(toAddresses).forEach(toAddress -> {
try {
message.addRecipient(Message.RecipientType.TO, new InternetAddress(toAddress));
} catch (Exception e) {
logger.error("sendEmail error!", e);
}
});
message.setSubject(subject);
Transport transport = session.getTransport("smtp");
transport.connect(emailHost, fromAddress, smtpPassword);
transport.sendMessage(message, message.getAllRecipients());
transport.close();
logger.debug("sendEmail to: {}", Arrays.asList(toAddresses));
return true;
} else {
logger.warn("[sendEmail] missing either from-field, to-field or host address!");
}
} catch (Exception e) {
logger.error("sendEmail error!", e);
}
return false;
}
}

@ -0,0 +1,161 @@
package com.example.sbcamel.service;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.MapDifference;
import com.google.common.collect.MapDifference.ValueDifference;
import com.google.common.collect.Maps;
public class InspectionClient {
private static final Logger logger = LoggerFactory.getLogger(InspectionClient.class);
@Autowired
private CloseableHttpClient httpClient;
@Autowired
private EmailService emailService;
@Autowired
private ObjectMapper objectMapper;
@Value("${app.inspection-client.email-password:}")
private String smtpPassword;
@Value("${app.inspection-client.email-sender:}")
private String fromAddress;
@Value("${app.inspection-client.email-recipient:}")
private String[] toAddresses;
@Value("${app.inspection-client.http-url}")
private String url;
@Value("${app.inspection-client.http-method}")
private String httpMethod;
@Value("${app.inspection-client.expected-result-file-path}")
private String expectedResultFilePath;
@Value("${app.inspection-client.alert-subject}")
private String alertSubject;
@Value("${app.inspection-client.http-username}")
private String username;
@Value("${app.inspection-client.http-password}")
private String password;
private static final String getBasicAuthenticationHeader(String username, String password) {
String valueToEncode = username + ":" + password;
return "Basic " + Base64.getEncoder().encodeToString(valueToEncode.getBytes());
}
public void sendMessage() {
try {
File jsonFile = new File(expectedResultFilePath);
if (!jsonFile.exists()) {
logger.info("file does not exist: {}", expectedResultFilePath);
return;
}
String jsonString = FileUtils.readFileToString(jsonFile, StandardCharsets.UTF_8);
logger.info("expected result: {}", jsonString);
HashMap<String, Object> expectedMap = convertStringToMap(jsonString);
logger.info("calling {} {}", httpMethod, url);
HttpUriRequestBase req = new HttpUriRequestBase(httpMethod, URI.create(url));
req.addHeader("Authorization", getBasicAuthenticationHeader(username, password));
Pair<Boolean, Boolean> sendEmailResult = httpClient.execute(req,
new HttpClientResponseHandler<Pair<Boolean, Boolean>>() {
@Override
public Pair<Boolean, Boolean> handleResponse(ClassicHttpResponse result)
throws HttpException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
result.getEntity().writeTo(baos);
String responseText = new String(baos.toByteArray(), StandardCharsets.UTF_8);
logger.info("actual result: {}", responseText);
if (StringUtils.isEmpty(responseText)) {
logger.error("actual result is empty!");
return Pair.of(Boolean.FALSE, Boolean.FALSE);
}
baos.close();
try {
HashMap<String, Object> resultMap = convertStringToMap(responseText);
MapDifference<String, Object> diff = Maps.difference(expectedMap, resultMap);
logger.info("diff.areEqual: {}", diff.areEqual());
StringBuilder contentBuilder = new StringBuilder();
if (!diff.areEqual()) {
Map<String, ValueDifference<Object>> entriesDiffering = diff.entriesDiffering();
entriesDiffering.entrySet().stream().forEach(entry -> {
logger.info("entriesDiffering key: {}", entry.getKey());
logger.info("entriesDiffering expected: {}", entry.getValue().leftValue());
logger.info("entriesDiffering actual: {}", entry.getValue().rightValue());
contentBuilder.append(entry.getKey() + "<br/>\n");
contentBuilder.append("EXPECTED: " + entry.getValue().leftValue() + "<br/>\n");
contentBuilder.append("ACTUAL: " + entry.getValue().rightValue() + "<br/>\n");
contentBuilder.append("<br/>\n");
});
return Pair.of(Boolean.TRUE, emailService.sendEmail(fromAddress, smtpPassword,
toAddresses, alertSubject, contentBuilder.toString()));
}
} catch (IOException e) {
logger.error("Error occurred while parsing message", e);
}
return Pair.of(Boolean.FALSE, Boolean.FALSE);
}
});
logger.info("send email result: {}", sendEmailResult);
} catch (Exception e) {
logger.error("Error occurred while preparing to send message", e);
}
}
public HashMap<String, Object> convertStringToMap(String jsonString) throws IOException {
TypeReference<java.util.HashMap<String, Object>> ref = new TypeReference<>() {
};
return new HashMap<>(objectMapper.readValue(jsonString, ref));
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getHttpMethod() {
return httpMethod;
}
public void setHttpMethod(String httpMethod) {
this.httpMethod = httpMethod;
}
}

@ -0,0 +1,99 @@
package com.example.sbcamel.service;
import java.io.Serializable;
import java.util.UUID;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
/**
* User entity
*
*/
@SuppressWarnings("serial")
public class Session implements Serializable {
private UUID sessionId;
@NotNull
@Size(min = 3, max = 40)
private String username;
@NotNull
@Size(min = 3, max = 40)
private String password;
@NotNull
private String selector1;
@NotNull
private String selector2;
@NotNull
private String selector3;
public Session() {
}
public Session(UUID sessionId, @NotNull @Size(min = 3, max = 40) String username,
@NotNull @Size(min = 3, max = 40) String password) {
this.sessionId = sessionId;
this.username = username;
this.password = password;
}
public UUID getSessionId() {
return sessionId;
}
public void setSessionId(UUID sessionId) {
this.sessionId = sessionId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSelector1() {
return selector1;
}
public void setSelector1(String selector1) {
this.selector1 = selector1;
}
public String getSelector2() {
return selector2;
}
public void setSelector2(String selector2) {
this.selector2 = selector2;
}
public String getSelector3() {
return selector3;
}
public void setSelector3(String selector3) {
this.selector3 = selector3;
}
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
}

@ -0,0 +1,46 @@
package com.example.sbcamel.service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
/**
* Service interface for managing sessions.
*/
public interface SessionService {
@POST
@Path("/session")
@Consumes(MediaType.APPLICATION_JSON)
Session updateSession(@Valid Session session);
@GET
@Path("/session/{sessionId}")
@Produces(MediaType.APPLICATION_JSON)
Session findSession(@PathParam("sessionId") UUID sessionId);
@GET
@Path("/session")
@Produces(MediaType.APPLICATION_JSON)
Collection<Session> findSessions();
@POST
@Path("/inspect/{sessionId}")
@Produces(MediaType.APPLICATION_JSON)
Map<String, List<String>> inspect(@PathParam("sessionId") UUID sessionId);
@POST
@Path("/test")
@Produces(MediaType.APPLICATION_JSON)
Map<String, Map<String, List<String>>> test(List<String> selectors);
}

@ -0,0 +1,33 @@
package com.example.sbcamel.service;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.UUID;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
public class UuidTypeHandler extends BaseTypeHandler<UUID> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType)
throws SQLException {
ps.setObject(i, parameter);
}
@Override
public UUID getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getObject(columnName, UUID.class);
}
@Override
public UUID getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getObject(columnIndex, UUID.class);
}
@Override
public UUID getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getObject(columnIndex, UUID.class);
}
}

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task https://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<task:scheduler id="inspectionScheduler" pool-size="2" />
<bean id="inspectionClient"
class="com.example.sbcamel.service.InspectionClient">
</bean>
<task:scheduled-tasks scheduler="inspectionScheduler">
<task:scheduled ref="inspectionClient"
method="sendMessage" cron="0 * * * * ?" />
</task:scheduled-tasks>
</beans>
Loading…
Cancel
Save