I'm looking to use #Transactional in spring boot, but after several attempts i cannot get the transaction working despite having an exception inside to rollback, so I'm missing something ?
if an exception happened with WebSocketHandler.sendNotificationToVenue in serviceimpl
i wanna rollback with two insert statements ;(
Here is the ServiceImpl
#Service
#Slf4j
public class OrderInfoServiceImpl implements OrderInfoService {
#Resource
private OrderInfoMapper orderInfoMapper;
#Resource
private OrderDetailsMapper orderdetailsMapper;
#Resource
private WebSocketHandler webSockethHandler;
private OrderDetailsVO od = new OrderDetailsVO();
#Transactional(rollbackFor = Exception.class)
#Override
public Map<String, String> insertOrderInfo(OrderInfoVO order) throws Exception {
Map<String, String> rMap = new HashMap<>();
rMap.put("result", "false");
int oiSum = 0;
try {
for (int i = 0; i < order.getOdList().size(); i++) {
if (order.getOdList().get(i) != null) {
int price = order.getOdList().get(i).getMiPrice();
int qty = order.getOdList().get(i).getOdQuantity();
oiSum += price * qty;
}
}
order.setOiSum(oiSum);
String oiMsg = "";
for (int i = 0; i < order.getOiMessage().size(); i++) {
oiMsg += order.getOiMessage().get(i).get(0) + "/ ";
}
oiMsg = oiMsg.substring(0, oiMsg.lastIndexOf("/"));
order.setOiMsg(oiMsg);
orderInfoMapper.insertOrderInfo(order);
for (int i = 0; i < order.getOdList().size(); i++) {
if (order.getOdList().get(i) != null) {
od = order.getOdList().get(i);
od.setOiNum(order.getOiNum());
orderdetailsMapper.insertOrderDetails(od);
}
}
webSockethHandler.sendNotificatonToVenue(order.getViNum(), order.getOiNum());
rMap.put("result", "true");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
return rMap;
}
and it's the transaction aop class
#Configuration
#EnableAspectJAutoProxy
#EnableTransactionManagement
#Slf4j
public class TransactionAOP {
#Resource
private DataSourceTransactionManager dstm;
#Bean
#ConfigurationProperties(prefix="spring.datasource.hikari")
public DataSource getDS() {
return DataSourceBuilder.create().build();
}
#Bean
public DataSourceTransactionManager txManager() {
return new DataSourceTransactionManager(getDS());
}
#Bean
public TransactionInterceptor txInterceptor() {
log.debug("transaction starts...");
TransactionInterceptor txInterceptor = new TransactionInterceptor();
Properties prop = new Properties();
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
rollbackRules.add(new RollbackRuleAttribute(Exception.class));
DefaultTransactionAttribute readOnly = new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED);
readOnly.setReadOnly(true);
readOnly.setTimeout(30);
RuleBasedTransactionAttribute update = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED,rollbackRules);
update.setTimeout(30);
prop.setProperty("select*", readOnly.toString());
prop.setProperty("get*", readOnly.toString());
prop.setProperty("find*", readOnly.toString());
prop.setProperty("search*", readOnly.toString());
prop.setProperty("count*", readOnly.toString());
prop.setProperty("*", update.toString());
txInterceptor.setTransactionAttributes(prop);
txInterceptor.setTransactionManager(dstm);
return txInterceptor;
}
#Bean
public Advisor txAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* com.grabit.bdi.service..*ServiceImpl.*(..))");
return new DefaultPointcutAdvisor(pointcut, txInterceptor());
}
}
thanks for your time in advance!
Could you try using the following instead?
#Transactional(rollbackFor = [Exception.class])
I thought I read somewhere that the API requires an array of Exception types to be stipulated in the attribute value.
Related
I am building an Spring Batch application that loads fetches data from external API and loading it to Database. I am making a REST call to external API which API return JSON/CSV string based on request parameters(json/csv).
The application currently works fine for CSV file saved in filesystem. I am trying to get rid of creating a new file and using the file as input every time. I would want to achieve the loading without having to create any file on the disk.
I have googled and tried different solutions, I have replaced FlatFileItemReader with JsonItemReader.
Can you please help me here.
ReportService.java
#Slf4j
#Service
public class ReportService {
public static final String OUTPUT_FILE_FORMAT = "csv";
#Autowired
ProxyRestClient proxyRestClient;
#Autowired
FileOperations fileWriter;
#Autowired
CommonUtility commonUtil;
#Autowired
ReportMapping reportMapping;
#Autowired
ReportConfig reportConfig;
#Autowired
JobMapping jobMap;
#Autowired
DataIntegrationResponseBuilder responseBuilder;
#Autowired
ImportJobLauncher batchJob;
#Autowired
WFitsPricingParser wFitsPricingParser;
public ResponseEntity<DataIntegrationResponse> getReport(String jobName) throws Exception {
HashMap<String, String> fitsParamMap = new HashMap<String, String>();
fitsParamMap = getFitsParameters(jobName);
ResponseEntity<String> response;
final String filePath = Path.of("").toAbsolutePath().toString() + "\\";
final String baseUrl = reportConfig.getBaseURL();
String reportURL = baseUrl + fitsParamMap.get("reportId") + "?format=" + OUTPUT_FILE_FORMAT;
String reportName = fitsParamMap.get("reportName");
log.info("Fetching report from FITS API.");
log.info("FITS API URL: " + reportURL + ".");
response = proxyRestClient.callFitsApi(reportURL, commonUtil.encodedCredentials());
/*
* ObjectMapper mapper = new ObjectMapper(); WFitsVendor[] jsonObj =
* mapper.readValue(response.getBody(), WFitsVendor[].class);
*/
if (response != null) {
if (response.getStatusCodeValue() == 200 && response.hasBody()) {
fileWriter.writeToFile(response.getBody(), filePath + reportName + "." + OUTPUT_FILE_FORMAT);
if (jobName.equals("vendor")) {
BatchJobResponse batchJobResponse = batchJob.importFitsVendor(filePath + reportName + "." + OUTPUT_FILE_FORMAT, response.getBody());
return new ResponseEntity<>(responseBuilder.buildResponse(response.getStatusCode(),
reportName + "." + OUTPUT_FILE_FORMAT, batchJobResponse.getJobName(), batchJobResponse.getJobStatus(), batchJobResponse.getJobId()),
HttpStatus.CREATED);
}
}
}
return new ResponseEntity<>(responseBuilder.buildResponse(HttpStatus.NOT_FOUND, "", "", "", 0 L),
HttpStatus.NOT_FOUND);
}
private HashMap<String, String> getFitsParameters(String jobName) {
HashMap<String, String> fitsParamMap = new HashMap<String, String>();
fitsParamMap.put("reportName", reportMapping.getMappings().getOrDefault(jobMap.getMappings().get(jobName), ""));
fitsParamMap.put("reportId", jobMap.getMappings().getOrDefault(jobName, ""));
return fitsParamMap;
}
}
ProxyRestClient.java
#Service
public class ProxyRestClient {
#Autowired
#Qualifier("externalRestTemplate")
RestTemplate restTemplate;
public ResponseEntity<String> callFitsApi(
String reportURL, String encodedCredentials)
throws JsonMappingException, JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + encodedCredentials);
HttpEntity<String> request = new HttpEntity<>(headers);
ResponseEntity<String> response = null;
ResponseEntity<String> responseWfits = null;
// response = restTemplate.exchange(reportURL, HttpMethod.GET, request,
// String.class);
responseWfits = restTemplate.exchange(https://fitsonline.trgrp.com/msmdsqa/api/report/user/5010296?format=json, HttpMethod.GET, request, String.class);
return responseWfits;
}
}
VendorJob.java
#Configuration
#EnableBatchProcessing
#AllArgsConstructor
public class VendorJob {
private JobBuilderFactory jobBuilderFactory;
private StepBuilderFactory stepBuilderFactory;
private static final String DROP_SCRIPT = "TRUNCATE TABLE MDIA.WFITS_VENDOR";
#Autowired
private HikariDataSource dataSource;
/*
* This is for CSV File
*
* #Bean
* #StepScope
public FlatFileItemReader<WFitsVendor>
* VendorReader(#Value("#{jobParameters['filePath']}") String filePath,
#Value("#{jobParameters['jsonObj']}") String jsonObj) {
FlatFileItemReader<WFitsVendor> itemReader = new FlatFileItemReader<>();
itemReader.setResource(new FileSystemResource(filePath));
itemReader.setName("csvReader"); itemReader.setLinesToSkip(1);
itemReader.setLineMapper(lineMapper());
itemReader.setRecordSeparatorPolicy(new ReaderPolicy());
return itemReader; }
*/
#Bean
#StepScope
public JsonItemReader<WFitsVendor> jsonItemReader(#Value("#{jobParameters['filePath']}") String filePath,
#Value("#{jobParameters['jsonObj']}") String jsonObj) {
ObjectMapper objectMapper = new ObjectMapper();
// configure the objectMapper as required
JacksonJsonObjectReader<WFitsVendor> jsonObjectReader =
new JacksonJsonObjectReader<>(WFitsVendor.class);
jsonObjectReader.setMapper(objectMapper);
return new JsonItemReaderBuilder<WFitsVendor>()
.jsonObjectReader(jsonObjectReader)
.resource(new ByteArrayResource(jsonObj.getBytes()))
.name("jsonItemReader")
.build();
}
private LineMapper<WFitsVendor> lineMapper() {
WFitsVendorLineMapper<WFitsVendor> lineMapper = new WFitsVendorLineMapper<>();
DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
lineTokenizer.setDelimiter(",");
lineTokenizer.setNames("VENDORNAME", "Type", "Notes");
lineTokenizer.setStrict(true);
lineTokenizer.setIncludedFields(0, 1, 2);
BeanWrapperFieldSetMapper<WFitsVendor> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
fieldSetMapper.setTargetType(WFitsVendor.class);
lineMapper.setLineTokenizer(lineTokenizer);
lineMapper.setFieldSetMapper(fieldSetMapper);
return lineMapper;
}
#Bean
public VendorProcessor VendorProcessor() {
return new VendorProcessor();
}
#Bean
public JdbcBatchItemWriter<WFitsVendor> VendorWriter() {
JdbcBatchItemWriter<WFitsVendor> databaseItemWriter = new JdbcBatchItemWriter<>();
databaseItemWriter.setDataSource(dataSource);
databaseItemWriter.setSql(
"INSERT INTO MDIA.WFITS_VENDOR(VENDOR, VENDOR_INFO, VENDOR_TYPE, CREATED_BY) VALUES (?, ?, ?, ?)");
ItemPreparedStatementSetter<WFitsVendor> valueSetter = new WFitsVendorPreparedStatementSetter();
databaseItemWriter.setItemPreparedStatementSetter(valueSetter);
return databaseItemWriter;
}
#Bean
public Step loadVendorTable() {
return stepBuilderFactory.get("load-wfitsvendor-table").<WFitsVendor, WFitsVendor> chunk(10000)
.reader(jsonItemReader(null, null)).writer(VendorWriter()).processor(VendorProcessor()).faultTolerant()
.taskExecutor(VendortaskExecutor()).build();
}
#Bean
public Step truncateVendorTable() {
return stepBuilderFactory.get("truncate-wfitsvendor-table").tasklet(truncateTableTasklet()).build();
}
public Tasklet truncateTableTasklet() {
return (contribution, chunkContext) -> {
new JdbcTemplate(dataSource).execute(DROP_SCRIPT);
return RepeatStatus.FINISHED;
};
}
#Bean
#Qualifier("VendorJob")
public Job runVendorJob() {
return jobBuilderFactory.get("VendorJob").listener(new JobCompletionListener()).start(truncateVendorTable())
.next(loadVendorTable()).build();
}
#Bean
public TaskExecutor VendortaskExecutor() {
return new ConcurrentTaskExecutor(Executors.newCachedThreadPool());
}
}
}
VendorLineMapper.java
public class VendorLineMapper<T> implements LineMapper<WFitsVendor> , InitializingBean {
private LineTokenizer tokenizer;
private FieldSetMapper<WFitsVendor> fieldSetMapper;
#Override
public WFitsVendor mapLine(String line, int lineNumber) throws Exception {
WFitsVendor vr = fieldSetMapper.mapFieldSet(tokenizer.tokenize(line));
//System.out.println(line);
vr.setLineNo(lineNumber);
return vr;
}
public void setLineTokenizer(LineTokenizer tokenizer) {
this.tokenizer = tokenizer;
}
public void setFieldSetMapper(FieldSetMapper<WFitsVendor> fieldSetMapper) {
this.fieldSetMapper = fieldSetMapper;
}
#Override
public void afterPropertiesSet() {
Assert.notNull(tokenizer, "The LineTokenizer must be set");
Assert.notNull(fieldSetMapper, "The FieldSetMapper must be set");
}
}
ReaderPolicy.java
public class ReaderPolicy extends DefaultRecordSeparatorPolicy {
#Override
public boolean isEndOfRecord(final String line) {
return line.trim().length() != 0 && super.isEndOfRecord(line);
}
#Override
public String postProcess(final String record) {
if (record == null || record.trim().length() == 0) {
return null;
}
return super.postProcess(record);
}
JSON Returned from API:
You can use a URLResource and point your JSON item reader to it, something like:
#Bean(destroyMethod = "close")
public InputStream urlResource() throws IOException {
URL url = new URL("https://path.to.your.resource");
URLConnection urlConnection = url.openConnection();
// urlConnection.setRequestProperty("", ""); // set auth headers if necessary
return urlConnection.getInputStream();
}
#Bean
public JsonItemReader<Pojo> itemReader() throws IOException {
return new JsonItemReaderBuilder<Pojo>()
.name("restReader")
.resource(new InputStreamResource(urlResource()))
.strict(true)
.jsonObjectReader(new JacksonJsonObjectReader<>(Pojo.class))
.build();
}
[ISSUE] repo always returns null when I call repo methods, while stepping through, throws null pointer exception. then front end receives
500: Http failure response for http://localhost:4200/api/aiprollout/updatecsv: 500 Internal Server Error
[HAVE TRIED] Adjusting AutoWired and components and service annotations.
[QUESTIONS]
1- Does every repo method need its own service and controller method?
2- Is it okay to create a new service that uses an existing controller?
3- If this new service uses SuperCsv and I create custom CsvCellProcessors, can these cell processors also call the repo? Should these cell processors perform logic? or should it be done else where? What class annotations should these cellProcessors classes have? #Component?
Any advice is greatly appreciated, feel a little lost at this point not even sure what to do.
[CODE]
Controller:
#RestController
#EnableConfigurationProperties({SpoofingConfigurationProperties.class})
#RequestMapping(value = "")
public class AipRolloutController {
private final Logger logger = some logger
private final AipRolloutService AipRolloutService;
private final CsvParserService csvParserService;
#Autowired
public AipRolloutController(AipRolloutService aipRolloutService, CsvParserService csvParserService) {
this.AipRolloutService = aipRolloutService;
this.csvParserService = csvParserService;
}
#PostMapping(value = "/updatecsv", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<?> processCsv(#RequestParam("csvFile") MultipartFile csvFile) throws IOException {
if (csvFile.isEmpty()) return new ResponseEntity(
responceJson("please select a file!"),
HttpStatus.NO_CONTENT
);
csvParserService.parseCsvFile(csvFile);
return new ResponseEntity(
responceJson("Successfully uploaded - " + csvFile.getOriginalFilename()),
new HttpHeaders(),
HttpStatus.CREATED
);
}
Service:
#Service
public class AipRolloutService {
private static final Logger logger = some logger
#Autowired
private AIPRolloutRepository AIPRolloutRepository;
New Csv parser Service
#Service
public class CsvParserService {
#Autowired private AipRolloutService aipRolloutService;
public CsvParserService(AipRolloutService aipRolloutService) {
this.aipRolloutService = aipRolloutService;
}
public void parseCsvFile(MultipartFile csvFile) throws IOException {
CsvMapReader csvMapReader = new CsvMapReader(new InputStreamReader(csvFile.getInputStream()), CsvPreference.STANDARD_PREFERENCE);
parseCsv(csvMapReader);
csvMapReader.close();
}
private void parseCsv(CsvMapReader csvMapReader) throws IOException {
String[] header = csvMapReader.getHeader(true);
List<String> headers = Arrays.asList(header);
verifySourceColumn(headers);
verifyPovColumn(headers);
final CellProcessor[] processors = getProcessors(headers);
Map<String, Object> csvImportMap = null;
while ((csvImportMap = csvMapReader.read(header, processors)) != null) {
CsvImportDTO csvImportDto = new CsvImportDTO(csvImportMap);
if ( activationTypeP(csvImportDto) ){
int mssValue = Integer.parseInt(csvImportDto.getMssValue());
aipRolloutService.updateAipRollout(csvImportDto.getSource(),
csvImportDto.getPov(),
csvImportDto.getActivationType(),
mssValue);
}
}
}
private CellProcessor[] getProcessors(List<String> headers) {
CellProcessor[] processors = new CellProcessor[headers.size()];
int index = 0;
for (String header : headers) {
if (header.contains(SOURCE_ID)) {
processors[index++] = new CsvSourceIdCellParser();
} else if (header.contains(POV)) {
processors[index++] = new CsvPovCellParser();
} else if (header.contains(ACTIVATION_TYPE)) {
processors[index++] = new CsvActivationTypeCellParser();
} else if (header.contains(ACTIVATION_DATE)) {
processors[index++] = new Optional();
} else if (header.contains(DEACTIVATION_DATE)) {
processors[index++] = new Optional();
} else if (header.contains(MSS_VALUE)) {
processors[index++] = new CsvMssValueCellParser();
} else {
processors[index++] = null; // throw exception? wrong header info instead of allowing null?
}
}
return processors;
}
Custom Cell Processor that calls repo and returns null
public class CsvSourceIdCellParser extends CellProcessorAdaptor {
#Autowired AIPRolloutRepository aipRolloutRepository;
public CsvSourceIdCellParser(){ super(); }
// this constructor allows other processors to be chained
public CsvSourceIdCellParser(CellProcessor next){ super(next); }
#Override
public Object execute(Object value, CsvContext csvContext) {
// throws an Exception if the input is null
validateInputNotNull(value, csvContext);
// get rid of description only need first 3 #'s
value = value.toString().substring(0,3);
// check if WH exists
if( aipRolloutRepository.dcExistsInDatabase(value.toString()) )
return value;
else
throw new RuntimeException("Check Warehouse Value, Value Not Found "
+ "Row number: " + csvContext.getRowNumber()
+ " Column number: " + csvContext.getColumnNumber());
}
}
Repository
#Repository
public class AIPRolloutRepository {
private static final Logger logger = LoggerFactory.getLogger(AIPRolloutRepository.class);
#Autowired
JdbcTemplate jdbcTemplate;
public AIPRolloutRepository() {
}
public boolean dcExistsInDatabase(String dc){
// Query for a count saves time and memory, query for distinct saves time and memory on execution
boolean hasRecord =
jdbcTemplate
.query( "select count (distinct '" + dc +"')" +
"from xxcus.XX_AIP_ROLLOUT" +
"where DC = '" + dc + "';",
new Object[] { dc },
(ResultSet rs) -> {
if (rs.next()) {
return true;
}
return false;
}
);
return hasRecord;
}
My project uses springboot+springDataJpa+shiro.
Because my server database uses the master and salve method, so I need to call my code to connect to the two databases, I designed to use the AbstractRoutingDataSource + aop method. Now I have a problem, I think it may be caused by shiro.
I know that the connection switching is performed by the getconnection() method of AbstractRoutingDataSource, and I cannot manually control this method. The problem now is that my getconnection() is executed at most twice in an interface request. Let me post my code and describe it:
#Order(0)
#Aspect
#Component
public class RoutingAopAspect {
#Around("#annotation(targetDataSource)")
public Object routingWithDataSource(ProceedingJoinPoint joinPoint, TargetDataSource targetDataSource) throws Throwable {
try {
DynamicRoutingDataSourceContext.setRoutingDataSource(targetDataSource.value());
return joinPoint.proceed();
} finally {
DynamicRoutingDataSourceContext.removeRoutingDataSource();
}
}
}
public class DynamicRoutingDataSourceContext {
public static final String MASTER = "master";
public static final String SLAVE = "slave";
private static final ThreadLocal<Object> threadLocalDataSource = new ThreadLocal<>();
public static void setRoutingDataSource(Object dataSource) {
if (dataSource == null) {
throw new NullPointerException();
}
threadLocalDataSource.set(dataSource);
// System.err.println(Thread.currentThread().getName()+" set RoutingDataSource : " + dataSource);
}
public static Object getRoutingDataSource() {
Object dataSourceType = threadLocalDataSource.get();
if (dataSourceType == null) {
threadLocalDataSource.set(DynamicRoutingDataSourceContext.MASTER);
return getRoutingDataSource();
}
// System.err.println(Thread.currentThread().getName()+" get RoutingDataSource : " + dataSourceType);
return dataSourceType;
}
public static void removeRoutingDataSource() {
threadLocalDataSource.remove();
// System.err.println(Thread.currentThread().getName()+" remove RoutingDataSource");
}
}
#EnableTransactionManagement
#Configuration
public class DataSourceConfig {
#Value("${datasource.master.url}")
private String masterUrl;
#Value("${datasource.master.username}")
private String masterUsername;
#Value("${datasource.master.password}")
private String masterPassword;
#Value("${dataSource.driverClass}")
private String masterDriverClassName;
#Value("${datasource.slave.url}")
private String slaveUrl;
#Value("${datasource.slave.username}")
private String slaveUsername;
#Value("${datasource.slave.password}")
private String slavePassword;
#Value("${dataSource.driverClass}")
private String slaveDriverClassName;
#Bean(name = "masterDataSource")
public DataSource masterDataSource(){
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(masterUrl);
datasource.setUsername(masterUsername);
datasource.setPassword(masterPassword);
datasource.setDriverClassName(masterDriverClassName);
return datasource;
}
#Bean(name = "slaveDataSource")
public DataSource slaveDataSource(){
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(slaveUrl);
datasource.setUsername(slaveUsername);
datasource.setPassword(slavePassword);
datasource.setDriverClassName(slaveDriverClassName);
return datasource;
}
#Primary
#Bean
public DynamicRoutingDataSource dynamicDataSource(#Qualifier(value = "masterDataSource") DataSource masterDataSource,
#Qualifier(value = "slaveDataSource") DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>(2);
targetDataSources.put(DynamicRoutingDataSourceContext.MASTER, masterDataSource);
targetDataSources.put(DynamicRoutingDataSourceContext.SLAVE, slaveDataSource);
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
dynamicRoutingDataSource.setTargetDataSources(targetDataSources);
dynamicRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
dynamicRoutingDataSource.afterPropertiesSet();
return dynamicRoutingDataSource;
}
}
public class DynamicRoutingDataSourceContext {
public static final String MASTER = "master";
public static final String SLAVE = "slave";
private static final ThreadLocal<Object> threadLocalDataSource = new ThreadLocal<>();
public static void setRoutingDataSource(Object dataSource) {
if (dataSource == null) {
throw new NullPointerException();
}
threadLocalDataSource.set(dataSource);
// System.err.println(Thread.currentThread().getName()+" set RoutingDataSource : " + dataSource);
}
public static Object getRoutingDataSource() {
Object dataSourceType = threadLocalDataSource.get();
if (dataSourceType == null) {
threadLocalDataSource.set(DynamicRoutingDataSourceContext.MASTER);
return getRoutingDataSource();
}
// System.err.println(Thread.currentThread().getName()+" get RoutingDataSource : " + dataSourceType);
return dataSourceType;
}
public static void removeRoutingDataSource() {
threadLocalDataSource.remove();
// System.err.println(Thread.currentThread().getName()+" remove RoutingDataSource");
}
}
This is the relevant basic configuration of AbstractRoutingDataSource.
I defined an aspect to get the parameters of #TargetDataSource in the method. This parameter is a data source that needs to be executed currently. I think there is no problem with my configuration.
Then I will use #TargetDataSource on my service method, and I use shiro, shiro’s doGetAuthorizationInfo() method and doGetAuthenticationInfo() are executed before my service, and both methods need to call my userservice .
Then the problem now is that after calling the doGetAuthorizationInfo() and doGetAuthenticationInfo() methods, they will automatically execute the getconnection() method of AbstractRoutingDataSource to switch the data source, and then execute to my own service, it will not execute the getconnection() method. , This is what I said getconnection() is executed at most twice in an interface request.
#Slf4j
#Component
public class ShiroRealm extends AuthorizingRealm {
#Autowired
#Lazy
private UserService userService;
#Autowired
CacheUtil cacheUtil;
#Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
#Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = JwtUtil.getClaim(principals.toString(), "username");
User user = userService.getUserByUsername(username);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole(user.getRole());
return simpleAuthorizationInfo;
}
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) {
String token = (String) auth.getCredentials();
String username = JwtUtil.getClaim(token, "username");
if (username == null) {
throw new AuthenticationException("token invalid");
}
User user = userService.getUserByUsername(username);
if (user == null) {
throw new AuthenticationException("User didn't existed!");
}
if (JwtUtil.verify(token, username, user.getPassword(), TokenType.ACCESS_TOKEN) &&
cacheUtil.hasKey(CacheKey.ACCESS_TOKEN_KEY + token)
) {
return new SimpleAuthenticationInfo(token, token, "userRealm");
}
throw new AuthenticationException("Token expired or incorrect");
}
}
#Service
public class PageServiceImpl implements PageService {
#Autowired
PageRepository pageRepository;
#Override
#TargetDataSource("slave")
#Transactional(rollbackFor = Exception.class)
public List<Page> adminFindAll() {
List<Page> pageList = pageRepository.findAll();
if (pageList.isEmpty()) {
throw new CustomNotFoundException("page list not found");
}
return pageList;
}
}
I don’t know if my description is clear. If it is not clear, please ask questions. I hope to get your help, thanks very much!
Currently I am working on the spring annotation based dependency injection for Activity Worker and Workflow worker As per the documentation.I have defined my beans inside my spring boot application. Each worker is defined in the separate maven module. The issue that I am facing is that when while running my ActivityWorker spring boot module it stays active and start looking up the activities but workflow worker stops immediately after starting the module with the message '
Unregistering JMX-exposed beans on shutdown
My implementation are as following:
#Activities(version = "2.2")
#ActivityRegistrationOptions(defaultTaskScheduleToStartTimeoutSeconds = 300, defaultTaskStartToCloseTimeoutSeconds = 100)
public interface TempActivities {
public GreetWrapper getName();
public void say(String what);
/* public Integer doProcess();
public int sum(Integer num);*/
}
public class TempActivitiesImpl implements TempActivities {
GreetWrapper greetObj = new GreetWrapper();
public TempActivitiesImpl() {
// TODO Auto-generated constructor stub
}
#Override
public GreetWrapper getName() {
greetObj.setGreet("World");
return greetObj;
}
#Override
public void say(String what) {
System.out.println(what);
}
}
#Workflow(dataConverter = GreetWrapper.class)
#WorkflowRegistrationOptions(defaultExecutionStartToCloseTimeoutSeconds = 3600)
public interface TempWorkflow {
#Execute(name = "TempWorkflow", version = "2.2")
public void greet();
}
public class TempWorkflowImpl implements TempWorkflow {
private TempActivitiesClient activitiesClientImpl = new TempActivitiesClientImpl();
private DecisionContextProvider contextProvider = new DecisionContextProviderImpl();
private WorkflowClock clock
= contextProvider.getDecisionContext().getWorkflowClock();
#Override
public void greet() {
greet1(0);
}
public void greet1(int count,
Promise < ? > ...waitFor) {
if (count == 3) {
return;
}
Promise < GreetWrapper > name = activitiesClientImpl.getName();
Promise < String > greeting = getGreeting(name);
activitiesClientImpl.say(greeting);
Promise < Void > timer = clock.createTimer(30);
greet1(count + 1, timer);
}
#Asynchronous
public Promise < String > getGreeting(Promise < GreetWrapper > name) {
String greeting = "Hello " + name.get().getGreet();
System.out.println("Greeting: " + greeting);
return Promise.asPromise(greeting);
}
}
Here is my Activity Worker beans
#Configuration
public class AppConfig {
public String getActivityTasklistName() {
return "HelloWorldTaskList";
}
public String getDomainName() {
return "helloWorldWalkthrough2";
}
public String getWorkflowTasklistName() {
return "HelloWorldWorkflow";
}
public String getEndPoint() {
String endPoint = "https://swf.us-east-1.amazonaws.com";
return endPoint;
}
String swfAccessId = System.getenv("AWS_ACCESS_KEY_ID");
String swfSecretKey = System.getenv("AWS_SECRET_ACCESS_KEY");
/*#Autowired
TempActivities tempActivitiesImpl;
#Autowired
TempWorkflow tempWorkflowImpl; */
#Bean
public ClientConfiguration clientConfiguration() {
ClientConfiguration config = new ClientConfiguration();
config.withSocketTimeout(70 * 1000);
return config;
}
#Bean
public AWSCredentials basicAWSCredentials() {
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(swfAccessId, swfSecretKey);
return basicAWSCredentials;
}
#Bean
public AmazonSimpleWorkflow amazonSimpleWorkflowClient() {
AmazonSimpleWorkflow amazonSimpleWorkflowClient = new AmazonSimpleWorkflowClient(basicAWSCredentials(), clientConfiguration());
amazonSimpleWorkflowClient.setEndpoint(getEndPoint());
return amazonSimpleWorkflowClient;
}
#Bean
public TempActivitiesClient tempActivitiesClient() {
TempActivitiesClient tempActivitiesClient = new TempActivitiesClientImpl();
return tempActivitiesClient;
}
#Bean
public SpringActivityWorker springActivityWorker() throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException {
SpringActivityWorker activityWorker = new SpringActivityWorker(amazonSimpleWorkflowClient(), getDomainName(), getWorkflowTasklistName());
activityWorker.addActivitiesImplementation(new TempActivitiesImpl());
return activityWorker;
}
}
Here is my workflow worker beans
public class WorkFlowAppConfig {
public String getActivityTasklistName() {
return "HelloWorldTaskList";
}
public String getDomainName() {
return "helloWorldWalkthrough2";
}
public String getWorkflowTasklistName() {
return "HelloWorldWorkflow";
}
public String getEndPoint() {
String endPoint = "https://swf.us-east-1.amazonaws.com";
return endPoint;
}
String swfAccessId = System.getenv("AWS_ACCESS_KEY_ID");
String swfSecretKey = System.getenv("AWS_SECRET_ACCESS_KEY");
/*#Autowired
TempActivities tempActivitiesImpl;*/
#Autowired
TempWorkflow tempWorkflowImpl;
#Bean
#Scope("workflow")
public ClientConfiguration clientConfiguration() {
ClientConfiguration config = new ClientConfiguration();
config.withSocketTimeout(70 * 1000);
return config;
}
#Bean
#Scope("workflow")
public AWSCredentials basicAWSCredentials() {
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(swfAccessId, swfSecretKey);
return basicAWSCredentials;
}
#Bean
#Scope("workflow")
public AmazonSimpleWorkflow amazonSimpleWorkflowClient() {
AmazonSimpleWorkflow amazonSimpleWorkflowClient = new AmazonSimpleWorkflowClient(basicAWSCredentials(), clientConfiguration());
amazonSimpleWorkflowClient.setEndpoint(getEndPoint());
return amazonSimpleWorkflowClient;
}
#Bean
#Scope("workflow")
public TempActivitiesClient activitiesClientImpl() {
return new TempActivitiesClientImpl();
}
#Bean
#Scope("workflow")
public SpringWorkflowWorker springWorkflowWorker() throws InstantiationException, IllegalAccessException {
SpringWorkflowWorker workflowWorker = new SpringWorkflowWorker(amazonSimpleWorkflowClient(), getDomainName(), getWorkflowTasklistName());
workflowWorker.addWorkflowImplementation(tempWorkflowImpl);
workflowWorker.setRegisterDomain(true);
// workflowWorker.setDomainRetentionPeriodInDays(1);
return workflowWorker;
}
#Bean
public CustomScopeConfigurer customScope() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
Map < String, Object > workflowScope = new HashMap < String, Object > ();
workflowScope.put("workflow", new WorkflowScope());
configurer.setScopes(workflowScope);
return configurer;
}
}
I have declared an aspect like the following
#Aspect
public class CacheMonitorImpl {
private final static Logger LOG = LoggerFactory
.getLogger(CacheMonitorImpl.class);
private final static NumberFormat NF = new DecimalFormat("0.0###");
#Autowired
private EntityManagerFactory entityManagerFactory;
#Around("execution(* aop.web.teacher.service..*.*(..))")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
LOG.info("$$ Test Property :: " + testprop);
if (!LOG.isDebugEnabled()) {
LOG.info("####### Logger is not debug enabled"
+ entityManagerFactory);
return pjp.proceed();
}
HibernateEntityManagerFactory hbmanagerfactory = (HibernateEntityManagerFactory) entityManagerFactory;
SessionFactory sessionFactory = hbmanagerfactory.getSessionFactory();
Statistics statistics = sessionFactory.getStatistics();
statistics.setStatisticsEnabled(true);
long hit0 = statistics.getQueryCacheHitCount();
long miss0 = statistics.getSecondLevelCacheMissCount();
Object result = pjp.proceed();
long hit1 = statistics.getQueryCacheHitCount();
long miss1 = statistics.getQueryCacheMissCount();
double ratio = (double) hit1 / (hit1 + miss1);
if (hit1 > hit0) {
LOG.debug(String.format("CACHE HIT; Ratio=%s; Signature=%s#%s()",
NF.format(ratio), pjp.getTarget().getClass().getName(), pjp
.getSignature().toShortString()));
} else if (miss1 > miss0) {
LOG.debug(String.format("CACHE MISS; Ratio=%s; Signature=%s#%s()",
NF.format(ratio), pjp.getTarget().getClass().getName(), pjp
.getSignature().toShortString()));
} else {
LOG.debug("query cache not used");
}
return null;
}
}
Now the aspect method is getting invoked but I am getting null EntityManagerFactory. Please point me to the correct of doing this!
Thanks in advance.