Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
38 changes: 38 additions & 0 deletions framework/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,50 @@

<artifactId>framework</artifactId>

<properties>
<spring-framework.version>7.0.6</spring-framework.version>
</properties>

<dependencies>
<dependency>
<groupId>com.neroyun</groupId>
<artifactId>mediator</artifactId>
<version>${neroyun.mediator.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.84</version>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>${jakarta-persistence.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.theurl.framework.application;

/**
* Marker interface for application services. Application services are responsible for orchestrating use cases and business logic.
*/
public interface ApplicationService {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.theurl.framework.application;

import com.neroyun.mediator.Mediator;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.RequestScope;

import java.util.concurrent.CompletionException;
import java.util.function.Consumer;

@Service
@RequestScope
public class BaseApplicationService implements ApplicationService {
protected Mediator mediator;

protected ThreadPoolTaskExecutor mediatorTaskExecutor;

protected BaseApplicationService(ApplicationContext applicationContext) {
mediator = applicationContext.getBean(Mediator.class);
mediatorTaskExecutor = applicationContext.getBean(ThreadPoolTaskExecutor.class);
}

protected void handleException(Throwable throwable, Consumer<Throwable> consumer) {
if (throwable instanceof CompletionException exception) {
handleException(exception.getCause(), consumer);
} else {
consumer.accept(throwable);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package io.theurl.framework.configure;

import com.neroyun.mediator.internal.AggregateException;
import com.neroyun.mediator.validation.ValidationException;
import io.theurl.framework.security.AccountException;
import io.theurl.framework.security.CredentialException;
import io.theurl.framework.security.UnauthorizedAccessException;
import jakarta.persistence.EntityNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.CompletionException;

@RestControllerAdvice
public class GlobalExceptionHandler {
private final String name = "message";
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

@ExceptionHandler(AccountException.class)
public ResponseEntity<Map<String, String>> handleAccountException(AccountException exception) {
LOGGER.error(exception.getMessage(), exception);
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(Map.of(name, exception.getLocalizedMessage()));
}

@ExceptionHandler(CredentialException.class)
public ResponseEntity<Map<String, String>> handleCredentialException(CredentialException exception) {
LOGGER.error(exception.getMessage(), exception);
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(Map.of(name, exception.getLocalizedMessage()));
}

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<Map<String, String>> handleIllegalArgumentException(IllegalArgumentException exception) {
LOGGER.error(exception.getMessage(), exception);
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Map.of(name, exception.getLocalizedMessage()));
}

@ExceptionHandler(UnauthorizedAccessException.class)
public ResponseEntity<Map<String, String>> handleUnauthorizedAccessException(UnauthorizedAccessException exception) {
LOGGER.error(exception.getMessage(), exception);
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(Map.of(name, exception.getLocalizedMessage()));
}

@ExceptionHandler(ValidationException.class)
public ResponseEntity<Map<String, String>> handleValidationException(ValidationException exception) {
LOGGER.error(exception.getMessage(), exception);
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Map.of(name, exception.getLocalizedMessage()));
}

@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<Map<String, String>> handleEntityNotFoundException(EntityNotFoundException exception) {
LOGGER.error(exception.getMessage(), exception);
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(Map.of(name, exception.getLocalizedMessage()));
}

@ExceptionHandler(AggregateException.class)
public ResponseEntity<Map<String, String>> handleAggregateException(AggregateException exception) {
LOGGER.error(exception.getMessage(), exception);
Throwable cause = exception.getExceptions().getFirst();
LOGGER.debug("AggregateException contains {} exceptions, handling the first one: {}", exception.getExceptions().size(), cause.getMessage());
return handleGeneralException(cause);
}

@ExceptionHandler(CompletionException.class)
public ResponseEntity<Map<String, String>> handleCompletionException(CompletionException exception) {
LOGGER.error(exception.getMessage(), exception);
Throwable cause = exception.getCause();
return handleGeneralException(cause);
}

private ResponseEntity<Map<String, String>> handleGeneralException(Throwable exception) {
if (exception == null) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
return switch (exception) {
case AccountException accountException -> handleAccountException(accountException);
case EntityNotFoundException entityNotFoundException ->
handleEntityNotFoundException(entityNotFoundException);
case CredentialException credentialException -> handleCredentialException(credentialException);
case IllegalArgumentException illegalArgumentException ->
handleIllegalArgumentException(illegalArgumentException);
case UnauthorizedAccessException unauthorizedAccessException ->
handleUnauthorizedAccessException(unauthorizedAccessException);
case ValidationException validationException -> handleValidationException(validationException);
default -> ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of(name, exception.getLocalizedMessage()));
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.theurl.framework.configure;

import com.neroyun.mediator.*;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskDecorator;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

@SuppressWarnings("rawtypes")
@Configuration
public class MediatorConfiguration {
@Bean
public Mediator mediator(ObjectProvider<Handler> handlers, ObjectProvider<Middleware> middlewares, ObjectProvider<Validator> validators) {
return new PipelinedMediator()
.use(() -> handlers.stream())
.use(() -> middlewares.stream())
.use(() -> validators.stream());
}

@Bean(name = "taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("mediator-");
executor.setCorePoolSize(8);
executor.setMaxPoolSize(32);
executor.setQueueCapacity(200);
executor.setTaskDecorator(copyRequestContextDecorator());
executor.initialize();
return executor;
}

private TaskDecorator copyRequestContextDecorator() {
return runnable -> {
RequestAttributes context = RequestContextHolder.getRequestAttributes();
return () -> {
try {
if (context != null) {
RequestContextHolder.setRequestAttributes(context);
}
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.theurl.framework.configure;

//import org.apache.hc.client5.http.config.RequestConfig;
//import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
//import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
//import org.apache.hc.core5.util.Timeout;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;

//@Configuration
//public class RestTemplateConfig {
// @ConditionalOnMissingBean(RestTemplate.class)
// @Bean
// public RestTemplate restTemplate() {
// RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());
// return restTemplate;
// }
//
// private ClientHttpRequestFactory getClientHttpRequestFactory() {
// int timeout = 5000;
// RequestConfig config = RequestConfig.custom()
// .setConnectionRequestTimeout(Timeout.of(timeout, TimeUnit.MILLISECONDS))
// //.setSocketTimeout(timeout)
// .build();
// CloseableHttpClient client = HttpClientBuilder
// .create()
// .setDefaultRequestConfig(config)
// .build();
// return new HttpComponentsClientHttpRequestFactory(client);
// }
//}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.theurl.framework.utility;

public class GuidGenerator {
}
6 changes: 3 additions & 3 deletions identity/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
Expand Down Expand Up @@ -91,7 +91,7 @@
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux-test</artifactId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
@SpringBootApplication
@ComponentScans({
@ComponentScan("io.theurl.framework"),
@ComponentScan("io.theurl.identity")
})
public class IdentityApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.theurl.identity.external;

import java.util.concurrent.CompletableFuture;

/**
* Defines the contract for external authentication providers.
* Implementations of this interface should handle the authentication process with external services.
Expand All @@ -10,5 +12,5 @@ public interface ExternalAuthProvider {
* @param authCode The authentication code received from the external service.
* @return The result of the authentication process encapsulated in an ExternalAuthResult object.
*/
ExternalAuthResult authenticate(String authCode);
CompletableFuture<ExternalAuthResult> authenticateAsync(String authCode);
}
Loading
Loading