Designing a new application, I have two sets of domain objects. One set mirrors the other and domain objects basically are paired up with similar attributes. When a domain object in the set A is created, updated, or deleted, a corresponding domain object in the set B will also be created, updated, or deleted. To reduce any coupling, I would like to separate those operations between a pair of domain objects. What will be a good mechanism to achieve this approach? I am thinking of using a messaging system. Will it work well for this case? I use Spring for this project.
Yes, using application events is a common solution for decreasing coupling between objects.
Actually spring already has builtin mechanism for that.
You might come up with something like:
#SpringBootApplication
public class So44490189Application {
public static void main(String[] args) {
SpringApplication.run(So44490189Application.class, args);
}
public static class UserCreatedEvent {
private final String email;
public UserCreatedEvent(String email) {
this.email = email;
}
}
#Service
public static class UserService {
#EventListener
public void handleUserCreatedEvent(UserCreatedEvent userCreatedEvent) {
System.out.println("Creating user " + userCreatedEvent.email);
}
}
#Service
public static class MemberService {
#EventListener
public void handleUserCreatedEvent(UserCreatedEvent userCreatedEvent) {
System.out.println("Creating member " + userCreatedEvent.email);
}
}
#Service
public static class OperationService {
private final ApplicationEventPublisher eventPublisher;
#Autowired
public OperationService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void createUser(String email) {
eventPublisher.publishEvent(new UserCreatedEvent(email));
}
}
#RestController
public static class OperationController {
private final OperationService operationService;
#Autowired
public OperationController(OperationService operationService) {
this.operationService = operationService;
}
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public void createUser(String email) {
operationService.createUser(email);
}
}
}
Usage
curl -XPOST 'localhost:8080?email=admin#localhost.localdomain'
Output
Creating member admin#localhost.localdomain
Creating user admin#localhost.localdomain
In this case creation user and members are mirrored.
One possible problem is transaction support and there is a couple of ways to deal with that. Spring has tools for it as well.
Related
I searched for a while but have no clue what I am missing.
I want to implement the observer pattern in Spring, to update documents in MongoDB which will create a notification in the front-end.
I implemented a Service class for the notifications (here are more than one, I show you just two of them), using #EventListener annotation
#Service
#RequiredArgsConstructor
public class NotificationService {
#EventListener ({ContextStartedEvent.class})
public void updateNotificationForIdea(String ideaId) {
//some logic inside the Service classes
}
}
#EventListener ({ContextStartedEvent.class})
public void createNotificationForNewComment(String ideaId, String creatorId) {
//some logic inside the Service classes
}
}
and I tried to implement the service, from where I want to send the notification
#Service
#RequiredArgsConstructor
public class CommentService {
private final ApplicationEventPublisher eventPublisher;
public CommentServiceResult createComment(Comment comment, String creatorId) {
// some logic
eventPublisher.publishEvent(); //here is where I miss something
return CommentServiceResult.builder()
.comment(createdComment)
.resultState(state)
.build();
}
I read that I need something like an "EventHelperClass" and tried to build this:
public class NotificationEvents extends ApplicationEvent{
public NotificationEvents(Object source) {
super(source);
}
private void updateNotificationForIdea(String ideaId){
}
private void createNotificationForNewComment(String ideaId, String creatorId) {
}
}
but I really don't know how to get the notifications from the NotificationsService in here (because they are void and are not a simple String message), to start the event from within CommentService...
I shortened the dependencies from the services and removed the internal logik for readability...
eventPublisher.publishEvent(...)
fires an event, you'll need to pass an event object as parameter, e.g.
public class CustomSpringEvent extends ApplicationEvent {
private String message;
public CustomSpringEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
Methods annotated with #EventListener ({CustomSpringEvent.class}) will catch it.
In your case:
CommentService fires an CommentEvent (fields: Comment comment, String creatorId).
Any Bean with a method
#EventListener ({CommentEvent.class})
void onCommentEvent(CommentEvent event) {
// ....
}
will catch it and get the event data.
https://www.baeldung.com/spring-events
I have this scheduler which is used to check for new data into DB table:
#Service
public class ReloadCache {
#Scheduled(fixedDelay = 1000)
public void reload() {
...... do something
}
}
......
#Service
public class LogicalClient {
final Map<String, Map<String, String>> map;
#PostConstruct
public void initializeBalances() {
............ map = new HashMap.......
}
#KafkaListener(......")
public void handle(....) {
.......read map here
}
}
Note that these two services are located in separate Java classes and packages.
When schedule runs and change is detected how how I can call again Java method initializeBalances annotated with #PostConstruct in order to generate again the map structure?
Inject your LogicalClient in your ReloadCache class and call that function like this:
#Service
public class ReloadCache {
private final LogicalClient logicalClient;
public ReloadCache(LogicalClient client) // Injection through constructor.
{
this.logicalClient = client;
}
#Scheduled(fixedDelay = 1000)
public void reload() {
...... do something
client.initializeBalances()
}
}
Both your classes are annotated with the #Service. So they are both Spring beans that can be injected wherever you find it suitable (as long as the receiving class is a bean itself).
I am working within an environment that changes credentials every several minutes. In order for beans that implement clients who depend on these credentials to work, the beans need to be refreshed. I decided that a good approach for that would be implementing a custom scope for it.
After looking around a bit on the documentation I found that the main method for a scope to be implemented is the get method:
public class CyberArkScope implements Scope {
private Map<String, Pair<LocalDateTime, Object>> scopedObjects = new ConcurrentHashMap<>();
private Map<String, Runnable> destructionCallbacks = new ConcurrentHashMap<>();
private Integer scopeRefresh;
public CyberArkScope(Integer scopeRefresh) {
this.scopeRefresh = scopeRefresh;
}
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!scopedObjects.containsKey(name) || scopedObjects.get(name).getKey()
.isBefore(LocalDateTime.now().minusMinutes(scopeRefresh))) {
scopedObjects.put(name, Pair.of(LocalDateTime.now(), objectFactory.getObject()));
}
return scopedObjects.get(name).getValue();
}
#Override
public Object remove(String name) {
destructionCallbacks.remove(name);
return scopedObjects.remove(name);
}
#Override
public void registerDestructionCallback(String name, Runnable runnable) {
destructionCallbacks.put(name, runnable);
}
#Override
public Object resolveContextualObject(String name) {
return null;
}
#Override
public String getConversationId() {
return "CyberArk";
}
}
#Configuration
#Import(CyberArkScopeConfig.class)
public class TestConfig {
#Bean
#Scope(scopeName = "CyberArk")
public String dateString(){
return LocalDateTime.now().toString();
}
}
#RestController
public class HelloWorld {
#Autowired
private String dateString;
#RequestMapping("/")
public String index() {
return dateString;
}
}
When I debug this implemetation with a simple String scope autowired in a controller I see that the get method is only called once in the startup and never again. So this means that the bean is never again refreshed. Is there something wrong in this behaviour or is that how the get method is supposed to work?
It seems you need to also define the proxyMode which injects an AOP proxy instead of a static reference to a string. Note that the bean class cant be final. This solved it:
#Configuration
#Import(CyberArkScopeConfig.class)
public class TestConfig {
#Bean
#Scope(scopeName = "CyberArk", proxyMode=ScopedProxyMode.TARGET_CLASS)
public NonFinalString dateString(){
return new NonFinalString(LocalDateTime.now());
}
}
if (!WebUtils.isEmpty(appAppService.getByAppid(id))) {
LOGGER.info(":bind app info");
errorNum++;
}
if (!WebUtils.isEmpty(appDownloadCensusService.getByAppid(id))) {
LOGGER.info(":bind bi info");
errorNum++;
}
if (!WebUtils.isEmpty(appManuCensusService.getByAppid(id))) {
LOGGER.info(":bind download info");
errorNum++;
}
if (!WebUtils.isEmpty(appOrderService.getByAppid(id))) {
LOGGER.info(":bind order info");
errorNum++;
}
if (!WebUtils.isEmpty(appShareService.getByAppid(id))) {
LOGGER.info(":bind share info");
errorNum++;
}
......
these xxxService are #Autowired properties, and the "...." presents other similar if statements.How can I avoid this by refactoring code?I tried to use reflection to get the xxxService,but I found that the xxxService class could be created,but properties in them,e.g. xxxxDao,SessionFactory(I was using Hibernate),these cannot be created by reflection,how can I
do this?Or if there any other way to solve the if check issue?
What I really want is how to refactor the code to make it look more pretty:how can I avoid so many if statements?
Considering all your classes share the same interface, as they should, you can inject all it's implementations into a List<>. Let's say the interface name is AppService.
#Component
public class Example {
private final List<AppService> appServices;
public Example(List<AppService> appServices) {
this.appServices = appServices;
}
public void verifyLogs(int id) {
appServices.forEach(appService -> {
if (!WebUtils.isEmpty(appService.getAppById(id))) {
LOGGER.info(":bind order info");
errorNum++;
}
}
);
}
The interface would be something like:
public interface AppService {
App getAppById(int id);
}
And the services:
#Service
public class AppAppService implements AppService {
public App getAppById(int id) {
// logic to get the app by id
}
}
Hope it helps!
Trying a different approach. The reason you can instantiate the classes but can't have the properties is that you have to get the instances from Spring context, where they're all initialised.
To do that you have to have access to the ApplicationContext, for example implementing the ApplicationContextAware interface.
#Component
public class Example implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void verify(int id) {
MyAppService appService = (MyAppService) applicationContext.getAutowireCapableBeanFactory().getBean("serviceName");
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
This way you achieve what you were looking for with the reflection methods in your question.
Ok, let’s if this is what you’re looking for. You’d then have to use reflection.
Merging the answer with application context, it would go like this:
private static final List<String> serviceClassNames = Arrays.asList(
“appAppService”, “censusAppService”, ...)
public void verify() {
serviceClassNames.forEach(className -> {
Object service = applicationContext.getAutowireCapableFactory().getBean(className);
Object app = Arrays.stream(service.getClass().getDeclaredMethods())
.filter(method -> method.getName.equals(“getAppById”))
.findFirst()
.orElseThrow(() -> new RuntimeException(“Method not found!”)
.invoke(id)
If (!WebUtils.isEmpty(app)) {
LOGGER.info......
}
}
Is this what you need?
Are there any techiques to collect a number of gwt-rpc service callback results?
I have a dialog window used to create new or edit existing object. These objects have a number of references to other object. So when user creating or editing an object he may pick one in the ListBox.
public class School {
private String name;
private String address;
}
public class Car {
private String model;
private String type;
}
public class Person {
private String firstName;
private String lastName;
private School school;
private Car firstCar;
}
When the dialog window appears on the screen it should request all available values for all referencing fields. These values are requested with AsyncCallback's via gwt-rpc, so I can handle it one-by-one.
service.getAllSchools(new AsyncCallback<List<School>>() {
#Override
public void onSuccess(List<School> result) {
fillSchoolListBox(result);
}
#Override
public void onFailure(Throwable caught) {
Window.alert("ups...");
}
});
...
service.getAllSchools(new AsyncCallback<List<Car>>() {
#Override
public void onSuccess(List<Car> result) {
fillCarListBox(result);
}
#Override
public void onFailure(Throwable caught) {
Window.alert("ups...");
}
});
How to get all result in one place?
Thanks.
The best solution would be Command Patter as igorbel said, but if you are beginner you can design for example Bean Container that only contains beans that must be transported at one request.
For example:
public class BeanContainer{
private ArrayList<School> schools = new ArrayList<School>();
private ArrayList<Car> cars = new ArrayList<Car>;
private ArrayList<Person> people = ArrayList<Person>();
public void addSchool(School school){
this.schools.add(school);
}
public void addSchoolCollection(ArrayList<School> schools){
this.schools.add(schools);
}
public ArrayList<School> getSchoolCollection(){
return schools;
}
...
}
Why don't you create a new service method that returns all the data as a result?
The implementation of such a method could simply call all of the other methods. You will have to encapsulate all the required data and return it as a single result. One example how you could handle this:
In the service implementation:
#Override
public Data getAllData(){
List<Cars> cars = this.getAllCars();
List<School> schools = this.getAllSchools();
return new Data(cars, schools);
}
And you can then use the method like this:
service.getAllData(new AsyncCallback<Data data>() {
#Override
public void onSuccess(Data data) {
fillCarListBox(data.getCars());
fillSchoolListBox(data.getSchools());
}
#Override
public void onFailure(Throwable caught) {
Window.alert("Pogreska...");
}
});
With this kind of approach you minimize the number of service calls on your client side. This not only creates a more readable code, but also usually speeds up the client side of your app. You should always try to minimize the number of service calls, ideally to a single one.
Concerning the more general question of collecting a number of asynchronous callbacks, a good approach is to use the Command Pattern. Gwt Remote Action is a library that provides an implementation of the mentioned pattern for doing RPC calls:
http://code.google.com/p/gwt-remote-action/