I'm fetching all rows from a DB2/400 table and returning them in a reactive request
My service look like this
package example.y2.api.service.impl;
import java.util.stream.Stream;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import example.y2.api.entity.impl.Y2ModelListEntry;
import example.y2.api.mapper.CoreMapper;
import example.y2.api.model.impl.ObjectListEntry;
import io.smallrye.mutiny.Multi;
#ApplicationScoped
public class Y2ListServiceAsync {
#Inject
CoreMapper mapper;
public Multi<ObjectListEntry> asyncAll() {
return Multi.createFrom().items(getStream()).map(this::map);
}
private Stream<Y2ModelListEntry> getStream() {
return Y2ModelListEntry.streamAll();
}
private ObjectListEntry map(Y2ModelListEntry y) {
ObjectListEntry listEntry = new ObjectListEntry();
mapper.directMap(listEntry, y);
return listEntry;
}
}
A Panache stream should be wrapped in a #Transactional method. I'm trying to understand if I can wrap a Panache stream when using the stream with a Multi.
This is my first attempt to write a reactive application.
There is no reactive JDBC driver in your app, right? Then streamAll() will block. It would work but it is kind of pointless because the blocking operation will block the worker thread this way.
Related
I am trying following Micronaut Kafka guide. It shows this piece of code:
Single<Book> sendBook(
#KafkaKey String author,
Single<Book> book
);
I tried implement whithout success that way
Producer
package com.tolearn.producer
import io.micronaut.configuration.kafka.annotation.KafkaClient
import io.micronaut.configuration.kafka.annotation.KafkaKey
import io.micronaut.configuration.kafka.annotation.Topic
import io.micronaut.messaging.annotation.Header
import io.reactivex.Single
#KafkaClient(
id = "demo-producer",
acks = KafkaClient.Acknowledge.ALL)
#Header(name = "X-Token", value = "\${my.application.token}")
public interface DemoProducer {
//Reactive and Non-Blocking
#Topic("demotopic")
fun sendDemoMsgReactive(
#KafkaKey key: String?,
msg: Single<String>): Single<String?>?
}
And call it from service layer by
package com.tolearn.service
import com.tolearn.producer.DemoProducer
import io.reactivex.Single
import io.reactivex.SingleOnSubscribe
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
#Singleton
class DemoService {
#Inject
#Named("dp")
lateinit var dp : DemoProducer
fun postDemo(key: String, msg: String){
//Reactive and No-blocking
val singleReturned:Single<String> = dp.sendDemoMsgReactive(key, SingleOnSubscribe<String> msg ).subscribe()
singleReturned.doOnSuccess{
print("ok")
}
singleReturned.doOnError ((e)->print(e))
}
}
Basically, what I want is post a message to kafka "no-blocking" style using io.reactivex.Single. I understand I must subscribe and then code two callbacks:onSuccess and onError. Certainly I am missing some basic concepts regard ReactiveX. Kindly, any clue will be appreciatted.
I'm helping my development team with some logging code in our framework.
using spring AOP I've created a groovy class called LoggingAspect. Its main purpose is to log method execution times for classes in com.zions.comon.services.logging directories and
annotated with #Loggable.
Since some classes already have #sl4j logging I need to detect if hat log member objects exists and use the built in #slf4j logging for that class. If it doesn't I need to execute the #sl4j annotation in aspect logging code.
The first statement in the try block will check if log member exists on object. If it does, then iLog will get set to incoming object's logger. However I'm not sure how to complete the rest of the code Once I detect the log member object. I don't expect anyone to write this code for me but would appreciate any suggestions/areas of researcoh on how to do this - such as using "if"
The logic should go something like:
Intercept and calculate method logging times in select classes
Check for existing log member object that indicates #slf4j is already present in class
If log member object exits use #sl4j logging features already built into that class
If log member object doesnt exist use #slf4j logging in logging Aspect code.
any help would be appreciated
"logging flow diagram"
Reverted code to original version - My LoggingAspect code looks like this at the moment
package com.zions.common.services.logging
import groovy.util.logging.Slf4j
import org.slf4j.Logger
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.EnableAspectJAutoProxy
#Aspect
#Configuration
#Slf4j
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class LoggingAspect {
*
* This is a Logging Aspect for the Loggable annotation that calculates method runtimes
* for all methods under classes annotated with #Loggable*/
/**
* Logs execution time of method under aspect.
*
* #param joinPoint - method under join
* #return actual return of method under join point.
* #throws Throwable
*/
#Around('execution (* *(..)) && !execution(* *.getMetaClass()) && #within(com.zions.common.services.logging.Loggable)')
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
def obj = joinPoint.this
Logger iLog = log
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
try {
/*First statement of try block attempts to test if log members exist on object.
If it does, then iLog will get set to incoming object's logger*/
obj.log.isInfoEnabled()
iLog = obj.log
} catch (e) {}
iLog.info("${joinPoint.getSignature()} executed in ${executionTime}ms");
return proceed;
}
}
If its helpful my logging Annotation is
package com.zions.common.services.logging
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/* Logging annotation to be used at class level
* Loggable annotation for all methods of a class annotated with the #Loggable annotation*/
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface Loggable {}
I've added a junit test class that validates when log member is found - The line 'iLog = obj.log' get's called from the LoggingAspect code and the test is PASSING.
LoggingAspectSpecification.groovy
package com.zions.common.services.logging
import static org.junit.Assert.*
import groovy.util.logging.Slf4j
import org.junit.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Profile
import org.springframework.context.annotation.PropertySource
import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification
#ContextConfiguration(classes=[LoggingAspectSpecificationConfig])
class LoggingAspectSpecification extends Specification {
#Autowired
SomeClass someClass
def 'Main flow for timing log'() {
setup: 'class to be logged'
when: 'execute something with class testing log'
someClass.methodOne()
someClass.methodTwo()
then: 'validate something logged'
true
}
}
#TestConfiguration
#Profile("test")
#ComponentScan(["com.zions.common.services.logging"])
#PropertySource("classpath:test.properties")
class LoggingAspectSpecificationConfig {
#Bean
SomeClass someClass() {
return new SomeClass()
}
}
#Loggable
#Slf4j
class SomeClass {
def methodOne() {
log.info('run methodOne')
}
def methodTwo() {
log.info('run methodTwo')
}
}
However my unit test is failing with classes that do not have #Slf4j meaning it will execute with the logger of the aspect instead of the pointcut object. The full error trace is:
groovy.lang.MissingPropertyException: No such property: log for class: com.zions.common.services.logging.SomeClass2
at com.zions.common.services.logging.SomeClass2.methodOne(LoggingAspectSpecification2.groovy:55)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:747)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89)
at com.zions.common.services.logging.LoggingAspect.logExecutionTime(LoggingAspect.groovy:42)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:643)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:632)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:174)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
at com.zions.common.services.logging.LoggingAspectSpecification2.Main flow for timing log(LoggingAspectSpecification2.groovy:27)
The second unit test code is below - (the only difference is that #Slf4j) is not present in the classes.
LoggingAspectSpecification2.groovy
package com.zions.common.services.logging
import static org.junit.Assert.*
import groovy.util.logging.Log
import groovy.util.logging.Slf4j
import org.junit.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Profile
import org.springframework.context.annotation.PropertySource
import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification
#ContextConfiguration(classes=[LoggingAspectSpecificationConfig2])
class LoggingAspectSpecification2 extends Specification {
#Autowired
SomeClass2 someClass2
def 'Main flow for timing log'() {
setup: 'class to be logged'
when: 'execute something with class testing log'
someClass2.methodOne()
someClass2.methodTwo()
then: 'validate something logged'
true
}
}
<!-- language: lang-groovy -->
#TestConfiguration
#Profile("test")
#ComponentScan(["com.zions.common.services.logging"])
#PropertySource("classpath:test.properties")
class LoggingAspectSpecificationConfig2 {
#Bean
SomeClass2 someClass2() {
return new SomeClass2()
}
}
<!-- language: lang-groovy -->
#Loggable
class SomeClass2 {
def methodOne() {
int x=10, y=20;
System.out.println(x+y+" testing the aspect logging code");
}
def methodTwo() {
int x=10, y=20;
System.out.println(x+y+" testing the aspect logging code");
}
}
I'm guessing something's wrong in my LoggingAspect code in the Try Catch block?
To resolve the error and get my unit test to pass without #Slf4j or #Log - I had to add a println statement to the SomeClass2 code as in,
int x=10, y=20;
System.out.println(x+y+" testing the apsect logging code");
adding #Log just gave it another built in log member similar to #Slf4j - adding the println statement and removing the #Log annotation force the LoggingAspect code to execute. Unit test is passing.
Before marking this as duplicate, just wanted you guys to know I have checked out the question posted here:
What is the difference between #PathParam and #PathVariable
Thing is, if the usage of PathParam and PathVariable are same (only that one is from the JAX-RS API and one is provided by Spring), why is it that using one gives me null and the other gives me the proper value?
I am using Postman to invoke the service as:
http://localhost:8080/topic/2
(I'm very new to SpringBoot)
Using PathParam :
import javax.websocket.server.PathParam;
import org.apache.tomcat.util.json.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class TopicController {
#Autowired
TopicService topicService;
#RequestMapping(method=RequestMethod.GET,path="/topic/{id}")
public Topic getById(#PathParam("id") long id) throws ParseException {
return topicService.getTopicById(id); //-- here id comes as null (when id is declared as a wrapper type - Long, else it throws an error)
}
}
Using PathVariable:
#RestController
public class TopicController {
#Autowired
TopicService topicService;
#RequestMapping(method=RequestMethod.GET,path="/topic/{id}")
public Topic getById(#PathVariable("id") long id) throws ParseException {
return topicService.getTopicById(id); //-- here id comes as 2
}
}
I think the pathparam in your project is under javax.ws... This one is not what they talked about. It is used in websocket, which means it is not a http annotaiton. JBoss implement pathparam needs additional jars.
I am using Flyway 5.0.5 and I am unable to create a java (SpringJdbcMigration) with autowired properties... They end up null.
The closest thing I can find is this question: Spring beans are not injected in flyway java based migration
The answer mentions it being fixed in Flyway 5 but the links are dead.
What am I missing?
I struggled with this for a long time due to my JPA dependency. I am going to edit the title of my question slightly to reflect this...
#Autowired beans are instantiated from the ApplicationContext. We can create a different bean that is ApplicationContextAware and use that to "manually wire" our beans for use in migrations.
A quite clean approach can be found here. Unfortunately, this throws an uncaught exception (specifically, ApplicationContext is null) when using JPA. Luckily, we can solve this by using the #DependsOn annotation and force flyway to run after the ApplicationContext has been set.
First we'll need the SpringUtility from avehlies/spring-beans-flyway2 above.
package com.mypackage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class SpringUtility implements ApplicationContextAware {
#Autowired
private static ApplicationContext applicationContext;
public void setApplicationContext(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/*
Get a class bean from the application context
*/
public static <T> T getBean(final Class clazz) {
return (T) applicationContext.getBean(clazz);
}
/*
Return the application context if necessary for anything else
*/
public static ApplicationContext getContext() {
return applicationContext;
}
}
Then, configure a flywayInitializer with a #DependsOn for springUtility. I extended the FlywayAutoConfiguration here hoping to keep the autoconfiguration functionality. This mostly seems to have worked for me, except that turning off flyway in my gradle.build file no longer works, so I had to add the #Profile("!integration") to prevent it from running during my tests. Other than that the autoconfiguration seems to work for me but admittedly I've only run one migration. Hopefully someone will correct me if I am wrong.
package com.mypackage;
import org.flywaydb.core.Flyway;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.DependsOn;
import com.mypackage.SpringUtility;
#Configuration
#Profile("!integration")
class MyFlywayConfiguration extends FlywayConfiguration {
#Primary
#Bean(name = "flywayInitializer")
#DependsOn("springUtility")
public FlywayMigrationInitializer flywayInitializer(Flyway flyway){
return super.flywayInitializer(flyway);
//return new FlywayMigrationInitializer(flyway, null);
}
}
And just to complete the example, here is a migration:
package db.migration;
import org.flywaydb.core.api.migration.spring.BaseSpringJdbcMigration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import com.mypackage.repository.AccountRepository;
import com.mypackage.domain.Account;
import com.mypackage.SpringUtility;
import java.util.List;
public class V2__account_name_ucase_firstname extends BaseSpringJdbcMigration {
private AccountRepository accountRepository = SpringUtility.getBean(AccountRepository.class);
public void migrate(JdbcTemplate jdbcTemplate) throws Exception {
List<Account> accounts = accountRepository.findAll();
for (Account account : accounts) {
String firstName = account.getFirstName();
account.setFirstName(firstName.substring(0, 1).toUpperCase() + firstName.substring(1));
account = accountRepository.save(account);
}
}
}
Thanks to avehlies on github, Andy Wilkinson on stack overflow and OldIMP on github for helping me along the way.
In case you are using more recent versions of Flyway, then extend BaseJavaMigration instead of BaseSpringJdbcMigration as the later is deprecated. Also, take a look at the below two comments by the user Wim Deblauwe.
The functionality hasn't made it into Flyway yet. It's being tracked by this issue. At the time of writing that issue is open and assigned to the 5.1.0 milestone.
Seems the updated answer provided by #mararn1618 is under documented on the official documentation, so I will provide a working setup here. Thanks to #mararn1618 for guiding in that direction.
Disclaimer, it's written in Kotlin :)
First you need a configuration for loading the migration classes, in Spring Boot (and perhaps Spring) you need either an implementation of FlywayConfigurationCustomizer or a setup of FlywayAutoConfiguration.FlywayConfiguration. Only the first is tested, but both should work
Configuration a, tested
import org.flywaydb.core.api.configuration.FluentConfiguration
import org.flywaydb.core.api.migration.JavaMigration
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.flyway.FlywayConfigurationCustomizer
import org.springframework.context.ApplicationContext
import org.springframework.stereotype.Component
#Component
class MyFlywayConfiguration #Autowired constructor(
val applicationContext: ApplicationContext
) : FlywayConfigurationCustomizer {
override fun customize(configuration: FluentConfiguration?) {
val migrationBeans = applicationContext.getBeansOfType(JavaMigration::class.java)
val migrationBeansAsArray = migrationBeans.values.toTypedArray()
configuration?.javaMigrations(*migrationBeansAsArray)
}
}
Configuration option B, untested, but should also work
import org.flywaydb.core.api.migration.JavaMigration
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
import org.springframework.boot.autoconfigure.flyway.FlywayConfigurationCustomizer
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
#Configuration
class MyFlywayConfiguration : FlywayAutoConfiguration.FlywayConfiguration() {
#Bean
fun flywayConfigurationCustomizer(applicationContext: ApplicationContext): FlywayConfigurationCustomizer {
return FlywayConfigurationCustomizer { flyway ->
val p = applicationContext.getBeansOfType(JavaMigration::class.java)
val v = p.values.toTypedArray()
flyway.javaMigrations(*v)
}
}
}
And with that you can just write your migrations as almost any other Spring bean:
import org.flywaydb.core.api.migration.BaseJavaMigration
import org.flywaydb.core.api.migration.Context
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
#Component
class V7_1__MyMigration #Autowired constructor(
) : BaseJavaMigration() {
override fun migrate(context: Context?) {
TODO("go crazy, mate, now you can import beans, but be aware of circular dependencies")
}
}
Side notes:
Be careful of circular dependencies, your migrations can most likely not depend on repositories (also makes sense, you are preparing them, after all)
Make sure your migrations are located where Spring scans for classes. So if you want to place them in the namespace db/migrations, you need to ensure that Spring scans that location
I haven't tested, but it's likely one should be cautious with mixing the path for these migrations and the locations where Flyway scans for migrations
Current flyway 6.5.5 version is released and back from 6.0.0 I believe support for spring beans is provided.
You can directly autowire spring beans into your Java based migrations (using #autowired), But the hunch is your Migration class also should be managed by Spring to resolve dependency.
There is a cool and simple way for it, by overriding default behavior of Flyway, check out https://reflectoring.io/database-migration-spring-boot-flyway/
the article clearly answers your question with code snippets.
If you are using deltaspike you can use BeanProvider to get a reference to your DAO.
Change your DAO code:
public static UserDao getInstance() {
return BeanProvider.getContextualReference(UserDao.class, false, new DaoLiteral());
}
Then in your migration method:
UserDao userdao = UserDao.getInstance();
And there you've got your reference.
(referenced from: Flyway Migration with java)
I implemented workflows, but it would be nice to know if there are hooks provided by the client library which allow to hook in. When a workflow was triggered and finished, a CQ.Notification should be displayed. Or do i need to implement a polling library by myself?
As far as I know, there is no built-in CQ area to see when something is done, aside from looking here:
http://yoursite.com:port/libs/cq/workflow/content/console.html
Once there, you can go to the 'Instances' tab and see what's happening.
In one application that I worked on, we ended up writing our own method that sends notifications to us based on one of our workflows (our workflow ties into it - from the workflow models area, you can set your process to be a servlet that you've put into CQ). Here is the main piece of code from our servlet that catches the process being active, and then calls our methods to email us based on what it finds:
import com.day.cq.workflow.WorkflowException;
import com.day.cq.workflow.WorkflowSession;
import com.day.cq.workflow.exec.WorkItem;
import com.day.cq.workflow.exec.WorkflowData;
import com.day.cq.workflow.exec.WorkflowProcess;
import com.day.cq.workflow.metadata.MetaDataMap;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.osgi.framework.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import java.util.Arrays;
public class YourServletName implements WorkflowProcess {
#Override
public void execute(WorkItem workItem, WorkflowSession workflowSession, MetaDataMap args) throws WorkflowException {
session = workflowSession.getSession();
final WorkflowData data = workItem.getWorkflowData();
String type = data.getPayloadType();
String[] argStrings = args.get("PROCESS_ARGS", ARG_UPDATED).split(",");
String reason = argStrings[0];
String baseUrl = argStrings[1];
try {
if (type.equals(TYPE_JCR_PATH) && data.getPayload() != null) {
String resourcePath = data.getPayload().toString();
logger.info("Send Notification that {} has been {}.", resourcePath, reason.toLowerCase());
if (resourcePath != null && !resourcePath.isEmpty()) {
ResourceInfo resourceInfo = new ResourceInfo(resourcePath, baseUrl);
sendEmail(resourceInfo, reason);
}
}
} catch (EmailException ex) {
logger.warn("Failed to send Email");
throw new WorkflowException(ex);
} catch (MailingException ex) {
logger.warn("Failed to send Email");
throw new WorkflowException(ex);
}
}
}
You can find more info in the documentation for Extending Workflow Functionality.
Look at the first code block on that page, and that will give you the best idea of how you can implement a custom workflow handler.
EDIT
If you want to see it on the front-end, you could do an AJAX call to get the JSON list of currently running workflows - you can hit this url:
http://localhost:4502/etc/workflow/instances.RUNNING.json
Then you could loop through them and see if yours is in there. This isn't very nice though, since they are all just listed by IDs. I would instead suggest using the querybuilder, or again, just doing an AJAX GET. This is one example:
1_group.0_path=/etc/workflow/instances
2_group.0_type=cq:Workflow
0_group.property.2_value=COMPLETED
0_group.property=status
0_group.property.and=true
3_group.property=modelId
3_group.property.2_value=/etc/workflow/models/your-model-name/jcr:content/model
3_group.property.and=true
Then the URL would look something like this:
http://yoursiteurl:port/libs/cq/search/content/querydebug.html?_charset_=UTF-8&query=http%3A%2F%2Fyoursiteurl%3Aport%3F%0D%0A1_group.0_path%3D%2Fetc%2Fworkflow%2Finstances%0D%0A2_group.0_type%3Dcq%3AWorkflow%0D%0A0_group.property.2_value%3DRUNNING%0D%0A0_group.property%3Dstatus%0D%0A0_group.property.and%3Dtrue%0D%0A3_group.property%3DmodelId%0D%0A3_group.property.2_value%3D%2Fetc%2Fworkflow%2Fmodels%2Fyour-model-name%2Fjcr%3Acontent%2Fmodel%0D%0A3_group.property.and%3Dtrue
It's ugly, but it gets you the results you need, and then you can parse them to get further information you need.