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!
Related
[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;
}
I want to write simple aspect for enabling Hibernate filtering on some services. I need it for implementing some "visibility rules".
But i don't know how to do it with WebFlux. ReactiveSecurityContextHolder.getContext() returns nothing, when i call it in #Around aspect. I have debugged code and found that authentication starts before Aspect execution and ending after Aspect execution finished. Anybody know solution to this problem ? How can i retreive logged in user from aspect when using webflux ?
My aspect:
#Pointcut("execution(* com.shs.crm.dao.service.user.*.*(..))")
public void dataAccessOperation() {
logger.info("dataAccessOperation performed");
}
#Around("com.shs.aspect.DataSecurityAspect.dataAccessOperation()")
public Object doAccessCheck(ProceedingJoinPoint proceedingJoinPoint)
throws Throwable {
Object returnObject;
if (proceedingJoinPoint.getTarget() instanceof CrmUserService) {
try {
Long userId = customSecurityService.getCurrentUserId().toFuture().get();//here i have IllegalStateException
} catch (Exception noContext) {
logger.debug(
"No Security Context associated with current thread - assuming least access");
}
// Here i will enable filter
returnObject = proceedingJoinPoint.proceed();
} else {
// here i will disable filter
returnObject = proceedingJoinPoint.proceed();
}
return returnObject;
}
Service for retreiving current user:
#Service
public class CustomSecurityService {
public Mono<Long> getCurrentUserId() {
return ReactiveSecurityContextHolder.getContext().switchIfEmpty(Mono.error(new IllegalStateException("ReactiveSecurityContext is empty")))
.map(SecurityContext::getAuthentication)
.map(Authentication::getName)
.map(Long::parseLong);
}
}
Security:
#Component
public class SecurityContextRepository implements ServerSecurityContextRepository{
#Autowired
private AuthenticationManager authenticationManager;
#Override
public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public Mono<SecurityContext> load(ServerWebExchange swe) {
ServerHttpRequest request = swe.getRequest();
String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String authToken = authHeader.substring(7);
Authentication auth = new UsernamePasswordAuthenticationToken(authToken, authToken);
return this.authenticationManager.authenticate(auth).map((authentication) -> {
return new SecurityContextImpl(authentication);
});
} else {
return Mono.empty();
}
}
}
#Component
public class AuthenticationManager implements ReactiveAuthenticationManager {
#Autowired
private JWTUtil jwtUtil;
#Autowired
private CrmUserService userService;
#Override
public Mono<Authentication> authenticate(Authentication authentication) {
String authToken = authentication.getCredentials().toString();
Long userId;
try {
userId = jwtUtil.getUserIdFromToken(authToken);
} catch (Exception e) {
userId = null;
}
if (userId != null && jwtUtil.validateToken(authToken)) {
Map<String, Claim> claims = jwtUtil.getAllClaimsFromToken(authToken);
Set<Long> permissions = userService.getUserPermissionsIds(userId);
//List<Long> permissions = claims.get("permissions").asList(Long.class);
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
userId,
null,
permissions.stream().map( permission -> new SimpleGrantedAuthority(permission.toString())).collect(Collectors.toList())
);
return Mono.just(auth);
} else {
return Mono.empty();
}
}
List<String> getPermissionsNamesByIds(List<Long> ids){
return ids.stream().map(id -> {
CrmPermission.AvailablePermissions permission = (CrmPermission.AvailablePermissions)CrmPermission.AvailablePermissions.fromValue(id.intValue());
return permission.getCode();
}).collect(Collectors.toList());
}
}
I can get current user only from controller:
#RequestMapping(method = RequestMethod.GET)
public Mono<CrmUserDto> getAccount(Principal principal) {
return customSecurityService.getCurrentUserId().flatMap(userId -> {
CrmUser currentUser = userRepository.findOneByIdWithRoles(userId);
CrmUserDto userDTO = modelMapperService.map(currentUser, CrmUserDto.class);
return Mono.just(userDTO);
});
}
I am trying to write some integration tests relative to some methods that needs to extract data from MongoDB. In detail, I am using the Embedded Mongo given by Spring Data project. The embedded mongo is clearly provided by Flapdoodle.
I need to import some json file into the Embedded Mongo. I have looked at the tests provided with flapdoodle, but I am not able to understand how they integrates with the magic given by Spring Data + Spring Boot.
Can anyone post some clarifying snippets?
You can create a junit rule (ExternalResource) which runs before and after each test. Check the MongoEmbeddedRule class to get some idea on the implementation details.
Integration test:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = RANDOM_PORT)
public abstract class TestRunner {
#Autowired
protected MongoTemplate mongoTemplate;
#Rule
public MongoEmbeddedRule mongoEmbeddedRule = new MongoEmbeddedRule(this);
ExternalResource Rule:
public class MongoEmbeddedRule extends ExternalResource {
private final Object testClassInstance;
private final Map<String, Path> mongoCollectionDataPaths;
private final String fieldName;
private final String getterName;
public MongoEmbeddedRule(final Object testClassInstance) {
this(testClassInstance, "mongoTemplate", "getMongoTemplate");
}
protected MongoEmbeddedRule(final Object testClassInstance, final String fieldName, final String getterName) {
this.fieldName = fieldName;
this.getterName = getterName;
this.testClassInstance = testClassInstance;
this.mongoCollectionDataPaths = mongoExtendedJsonFilesLookup();
}
#Override
protected void before() {
dropCollections();
createAndPopulateCollections();
}
#Override
protected void after() {
}
protected Set<String> getMongoCollectionNames() {
return mongoCollectionDataPaths.keySet();
}
public void dropCollections() {
getMongoCollectionNames().forEach(collectionName -> getMongoTemplate().dropCollection(collectionName));
}
protected void createAndPopulateCollections() {
mongoCollectionDataPaths.forEach((key, value) -> insertDocumentsFromMongoExtendedJsonFile(value, key));
}
protected MongoTemplate getMongoTemplate() {
try {
Object value = ReflectionTestUtils.getField(testClassInstance, fieldName);
if (value instanceof MongoTemplate) {
return (MongoTemplate) value;
}
value = ReflectionTestUtils.invokeGetterMethod(testClassInstance, getterName);
if (value instanceof MongoTemplate) {
return (MongoTemplate) value;
}
} catch (final IllegalArgumentException e) {
// throw exception with dedicated message at the end
}
throw new IllegalArgumentException(
String.format(
"%s expects either field '%s' or method '%s' in order to access the required MongoTemmplate",
this.getClass().getSimpleName(), fieldName, getterName));
}
private Map<String, Path> mongoExtendedJsonFilesLookup() {
Map<String, Path> collections = new HashMap<>();
try {
Files.walk(Paths.get("src","test","resources","mongo"))
.filter(Files::isRegularFile)
.forEach(filePath -> collections.put(
filePath.getFileName().toString().replace(".json", ""),
filePath));
} catch (IOException e) {
e.printStackTrace();
}
return collections;
}
private void insertDocumentsFromMongoExtendedJsonFile(Path path, String collectionName) {
try {
List<Document> documents = new ArrayList<>();
Files.readAllLines(path).forEach(l -> documents.add(Document.parse(l)));
getMongoTemplate().getCollection(collectionName).insertMany(documents);
System.out.println(documents.size() + " documents loaded for " + collectionName + " collection.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
json file (names.json) with MongoDB Extended JSON, where every document is in one line and the collection name is the filename without extension.
{ "_id" : ObjectId("594d324d5b49b78da8ce2f28"), "someId" : NumberLong(1), "name" : "Some Name 1", "lastModified" : ISODate("1970-01-01T00:00:00Z")}
{ "_id" : ObjectId("594d324d5b49b78da8ce2f29"), "someId" : NumberLong(2), "name" : "Some Name 2", "lastModified" : ISODate("1970-01-01T00:00:00Z")}
You can have a look at this following Test class, provided by "flapdoodle". The test shows how to import a JSON file containing the collection dataset:
MongoImportExecutableTest.java
You could theoretically also import a whole dump of a database. (using MongoDB restore):
MongoRestoreExecutableTest.java
You can create an abstract class and have setup logic to start mongod and mongoimport process.
AbstractMongoDBTest.java
public abstract class AbstractMongoDBTest {
private MongodProcess mongodProcess;
private MongoImportProcess mongoImportProcess;
private MongoTemplate mongoTemplate;
void setup(String dbName, String collection, String jsonFile) throws Exception {
String ip = "localhost";
int port = 12345;
IMongodConfig mongodConfig = new MongodConfigBuilder().version(Version.Main.PRODUCTION)
.net(new Net(ip, port, Network.localhostIsIPv6()))
.build();
MongodStarter starter = MongodStarter.getDefaultInstance();
MongodExecutable mongodExecutable = starter.prepare(mongodConfig);
File dataFile = new File(Thread.currentThread().getContextClassLoader().getResource(jsonFile).getFile());
MongoImportExecutable mongoImportExecutable = mongoImportExecutable(port, dbName,
collection, dataFile.getAbsolutePath()
, true, true, true);
mongodProcess = mongodExecutable.start();
mongoImportProcess = mongoImportExecutable.start();
mongoTemplate = new MongoTemplate(new MongoClient(ip, port), dbName);
}
private MongoImportExecutable mongoImportExecutable(int port, String dbName, String collection, String jsonFile,
Boolean jsonArray, Boolean upsert, Boolean drop) throws
IOException {
IMongoImportConfig mongoImportConfig = new MongoImportConfigBuilder()
.version(Version.Main.PRODUCTION)
.net(new Net(port, Network.localhostIsIPv6()))
.db(dbName)
.collection(collection)
.upsert(upsert)
.dropCollection(drop)
.jsonArray(jsonArray)
.importFile(jsonFile)
.build();
return MongoImportStarter.getDefaultInstance().prepare(mongoImportConfig);
}
#AfterEach
void clean() {
mongoImportProcess.stop();
mongodProcess.stop();
}
public MongoTemplate getMongoTemplate(){
return mongoTemplate;
}
}
YourTestClass.java
public class YourTestClass extends AbstractMongoDBTest{
#BeforeEach
void setup() throws Exception {
super.setup("db", "collection", "jsonfile");
}
#Test
void test() throws Exception {
}
}
AdminSOAPRunner:
#Component
public class AdminSOAPRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(AdminSOAPRunner.class);
private String userId;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
#Autowired
private AdminAuth adminAuthenticator;
#Autowired
private AdminBean adminBean;
private AccountService accountService;
private void setBindingProviderByAccountService() {
WSBindingProvider bindingProvider = (WSBindingProvider) this.accountService;
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, adminBean.getAccountUrl());
LOGGER.info("Endpoint {}", adminBean.getAccountUrl());
}
private RequestInfo getRequestInfo() {
RequestInfo requestInfo = new RequestInfo();
requestInfo.setAppName(adminBean.getAppName());
requestInfo.setUserId(this.getUserId());
requestInfo.setTrace(UUID.randomUUID().toString());
return requestInfo;
}
public List<ApplyAccountResult> getAccounts(ApplyAccountRequest request) {
AccountService_Service service = null;
URL serviceWSDL = AccountService_Service.class.getResource("/Account-service/Account-service.wsdl");
service = new AccountService_Service(serviceWSDL);
SOAPHandlerResolver SOAPHandlerResolver = new SOAPHandlerResolver();
SOAPHandlerResolver.getHandlerList().add(new SOAPHandler(this.adminAuthenticator));
service.setHandlerResolver(SOAPHandlerResolver);
if (accountService == null) {
accountService = service.getAccountService();
}
setBindingProviderByAccountService();
ApplyAccountAccountResponse response = null;
LOGGER.info("Making a SOAP request.");
response = AccountService.applyAccount(request, getRequestInfo(), new Holder<ResponseInfo>());
LOGGER.info("SOAP request completed.");
return response.getApplyAccountResults();
}
SOAPHandlerResolver:
public class SOAPHandlerResolver implements HandlerResolver {
#SuppressWarnings("rawtypes")
private List<Handler> handlerList;
public SOAPHandlerResolver() {
this.handlerList = null;
}
#SuppressWarnings("rawtypes")
public List<Handler> getHandlerList() {
if (this.handlerList == null) {
this.handlerList = new ArrayList<>();
}
return this.handlerList;
}
#SuppressWarnings("rawtypes")
#Override
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerChain = new ArrayList<>();
if (this.handlerList == null || this.handlerList.isEmpty()) {
this.handlerList = new ArrayList<>();
this.handlerList.add(new SOAPHandler(null));
}
handlerChain.addAll(this.handlerList);
return handlerChain;
}
}
SOAPHandler
public class SOAPHandler implements SOAPHandler<SOAPMessageContext> {
private AdminAuth adminAuth;
private static final Logger LOGGER = LoggerFactory.getLogger(SOAPHandler.class);
public MosaicOnboardSOAPHandler(AdminAuth adminAuth) {
if (adminAuth == null) {
adminAuth = new AdminAuth();
LOGGER.info("AdminAuth found null. Creating new adminAuth instance.");
}
this.adminAuth = adminAuth;
}
#Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty) {
#SuppressWarnings("unchecked")
Map<String, List<String>> headers = (Map<String, List<String>>) context.get(MessageContext.HTTP_REQUEST_HEADERS);
if (headers == null) {
headers = new HashMap<>();
context.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
}
List<String> cookie = headers.get("Cookie");
if (cookie == null) {
cookie = new ArrayList<>();
headers.put("Cookie", cookie);
}
cookie.add(this.adminAuth.getToken());
}
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
return false;
}
#Override
public void close(MessageContext context) {
}
#Override
public Set<QName> getHeaders() {
return null;
}
}
AdminAuth:
#Component
public class AdminAuth {
#Autowired
private AdminBean adminBean;
private static final Logger LOG = LoggerFactory.getLogger(Admin.class);
private String token;
private void generateToken() {
try {
AdminTokenHelper adminTokenHelper = new AdminTokenHelper(adminBean.getAutheticationServerURL(), adminBean.getLicense());
token = adminTokenHelper.getToken(adminBean.getUsername(), adminBean.getPassword().toCharArray());
LOG.info("Token generation successful");
} catch (Exception ex) {
ex.printStackTrace();
LOG.error("Token generation failed");
LOG.error(ex.getMessage());
throw new RuntimeException("Token generation failed", ex);
}
}
#Cacheable(value = "tokenCache")
public String getToken() {
LOG.warn("Token not available. Generating a new token.");
generateToken();
return token;
}
}
ehcache.xml
<cache name="tokenCache" maxEntriesLocalHeap="1" eternal="false" timeToIdleSeconds="895" timeToLiveSeconds="895" memoryStoreEvictionPolicy="LRU"/>
Applcation
#EnableCaching
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.sources(Application.class).profiles(determineEnvironmentProfile());
}
}
In AdminAuth, it uses functional user to generate token. the token generated for authentication expires in 15 minutes. So my purpose was to write cache so that all the calls from ui can use the same token regardless of actual user. So i set the time 14:55 to generate new token. Now the problem comes when it's after 15 minutes and the cache doesn't evict the old toeken so that call uses the old and expired token and it fails.
I tried different eviction policies like LRU, LFU, FiFO but nothing is working. The calls are coming from ui through tomcat container in spring boot 1.3.
Why is this not getting evicted? What am i missing? Any help is appreciated
Replace #Cacheable(value = "tokenCache") with #Cacheable("tokenCache")
From the comments:
The dependency on spring-boot-starter-cache was missing. This prevented Spring Boot from automatically configuring the CacheManager. Once this dependency was added, the cache configuration worked.
See http://docs.spring.io/spring-boot/docs/1.3.x/reference/html/boot-features-caching.html
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;
}
}