Integrate my app with Activiti - spring

I have one issue. I integrate my app with Activiti (in the same DB). When I insert, update or delete my entities (not entities's Activiti) by Dao class have use #Transactional but nothing about is being saved to database with no exception.
Here is my config to integrating:
#Configuration
public class ActivitiEngineConfiguration {
private final Logger log = LoggerFactory.getLogger(ActivitiEngineConfiguration.class);
#Autowired
protected Environment environment;
#Bean
public DataSource dataSource() {
SimpleDriverDataSource ds = new SimpleDriverDataSource();
try {
#SuppressWarnings("unchecked")
Class<? extends Driver> driverClass = (Class<? extends Driver>) Class.forName(environment.getProperty("jdbc.driver", "org.postgresql.Driver"));
ds.setDriverClass(driverClass);
} catch (Exception e) {
log.error("Error loading driver class", e);
}
ds.setUrl(environment.getProperty("spring.datasource.url"));
ds.setUsername(environment.getProperty("spring.datasource.username"));
ds.setPassword(environment.getProperty("spring.datasource.password"));
return ds;
}
#Bean(name = "transactionManager")
public PlatformTransactionManager annotationDrivenTransactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
System.out.println("transactionManager: "+transactionManager);
return transactionManager;
}
#Bean(name="processEngineFactoryBean")
public ProcessEngineFactoryBean processEngineFactoryBean() {
ProcessEngineFactoryBean factoryBean = new ProcessEngineFactoryBean();
factoryBean.setProcessEngineConfiguration(processEngineConfiguration());
return factoryBean;
}
#Bean(name="processEngine")
public ProcessEngine processEngine() {
// Safe to call the getObject() on the #Bean annotated processEngineFactoryBean(), will be
// the fully initialized object instanced from the factory and will NOT be created more than once
try {
return processEngineFactoryBean().getObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#Bean(name="processEngineConfiguration")
public ProcessEngineConfigurationImpl processEngineConfiguration() {
SpringProcessEngineConfiguration processEngineConfiguration = new SpringProcessEngineConfiguration();
processEngineConfiguration.setDataSource(dataSource());
processEngineConfiguration.setDatabaseSchemaUpdate(environment.getProperty("engine.schema.update", "true"));
processEngineConfiguration.setTransactionManager(annotationDrivenTransactionManager());
processEngineConfiguration.setJobExecutorActivate(Boolean.valueOf(
environment.getProperty("engine.activate.jobexecutor", "false")));
processEngineConfiguration.setAsyncExecutorEnabled(Boolean.valueOf(
environment.getProperty("engine.asyncexecutor.enabled", "true")));
processEngineConfiguration.setAsyncExecutorActivate(Boolean.valueOf(
environment.getProperty("engine.asyncexecutor.activate", "true")));
processEngineConfiguration.setHistory(environment.getProperty("engine.history.level", "full"));
String mailEnabled = environment.getProperty("engine.email.enabled");
if ("true".equals(mailEnabled)) {
processEngineConfiguration.setMailServerHost(environment.getProperty("engine.email.host"));
int emailPort = 1025;
String emailPortProperty = environment.getProperty("engine.email.port");
if (StringUtils.isNotEmpty(emailPortProperty)) {
emailPort = Integer.valueOf(emailPortProperty);
}
processEngineConfiguration.setMailServerPort(emailPort);
String emailUsernameProperty = environment.getProperty("engine.email.username");
if (StringUtils.isNotEmpty(emailUsernameProperty)) {
processEngineConfiguration.setMailServerUsername(emailUsernameProperty);
}
String emailPasswordProperty = environment.getProperty("engine.email.password");
if (StringUtils.isNotEmpty(emailPasswordProperty)) {
processEngineConfiguration.setMailServerPassword(emailPasswordProperty);
}
}
// List<AbstractFormType> formTypes = new ArrayList<AbstractFormType>();
// formTypes.add(new UserFormType());
// formTypes.add(new ProcessDefinitionFormType());
// formTypes.add(new MonthFormType());
// processEngineConfiguration.setCustomFormTypes(formTypes);
return processEngineConfiguration;
}
#Bean
public RepositoryService repositoryService() {
return processEngine().getRepositoryService();
}
#Bean
public RuntimeService runtimeService() {
return processEngine().getRuntimeService();
}
#Bean
public TaskService taskService() {
return processEngine().getTaskService();
}
#Bean
public HistoryService historyService() {
return processEngine().getHistoryService();
}
#Bean
public FormService formService() {
return processEngine().getFormService();
}
#Bean
public IdentityService identityService() {
return processEngine().getIdentityService();
}
#Bean
public ManagementService managementService() {
return processEngine().getManagementService();
}
}
DAO Layer:
#Autowired
private SessionFactory sessionFactory;
#Override
#Transactional
public void save(MyEntity obj) {
sessionFactory.getCurrentSession().saveOrUpdate(loaiDanhMuc);
}
Thank all!

I guess #Transactional is not working in this case
Please check following things below:
Check if <tx:annotation-driven /> is defined in your spring xml file.

Related

AbstractRoutingDataSource - unable to change datasource dynamically

I am using AbstractRoutingDataSource so as to have multiple data base connections but, even after changing the contextHoler the database remains same
in a single API call it works perfectly fine but if i change the datasource within the same api call it doesn't change the datasource. if i have 2 api calls with different contextHolder it would work
#GetMapping("/getdata2")
public ClaimAmountAndTCD getDetails2() {
ClaimAmountAndTCD claimAmountAndTCD2 = serviceCls.getBook2Data();
System.out.println(claimAmountAndTCD2);
return claimAmountAndTCD2;
}
#GetMapping("/getdata")
public ClaimAmountAndTCD getDetails() {
ClaimAmountAndTCD claimAmountAndTCD = serviceCls.getBook1Data();
// ClaimAmountAndTCD claimAmountAndTCD2 = serviceCls.getBook2Data();
System.out.println(claimAmountAndTCD);
return claimAmountAndTCD;
}
below is my database config code
#Configuration
#Profile("dev")
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "as400EntityManager", transactionManagerRef = "as400TransactionManager", basePackages = {
"com.spring.demo.repository" })
public class DevDataSourceConfig {
#Value("${db.gmrp.sxcd1.servername}")
private String servername;
#Value("${db.gmrp.sxcd1.username}")
private String username;
#Value("${db.gmrp.sxcd1.password}")
private String password;
#Value("${db.gmrp.sxcd1.libraries}")
private String libraries;
#Value("${db.naming}")
private String dbNaming;
#Value("${db.driver}")
private String driver;
#Value("${db.isolation}")
private String dbIsolation;
#Bean
public AS400JDBCDataSource dataSourceDevSXCD1() {
AS400JDBCDataSource as400DataSource = new AS400JDBCDataSource();
System.out.println(driver + " " + libraries + " " + username + " " + password);
as400DataSource.setDriver(driver);
as400DataSource.setLibraries(libraries);
as400DataSource.setServerName(servername);
as400DataSource.setNaming(dbNaming);
as400DataSource.setTransactionIsolation(dbIsolation);
if (password != null && !password.isEmpty() && password.trim().length() > 0) {
as400DataSource.setUser(username);
as400DataSource.setPassword(password);
}
return as400DataSource;
}
#Bean(name = "as400dev")
#Primary
public DataSource dataSource() {
AbstractRoutingDataSource dataSource = new RoutingDataSource();
Map<Object, Object> resolvedDataSources = new HashMap<>();
resolvedDataSources.put(DbType.DEVSXCD1, dataSourceDevSXCD1());
dataSource.setDefaultTargetDataSource(dataSourceDevSXCD1()); // default
dataSource.setTargetDataSources(resolvedDataSources);
return dataSource;
}
#Bean(name = "as400EntityManager")
#Primary
public LocalContainerEntityManagerFactoryBean as400DevEntityManager(EntityManagerFactoryBuilder builder,
#Qualifier("as400dev") DataSource as400DataSource) {
return builder.dataSource(as400DataSource).packages("com.spring.demo.entity").build();
}
#Bean(name = "as400TransactionManager")
public PlatformTransactionManager as400DevTransactionManager(
#Qualifier("as400EntityManager") EntityManagerFactory entityManager) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManager);
return transactionManager;
}
}
DbContextHoler class
package com.spring.demo.config;
public final class DbContextHolder {
private DbContextHolder() {
// default constructor
}
private static final ThreadLocal<DbType> contextHolder = new ThreadLocal<>();
// set the data source
public static void setDbType(DbType dbType) {
if (dbType == null) {
throw new NullPointerException();
}
contextHolder.set(dbType);
}
// get the current data source in use
public static DbType getDbType() {
return contextHolder.get();
}
// clear data source
public static void clearDbType() {
contextHolder.remove();
}
}
AbstractRoutingDataSource
package com.spring.demo.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class RoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}

Bean not getting overridden in Spring boot

I am trying to write and test an application that used spring-cloud with azure functions following this tutorial.
https://github.com/markusgulden/aws-tutorials/tree/master/spring-cloud-function/spring-cloud-function-azure/src/main/java/de/margul/awstutorials/springcloudfunction/azure
I am tryign to write a testcase and override the bean.
Here is the application class having function and handler Bean function.
#SpringBootApplication
#ComponentScan(basePackages = { "com.package" })
public class DataFunctions extends AzureSpringBootRequestHandler<GenericMessage<Optional<String>>, Data> {
#FunctionName("addData")
public HttpResponseMessage addDataRun(
#HttpTrigger(name = "add", methods = {
HttpMethod.POST }, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) throws JsonParseException, JsonMappingException, IOException {
context.getLogger().info("Java HTTP trigger processed a POST request.");
try {
handleRequest(new GenericMessage<Optional<String>>(request.getBody()), context);
} catch (ServiceException ex) {
ErrorMessage em = new ErrorMessage();
return request.createResponseBuilder(handleException(ex, em)).body(em).build();
}
return request.createResponseBuilder(HttpStatus.CREATED).build();
}
#Autowired
MyService mService;
#Bean
public Consumer<GenericMessage<Optional<String>>> addData() {
ObjectMapper mapper = new ObjectMapper();
return req -> {
SomeModel fp = null;
try {
fp = mapper.readValue(req.getPayload().get(), SomeModel.class);
} catch (Exception e) {
throw new ServiceException(e);
}
mService.addData(fp);
};
}
}
I want to test by overriding the above bean.
Cosmosdb spring configuration
#Configuration
#EnableDocumentDbRepositories
public class CosmosDBConfig extends AbstractDocumentDbConfiguration {
#Value("${cosmosdb.collection.endpoint}")
private String uri;
#Value("${cosmosdb.collection.key}")
private String key;
#Value("${cosmosdb.collection.dbname}")
private String dbName;
#Value("${cosmosdb.connect.directly}")
private Boolean connectDirectly;
#Override
public DocumentDBConfig getConfig() {
ConnectionPolicy cp = ConnectionPolicy.GetDefault();
if (connectDirectly) {
cp.setConnectionMode(ConnectionMode.DirectHttps);
} else {
cp.setConnectionMode(ConnectionMode.Gateway);
}
return DocumentDBConfig.builder(uri, key, dbName).connectionPolicy(cp).build();
}
}
Here is the configuration
#TestConfiguration
#PropertySource(value = "classpath:application.properties", encoding = "UTF-8")
#Profile("test")
#Import({DataFunctions.class})
public class TestConfig {
#Bean(name="addData")
#Primary
public Consumer<GenericMessage<Optional<String>>> addData() {
return req -> {
System.out.println("data mock");
};
}
#Bean
#Primary
public DocumentDBConfig getConfig() {
return Mockito.mock(DocumentDBConfig.class);
}
}
Finally the test class
#RunWith(SpringRunner.class)
//#SpringBootTest //Enabling this gives initialization error.
#ActiveProfiles("test")
public class TempTest {
#InjectMocks
DataFunctions func;
#Mock
MyService mService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
private Optional<String> createRequestString(final String res) throws IOException {
InputStream iStream = TempTest.class.getResourceAsStream(res);
String charset="UTF-8";
try (BufferedReader br = new BufferedReader(new InputStreamReader(iStream, charset))) {
return Optional.of(br.lines().collect(Collectors.joining(System.lineSeparator())));
}
}
#Test
public void testHttpPostTriggerJava() throws Exception {
#SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
final Optional<String> queryBody = createRequestString("/test-data.json");
doNothing().when(mService).addData(Mockito.any(SomeModel.class));
doReturn(queryBody).when(req).getBody();
doAnswer(new Answer<HttpResponseMessage.Builder>() {
#Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
final ExecutionContext context = mock(ExecutionContext.class);
doReturn(Logger.getGlobal()).when(context).getLogger();
doReturn("addData").when(context).getFunctionName();
// Invoke
final HttpResponseMessage ret = func.addDataRun(req, context);
// Verify
assertEquals(ret.getStatus(), HttpStatus.CREATED);
}
}
For this case instead of test configuration addData the actual bean is called from DataFunctions class. Also the database connection is also created when it should use the mocked bean from my test configuration. Can somebody please point out what is wrong in my test configuration?
I was able to resolve the first part of cosmos db config loading by marking it with
#Configuration
#EnableDocumentDbRepositories
#Profile("!test")
public class CosmosDBConfig extends AbstractDocumentDbConfiguration {
...
}
Also had to mark the repository bean as optional in the service.
public class MyService {
#Autowired(required = false)
private MyRepository myRepo;
}
Didn't use any spring boot configuration other than this.
#ActiveProfiles("test")
public class FunctionTest {
...
}
For the second part of providing mock version of Mock handlers, I simply made the test config file as spring application as below.
#SpringBootApplication
#ComponentScan(basePackages = { "com.boeing.da.helix.utm.traffic" })
#Profile("test")
public class TestConfiguration {
public static void main(final String[] args) {
SpringApplication.run(TestConfiguration.class, args);
}
#Bean(name="addData")
#Primary
public Consumer<GenericMessage<Optional<String>>> addData() {
return req -> {
System.out.println("data mock");
};
}
}
and made use of this constructor from azure functions library in spring cloud in my constructor
public class AppFunctions
extends AzureSpringBootRequestHandler<GenericMessage<Optional<String>>, List<Data>> {
public AppFunctions(Class<?> configurationClass) {
super(configurationClass);
}
}
public AzureSpringBootRequestHandler(Class<?> configurationClass) {
super(configurationClass);
}
Hope it helps someone.

When JMS updating record using JPA/Hibernate — TransactionRequiredException: No Transaction is in progress error

What we are doing: Update subscription for received business ID.
We receive business ID on activeMQ queue.
JMSListener picked business ID, find the all associated subscription for received business.
Process all subscription identify who need update (can be zero or more than one).
For each subscription we call service method udate(SubscriptionDTO subscriptionDTO). Find the subscription entity object. This update method update the entity object using dto object. Then we call flush method on it. Note : Update method is annotated with #transactional. Call flush method from service#udpate(dto) method.
We observed different behavior in windows vs Linux. On windows it worked well but on Linux we are getting failure after execution of flush method– javax.persistence.TransactionRequiredException: no transaction is in progress
We tried adding #Transactional on JMSListner but got same error on Linux. But when we tried without call of flush method error gone but no update happened. Subscription data is same as before.
Not sure how good problem was explained above.
Looking for guidance, much appreciated for response.
JPA Configuration
package edu.learn.config;
#Configuration
#EnableJpaRepositories(basePackages = "edu.learn.repositories", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
#EnableTransactionManagement
public class JpaConfiguration
{
private final String DEFAULT_TEST_QUERY = "SELECT 1";
#Autowired
private Environment environment;
#Value("${datasource.maxPoolSize:10}")
private int maxPoolSize;
/*
* Populate SpringBoot DataSourceProperties object directly from
* application.yml based on prefix.Thanks to .yml, Hierachical data is
* mapped out of the box with matching-name properties of
* DataSourceProperties object].
*/
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource")
public DataSourceProperties dataSourceProperties()
{
return new DataSourceProperties();
}
/*
* Configure HikariCP pooled DataSource.
*/
#Bean
public DataSource dataSource()
{
DataSourceProperties dataSourceProperties = dataSourceProperties();
HikariDataSource dataSource = (HikariDataSource) DataSourceBuilder.create()
.type(com.zaxxer.hikari.HikariDataSource.class).username(dataSourceProperties.getUsername())
.password(dataSourceProperties.getPassword()).build();
dataSource.setMaximumPoolSize(PropertyUtil.getMaxPoolSize(environment));
dataSource.setInitializationFailFast(PropertyUtil.getInitializationFailFast(environment));
dataSource.setMinimumIdle(PropertyUtil.getMinIdle(environment));
dataSource.setConnectionTestQuery(DEFAULT_TEST_QUERY);
dataSource.setConnectionTimeout(PropertyUtil.getConnectionTimeout(environment));
dataSource.setIdleTimeout(PropertyUtil.getIdleTimeout(environment));
dataSource.setMaxLifetime(PropertyUtil.getMaxLifetime(environment));
dataSource.setDataSourceClassName(PropertyUtil.getDataSourceClassName(environment));
if ("com.mysql.jdbc.jdbc2.optional.MysqlDataSource".equals(PropertyUtil.getDataSourceClassName(environment)))
{
dataSource.addDataSourceProperty("databaseName", PropertyUtil.defaultSchema(environment));
dataSource.addDataSourceProperty("cachePrepStmts", PropertyUtil.getCachePrepStmts(environment));
dataSource.addDataSourceProperty("prepStmtCacheSize", PropertyUtil.getPrepStmtCacheSize(environment));
dataSource.addDataSourceProperty(
"prepStmtCacheSqlLimit", PropertyUtil.getPrepStmtCacheSqlLimit(environment)
);
dataSource.addDataSourceProperty("useServerPrepStmts", PropertyUtil.getUseServerPrepStmts(environment));
dataSource.addDataSourceProperty("serverName", PropertyUtil.dbServerName(environment));
dataSource.addDataSourceProperty("portNumber", PropertyUtil.portNumber(environment));
}
return dataSource;
}
/*
* Entity Manager Factory setup.
*/
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException
{
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[]
{
"edu.learn.model"
});
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
factoryBean.setJpaProperties(jpaProperties());
return factoryBean;
}
/*
* Provider specific adapter.
*/
#Bean
public JpaVendorAdapter jpaVendorAdapter()
{
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
return hibernateJpaVendorAdapter;
}
/*
* Here you can specify any provider specific properties.
*/
private Properties jpaProperties()
{
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.globally_quoted_identifiers", "true");
properties.put("hibernate.hbm2ddl.auto", "validates");
properties.put("ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
properties.put("show_sql", "false");
properties.put("format_sql", "true");
return properties;
}
#Bean
#Autowired
public PlatformTransactionManager transactionManager(EntityManagerFactory emf)
{
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(emf);
return txManager;
}
static class PropertyUtil
{
final static String DEFAULT_CHARACTER_ENCODING = "utf-8";
final static String DEFAULT_DATASOURCE_CLASS = "com.mysql.jdbc.Driver";
public static boolean getCachePrepStmts(final Environment environment)
{
String cachePrepStmts = environment.getProperty("spring.datasource.hikari.cachePrepStmts");
if ("false".equalsIgnoreCase(cachePrepStmts))
{
return false;
}
return true;
}
public static int getPrepStmtCacheSize(final Environment environment)
{
String prepStmtCacheSize = environment.getProperty("spring.datasource.hikari.prepStmtCacheSize");
try
{
return Integer.parseInt(prepStmtCacheSize);
}
catch(Exception e)
{
return 250;
}
}
public static int getPrepStmtCacheSqlLimit(final Environment environment)
{
String prepStmtCacheSqlLimit = environment.getProperty("spring.datasource.hikari.prepStmtCacheSqlLimit");
try
{
return Integer.parseInt(prepStmtCacheSqlLimit);
}
catch(Exception e)
{
return 2048;
}
}
public static boolean getUseServerPrepStmts(final Environment environment)
{
String useServerPrepStmts = environment.getProperty("spring.datasource.hikari.useServerPrepStmts");
if ("false".equalsIgnoreCase(useServerPrepStmts))
{
return false;
}
return true;
}
public static boolean getInitializationFailFast(final Environment environment)
{
String initializationFailFast = environment.getProperty("spring.datasource.hikari.initializationFailFast");
if ("false".equalsIgnoreCase(initializationFailFast))
{
return false;
}
return true;
}
public static long getConnectionTimeout(final Environment environment)
{
String connectionTimeout = environment.getProperty("spring.datasource.hikari.connectionTimeout");
try
{
return Integer.parseInt(connectionTimeout);
}
catch(Exception e)
{
return TimeUnit.SECONDS.toMillis(60);
}
}
public static long getIdleTimeout(final Environment environment)
{
String idleTimeout = environment.getProperty("spring.datasource.hikari.idleTimeout");
try
{
return Integer.parseInt(idleTimeout);
}
catch(Exception e)
{
return TimeUnit.SECONDS.toMillis(60);
}
}
public static long getMaxLifetime(final Environment environment)
{
String maxLifetime = environment.getProperty("spring.datasource.hikari.maxLifetime");
try
{
return Integer.parseInt(maxLifetime);
}
catch(Exception e)
{
return TimeUnit.SECONDS.toMillis(90);
}
}
public static int getMinIdle(final Environment environment)
{
String minIdle = environment.getProperty("spring.datasource.hikari.minIdle");
try
{
return Integer.parseInt(minIdle);
}
catch(Exception e)
{
return 5;
}
}
public static int getMaxPoolSize(final Environment environment)
{
String maxPoolSize = environment.getProperty("spring.datasource.maxPoolSize");
try
{
return Integer.parseInt(maxPoolSize);
}
catch(Exception e)
{
return 25;
}
}
public static String getDataSourceClassName(final Environment environment)
{
String dataSourceClassName = environment.getProperty("spring.datasource.dataSourceClassName");
if (dataSourceClassName != null && "".equalsIgnoreCase(dataSourceClassName.trim()) == false)
{
return dataSourceClassName;
}
return DEFAULT_DATASOURCE_CLASS;
}
public static String getCharacterEncoding(final Environment environment)
{
String characterEncoding = environment.getProperty("spring.datasource.characterEncoding");
if (characterEncoding != null && "".equalsIgnoreCase(characterEncoding.trim()) == false)
{
return characterEncoding;
}
return DEFAULT_CHARACTER_ENCODING;
}
public static boolean getUniCode(final Environment environment)
{
String useUnicode = environment.getProperty("spring.datasource.useUnicode");
if ("false".equalsIgnoreCase(useUnicode))
{
return false;
}
return true;
}
public static String showSQL(final Environment environment)
{
String showSQL = environment.getProperty("spring.datasource.hibernate.showSQL");
if ("false".equalsIgnoreCase(showSQL))
{
return "false";
}
return "true";
}
public static String formatSQL(final Environment environment)
{
String formatSQL = environment.getProperty("spring.datasource.hibernate.format_sql");
if ("false".equalsIgnoreCase(formatSQL))
{
return "false";
}
return "true";
}
public static String dbServerName(final Environment environment)
{
String dbServerName = environment.getProperty("spring.datasource.serverName");
if (dbServerName == null || "".equalsIgnoreCase(dbServerName.trim()))
{
return "localhost";
}
return dbServerName;
}
public static int portNumber(final Environment environment)
{
String portNumber = environment.getProperty("spring.datasource.portNumber");
try
{
return Integer.parseInt(portNumber);
}
catch(Exception e)
{
return 3306;
}
}
public static String defaultSchema(final Environment environment)
{
String defaultSchema = environment.getProperty("spring.datasource.defaultSchema");
if (defaultSchema == null || "".equalsIgnoreCase(defaultSchema.trim()))
{
return "subscription";
}
return defaultSchema;
}
}
}
JMS Configuration
#EnableJms
#Configuration
public class JmsConfiguration
{
#Bean
public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer)
{
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
return factory;
}
}
Startup class
#Import({JpaConfiguration.class, JmsConfiguration.class})
#SpringBootApplication(scanBasePackages={"edu.learn"})
public class SubscriptionApiApplication
{
public static void main(String[] args)
{
SpringApplication.run(SubscriptionApiApplication.class, args);
}
}
JMS queue listener
#Component
public class OrderTransactionReceiver
{
private static final Logger LOGGER = LoggerFactory.getLogger(OrderTransactionReceiver.class);
#Autowired
private SubscriptionService subsriptionService;
#JmsListener(destination = "OrderTransactionQueue", containerFactory = "myFactory")
public void receiveMessage(String businessID)
{
List<SubscriptionDTO> subscriptions = subsriptionService.findByBusinessID(businessID);
for (SubscriptionDTO subscription : subscriptions)
{
subscription.setStatus("active");
subsriptionService.update(subscription);
}
}
}
Service
#Service
class RepositorySubscriptionService implements SubscriptionService
{
private static final Logger LOGGER = LoggerFactory.getLogger(RepositorySubscriptionService.class);
private final SubscriptionRepository repository;
#Transactional
#Override
public SubscriptionDTO update(SubscriptionDTO updatedSubscriptionEntry)
{
LOGGER.info(
"Updating the information of a subscription entry by using information: {}", updatedSubscriptionEntry
);
SubscriptionEntity updated = findSubscriptionEntryById(updatedSubscriptionEntry.getId());
updated.update(updatedSubscriptionEntry.getStatus());
// We need to flush the changes or otherwise the returned object
// doesn't contain the updated audit information.
repository.flush();
LOGGER.info("Updated the information of the subscription entry: {}", updated);
return SubscriptionMapper.mapEntityIntoDTO(updated);
}
}
Repostiory
public interface SubscriptionRepository extends
Repository<SubscriptionEntity, String>,
JpaSpecificationExecutor<SubscriptionEntity>
{
void delete(SubscriptionEntity deleted);
List<SubscriptionEntity> findAll();
Optional<SubscriptionEntity> findOne(String id);
List<SubscriptionEntity> findByBusinessID(final String businessID);
void flush();
SubscriptionEntity save(SubscriptionEntity persisted);
}
JTA transaction configuration
#Configuration
#EnableJpaRepositories(basePackages = "edu.learn.repositories", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
#EnableTransactionManagement
public class JpaConfiguration
{
private final String DEFAULT_TEST_QUERY = "SELECT 1";
#Autowired
private Environment environment;
#Value("${datasource.maxPoolSize:10}")
private int maxPoolSize;
/*
* Populate SpringBoot DataSourceProperties object directly from
* application.yml based on prefix.Thanks to .yml, Hierachical data is
* mapped out of the box with matching-name properties of
* DataSourceProperties object].
*/
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource")
public DataSourceProperties dataSourceProperties()
{
return new DataSourceProperties();
}
/*
* Configure HikariCP pooled DataSource.
*/
#Bean(initMethod = "init", destroyMethod = "close")
public DataSource dataSource()
{
DataSourceProperties dataSourceProperties = dataSourceProperties();
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setServerName(PropertyUtil.dbServerName(environment));
mysqlXaDataSource.setPort(PropertyUtil.portNumber(environment));
mysqlXaDataSource.setUser(dataSourceProperties.getUsername());
mysqlXaDataSource.setPassword(dataSourceProperties.getPassword());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setDatabaseName(PropertyUtil.defaultSchema(environment));
mysqlXaDataSource.setCachePreparedStatements(PropertyUtil.getCachePrepStmts(environment));
try
{
mysqlXaDataSource.setPrepStmtCacheSqlLimit(PropertyUtil.getPrepStmtCacheSqlLimit(environment));
mysqlXaDataSource.setPrepStmtCacheSize(PropertyUtil.getPrepStmtCacheSize(environment));
}
catch (SQLException e)
{
e.printStackTrace();
}
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("xaSubscription");
xaDataSource.setTestQuery(DEFAULT_TEST_QUERY);
xaDataSource.setMaxPoolSize(PropertyUtil.getMaxPoolSize(environment));
xaDataSource.setMaxIdleTime(PropertyUtil.getMinIdle(environment));
xaDataSource.setMaxLifetime((int)PropertyUtil.getMaxLifetime(environment));
xaDataSource.setBorrowConnectionTimeout((int)PropertyUtil.getConnectionTimeout(environment));
return xaDataSource;
}
/*
* Entity Manager Factory setup.
*/
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException
{
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[]
{
"edu.learn.model"
});
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
factoryBean.setJpaProperties(jpaProperties());
return factoryBean;
}
/*
* Provider specific adapter.
*/
#Bean
public JpaVendorAdapter jpaVendorAdapter()
{
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
return hibernateJpaVendorAdapter;
}
/*
* Here you can specify any provider specific properties.
*/
private Properties jpaProperties()
{
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.globally_quoted_identifiers", "true");
properties.put("hibernate.hbm2ddl.auto", "validates");
properties.put("ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
properties.put("show_sql", "false");
properties.put("format_sql", "true");
properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
properties.put("javax.persistence.transactionType", "JTA");
return properties;
}
#Bean
public UserTransaction userTransaction() throws Throwable
{
UserTransactionImp userTransactionImp = new UserTransactionImp();
userTransactionImp.setTransactionTimeout(1000);
return userTransactionImp;
}
#Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public TransactionManager atomikosTransactionManager() throws Throwable
{
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);
AtomikosJtaPlatform.transactionManager = userTransactionManager;
return userTransactionManager;
}
#Bean(name = "transactionManager")
#DependsOn({ "userTransaction", "atomikosTransactionManager" })
public PlatformTransactionManager transactionManager() throws Throwable
{
UserTransaction userTransaction = userTransaction();
AtomikosJtaPlatform.transaction = userTransaction;
TransactionManager atomikosTransactionManager = atomikosTransactionManager();
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}
}
public class AtomikosJtaPlatform extends AbstractJtaPlatform
{
private static final long serialVersionUID = 1L;
static TransactionManager transactionManager;
static UserTransaction transaction;
#Override
protected TransactionManager locateTransactionManager()
{
return transactionManager;
}
#Override
protected UserTransaction locateUserTransaction()
{
return transaction;
}
}

Implementing Snapshot in AXON 3.0 : Aggregate Type is unknown in this snapShotter

I'm still new to the axon frame work.
I'm trying to implement snapshotting using mongodb in my application and I keep on getting an error saying
"AbstractSnapshotter : An attempt to create and store a snapshot resulted in an exception. Exception summary: Aggregate Type is unknown in this snapshotter: com.myworklife.contacts.domain.contact.Contact"
This is a part of my java config file.
#Bean
public AggregateSnapshotter snapShotter(EventStore eventStore, AggregateFactory<Contact> contactAggregateFactory) {
return new AggregateSnapshotter(eventStore);
}
#Bean
public SnapshotTriggerDefinition snapshotTriggerDefinition(Snapshotter snapShotter) throws Exception {
return new EventCountSnapshotTriggerDefinition(snapShotter, 1);
}
#Bean
public EventStore eventStore(EventStorageEngine eventStorageEngine) {
return new EmbeddedEventStore(eventStorageEngine);
}
#Bean
public Repository<Contact> contactAggregateRepository(EventStore eventStore, SnapshotTriggerDefinition snapshotTriggerDefinition) {
return new ContactRepository(eventStore, snapshotTriggerDefinition);
}
And my repository.
#Repository("ContactRepository")
public class ContactRepository extends EventSourcingRepository<Contact> {
#Autowired
public ContactRepository(EventStore eventStore, SnapshotTriggerDefinition snapshotTriggerDefinition) {
super(Contact.class, eventStore, snapshotTriggerDefinition);
}
public Contact findContact(ContactId contactId) {
return load(contactId.toString()).getWrappedAggregate().getAggregateRoot();
}
}
My aggregate.
#Aggregate(repository="contactAggregateRepository")
public class Contact {
#AggregateIdentifier
private ContactId id;
private String name;
private String mobileNumber;
public Contact() {
// do nothing, Axon requires default constructor
}
#CommandHandler
public Contact(CreateContactCommand createContactCommand) {
apply(new ContactHasBeenCreatedEvent(createContactCommand.getContactId(), createContactCommand.getName(),
createContactCommand.getMobileNumber()));
}
}
Is there something I'm doing wrong?
since I'm getting an error saying "An attempt to create and store a snapshot resulted in an exception. Exception summary: Aggregate Type is unknown in this snapshotter: com.myworklife.contacts.domain.contact.Contact"
Any help will be highly appreciated.
Thanks,
Pat
You need to add the contactAggregateFactory to the constructor of the AggregateSnapshotter in the snapShotter bean:
#Bean
public AggregateSnapshotter snapShotter(EventStore eventStore, AggregateFactory<Contact> contactAggregateFactory) {
return new AggregateSnapshotter(eventStore, contactAggregateFactory);
}
1.depencity jar
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring-boot-autoconfigure</artifactId>
<version>${axon.version}</version>
</dependency>
2.At first,you should config your application.ymp or bootstrap.yml,like this:spring:
data:
mongodb:
host: localhost
port: 27017
database: axonframework
events: domainevents
snapshots: snapshotevents
3.config your mongoDB:
#Bean(name = "axonMongoTemplate")
public MongoTemplate axonMongoTemplate() {
MongoTemplate template = new DefaultMongoTemplate(mongoClient(), mongoDbName, eventsCollectionName, snapshotCollectionName);
return template;
}
#Bean
public MongoClient mongoClient(){
MongoFactory mongoFactory = new MongoFactory();
mongoFactory.setMongoAddresses(Arrays.asList(new ServerAddress(mongoHost)));
return mongoFactory.createMongo();
}
#Bean
public EventStorageEngine eventStorageEngine(Serializer serializer){
return new MongoEventStorageEngine(
serializer,null, axonMongoTemplate(), new DocumentPerEventStorageStrategy());
4.config repository for your aggregate,for example,i config a Element Aggreaget's repository
#Configuration
public class ElementConfiguration {
#Autowired
private EventStore eventStore;
#Bean
#Scope("prototype")
public Element elementAggregate() {
return new Element();
}
#Bean
public AggregateFactory<Element> elementAggregateAggregateFactory() {
SpringPrototypeAggregateFactory<Element> aggregateFactory = new SpringPrototypeAggregateFactory<>();
aggregateFactory.setPrototypeBeanName("elementAggregate");
return aggregateFactory;
}
#Bean
public SpringAggregateSnapshotterFactoryBean springAggregateSnapshotterFactoryBean(){
SpringAggregateSnapshotterFactoryBean factory = new SpringAggregateSnapshotterFactoryBean();
return factory;
}
#Bean
public Repository<Element> elementAggregateRepository(Snapshotter snapshotter) {
EventCountSnapshotTriggerDefinition eventCountSnapshotTriggerDefinition = new EventCountSnapshotTriggerDefinition(snapshotter, 3);
EventSourcingRepository<Element> repository = new EventSourcingRepository<Element>(
elementAggregateAggregateFactory(),
eventStore,
eventCountSnapshotTriggerDefinition
);
return repository;
}
5.enjoy
If you're using Axon 3.3 and SpringBoot 2, this is how we did it in this project:
#Configuration
public class SnapshotConfiguration {
#Bean
public SpringAggregateSnapshotter snapshotter(ParameterResolverFactory parameterResolverFactory,
EventStore eventStore,
TransactionManager transactionManager) {
// https://docs.axoniq.io/reference-guide/v/3.3/part-iii-infrastructure-components/repository-and-event-store#snapshotting
// (...) By default, snapshots are created in the thread that calls the scheduleSnapshot() method, which is generally not recommended for production (...)
Executor executor = Executors.newSingleThreadExecutor();
return new SpringAggregateSnapshotter(eventStore, parameterResolverFactory, executor, transactionManager);
}
#Bean
public EventCountSnapshotTriggerDefinition snapshotTrigger(SpringAggregateSnapshotter snapshotter) {
int snapshotThreshold = 42;
return new EventCountSnapshotTriggerDefinition(snapshotter, snapshotThreshold);
}
}
And if you need the EventStore configuration:
#Configuration
public class AxonMongoEventStoreConfiguration {
#Bean
public MongoClient axonMongoClient(MongoClientURI axonMongoClientUri) {
return new MongoClient(axonMongoClientUri);
}
#Bean
public MongoClientURI axonMongoClientUri() {
return new MongoClientURI("mongodb://host:port/database");
}
#Bean
#Primary
public EventStorageEngine eventStore(MongoClient axonMongoClient, MongoClientURI axonMongoClientUri) {
DefaultMongoTemplate mongoTemplate = new DefaultMongoTemplate(axonMongoClient, axonMongoClientUri.getDatabase());
return new MongoEventStorageEngine(mongoTemplate);
}
}

Get String from Spring FTP streaming Inbound Channel Adapter

I have the following code which works OK retrieving files from FTP server into a stream, but I need to get String of each file, seems I need to use the Transformer passing a charset but what I'm missing? How exactly to get the content String of each file transferred?
Thanks a lot in advance
#SpringBootApplication
#EnableIntegration
public class FtpinboundApp extends SpringBootServletInitializer implements WebApplicationInitializer {
final static Logger logger = Logger.getLogger(FtpinboundApp.class);
public static void main(String[] args) {
SpringApplication.run(FtpinboundApp.class, args);
}
#Bean
public SessionFactory<FTPFile> ftpSessionFactory() {
DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
sf.setHost("X.X.X.X");
sf.setPort(21);
sf.setUsername("xxx");
sf.setPassword("XXX");
return new CachingSessionFactory<FTPFile>(sf);
}
#Bean
#ServiceActivator(inputChannel = "stream")
public MessageHandler handler() {
return new MessageHandler() {
#Override
public void handleMessage(Message<?> message) throws MessagingException {
System.out.println("trasnferred file:" + message.getPayload());
}
};
}
#Bean
#InboundChannelAdapter(value = "stream", poller = #Poller(fixedRate = "1000"))
public MessageSource<InputStream> ftpMessageSource() {
FtpStreamingMessageSource messageSource = new FtpStreamingMessageSource(template(), null);
messageSource.setRemoteDirectory("/X/X/X");
messageSource.setFilter(new FtpPersistentAcceptOnceFileListFilter(new SimpleMetadataStore(), "streaming"));
return messageSource;
}
#Bean
#Transformer(inputChannel = "stream", outputChannel = "data")
public org.springframework.integration.transformer.Transformer transformer() {
return new StreamTransformer("UTF-8");
}
#Bean
public FtpRemoteFileTemplate template() {
return new FtpRemoteFileTemplate(ftpSessionFactory());
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(5000));
return pollerMetadata;
}
}
Use a StreamTransformer to get the whole file as a single string, or a FileSplitter to get a message for each line.
EDIT (filter config)
#Bean
#InboundChannelAdapter(channel = "stream")
public MessageSource<InputStream> ftpMessageSource() {
FtpStreamingMessageSource messageSource = new FtpStreamingMessageSource(template(), null);
messageSource.setRemoteDirectory("ftpSource/");
messageSource.setFilter(filter());
return messageSource;
}
public FileListFilter<FTPFile> filter() {
CompositeFileListFilter<FTPFile> filter = new CompositeFileListFilter<>();
filter.addFilter(new FtpSimplePatternFileListFilter("*.txt"));
filter.addFilter(acceptOnceFilter());
return filter;
}
#Bean
public FtpPersistentAcceptOnceFileListFilter acceptOnceFilter() {
FtpPersistentAcceptOnceFileListFilter filter = new FtpPersistentAcceptOnceFileListFilter(meta(),
"streaming"); // keys will be, e.g. "streamingfoo.txt"
filter.setFlushOnUpdate(true);
return filter;
}
#Bean
public ConcurrentMetadataStore meta() {
PropertiesPersistingMetadataStore meta = new PropertiesPersistingMetadataStore();
meta.setBaseDirectory("/tmp/foo");
meta.setFileName("ftpStream.properties");
return meta;
}
EDIT2 - remove remote file with an advice
#ServiceActivator(inputChannel = "data", adviceChain = "after")
#Bean
public MessageHandler handle() {
return System.out::println;
}
#Bean
public ExpressionEvaluatingRequestHandlerAdvice after() {
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
advice.setOnSuccessExpression(
"#template.remove(headers['file_remoteDirectory'] + headers['file_remoteFile'])");
advice.setPropagateEvaluationFailures(true);
return advice;
}

Resources