Issue with a Spring integration test involving an entity with lazy collections - spring-test

I am experimenting with the new Spring Security Test framework. I have a test that involves returning a JPA entity with lazy collections.
The following test ends up in error because one of the lazy collections.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ActiveProfiles({ Profiles.TEST })
#ContextConfiguration(classes = { FullSecurityTestConfiguration.class, FullIntegrationTestConfiguration.class, BaseTestConfiguration.class, WebMvcConfiguration.class,
EnableHelperComponents.class })
#TestExecutionListeners(listeners = { ServletTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class, WithSecurityContextTestExcecutionListener.class })
public class CurriculumPermissionEvaluatorAuthorizationTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#After
public void clean() {
SecurityContextHolder.clearContext();
}
#Test
#WithUserDetails("balteo#yahoo.fr")
public void shouldAllowCurriculumRetrieval() throws Exception {
mockMvc.perform(get("/curriculum/findCurriculumById").param("id", "1")//
.contentType(MediaType.APPLICATION_JSON)//
.header("X-Ajax", "true"))//
.andDo(print())//
.andExpect(status().isOk());//
}
Here is the error message:
21:40:52.731 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction commit
21:40:52.732 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#4a17c31]
21:40:52.733 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#4a17c31] after transaction
21:40:52.733 [main] DEBUG o.s.o.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
21:40:52.795 [main] DEBUG o.s.w.s.m.m.a.ResponseBodyAdviceChain - Invoking ResponseBodyAdvice chain for body=Curriculum [drivingLicense=true, smoker=false, maxNumberChildren=2, workExperienceInYears=11, dateOfBirth=1975-01-06 00:00:00.0, telephoneNumber=0142778899, firstName=Juliette, visible=true, validated=true]
21:40:52.796 [main] DEBUG o.s.w.s.m.m.a.ResponseBodyAdviceChain - After ResponseBodyAdvice chain body=Curriculum [drivingLicense=true, smoker=false, maxNumberChildren=2, workExperienceInYears=11, dateOfBirth=1975-01-06 00:00:00.0, telephoneNumber=0142778899, firstName=Juliette, visible=true, validated=true]
21:40:52.819 [main] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
21:40:52.820 [main] DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
21:40:52.820 [main] DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
21:40:52.821 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Null ModelAndView returned to DispatcherServlet with name '': assuming HandlerAdapter completed request handling
21:40:52.821 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Successfully completed request
What I am getting wrong in my test setup? Why is the JPA entity manager being closed too early? How can I circumvent this issue?
edit 1:
Method from controller:
#RequestMapping(value = "/findCurriculumById", method = RequestMethod.GET, produces = "application/json")
#ResponseBody
public Curriculum findCurriculumById(#RequestParam Long id) {
return curriculumService.findCurriculum(id);
}
Method from service:
#Override
#PreAuthorize("isAuthenticated() AND hasPermission(#curriculumId, 'curriculumByIdOwnerPermission')")
#Cacheable(value = CacheConfiguration.DATABASE_CACHE_NAME, key = "'curriculum.Id:' + #curriculumId")
public Curriculum findCurriculum(Long curriculumId) {
return curriculumRepository.findOne(curriculumId);
}
edit 2:
I realized that the test is actually in error even though I've annotated the test method with #Transactional as follows:
#Test
#WithUserDetails("balteo#yahoo.fr")
#Transactional
public void shouldAllowCurriculumRetrieval() throws Exception {
mockMvc.perform(get("/curriculum/findCurriculumById").param("id", "1")//
.contentType(MediaType.APPLICATION_JSON)//
.header("X-Ajax", "true"))//
.andDo(print())//
.andExpect(status().isOk());//
}
Here is the error message:
16:54:31.747 [main] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter#ab4a4ae, returned: 1
16:54:31.747 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Authorization successful
16:54:31.747 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - RunAsManager did not change Authentication object
16:54:31.752 [main] DEBUG o.s.w.s.m.m.a.ResponseBodyAdviceChain - Invoking ResponseBodyAdvice chain for body=Curriculum [drivingLicense=true, smoker=false, maxNumberChildren=2, workExperienceInYears=11, dateOfBirth=1975-01-06 00:00:00.0, telephoneNumber=0142778899, firstName=Juliette, visible=true, validated=true]
16:54:31.752 [main] DEBUG o.s.w.s.m.m.a.ResponseBodyAdviceChain - After ResponseBodyAdvice chain body=Curriculum [drivingLicense=true, smoker=false, maxNumberChildren=2, workExperienceInYears=11, dateOfBirth=1975-01-06 00:00:00.0, telephoneNumber=0142778899, firstName=Juliette, visible=true, validated=true]
16:54:31.767 [main] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
16:54:31.768 [main] DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
16:54:31.768 [main] DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
16:54:31.768 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Null ModelAndView returned to DispatcherServlet with name '': assuming HandlerAdapter completed request handling
16:54:31.768 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Successfully completed request
MockHttpServletRequest:
HTTP Method = GET
Request URI = /curriculum/findCurriculumById
Parameters = {id=[1]}
Headers = {Content-Type=[application/json], X-Ajax=[true]}
Handler:
Type = com.bignibou.controller.curriculum.CurriculumController
Method = public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)
Async:
Was async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotWritableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 500
Error message = null
Headers = {Content-Type=[application/json]}
Content type = application/json
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
16:54:31.770 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction rollback
16:54:31.770 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Rolling back JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#76d0bc1d]
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#76d0bc1d] after transaction
16:54:31.771 [main] DEBUG o.s.o.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Resuming suspended transaction after completion of inner transaction
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Creating new transaction with name [com.bignibouX.tests.security.curriculum.CurriculumPermissionEvaluatorAuthorizationTest.clean]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#3972d402] for JPA transaction
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle#21bcecc5]
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction commit
16:54:31.772 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#3972d402]
16:54:31.772 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#3972d402] after transaction
edit 3:
Here is the relevant part of Curriculum:
#Entity
public class Curriculum {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "curriculum")
private Set<WorkExperience> workExperiences;

Your test method is not transactional.
You need to annotate either CurriculumPermissionEvaluatorAuthorizationTest or shouldAllowCurriculumRetrieval() with #Transactional.

Related

What's the best way to handle reconnection with MqttPahoMessageHandler as token has now expired?

I have used the following example as a basis for my own code to publish to a MQTT server: https://github.com/spring-projects/spring-integration-samples/blob/master/basic/mqtt/src/main/java/org/springframework/integration/samples/mqtt/Application.java
I have a particular use case where the password is a token in particular a keycloak token which will expire. If for whatever reason the spring application loses connection with the MQTT server and tries to reconnect the token will have expired and an MqttSecurityException: Not authorized to connect exception will be thrown. I tried extending the method connectionLost in MqttPahoMessageHandler but as the MqttPahoClientFactory & IMqttAsyncClient are private final there is not much I can do. Wondering if there is any other approach I've not thought of or is the library just not meant to be used like this???
Thanks for any replies.
We get the MqttConnectOptions from the client factory each time we try to connect so you should be able to just update the password there.
If that doesn't work for some reason, open a new feature request.
EDIT
Regarding your comment, what's wrong with this?
#Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
MqttConnectOptions options = new MqttConnectOptions();
options.setServerURIs(new String[] { "tcp://localhost:1883" });
options.setUserName("guest");
options.setPassword("guest".toCharArray());
factory.setConnectionOptions(options);
return factory;
}
#Bean
public ApplicationRunner runner(MqttPahoClientFactory mqttClientFactory, MqttPahoMessageHandler handler) {
return args -> {
Thread.sleep(30_000);
System.out.println("Changing password");
mqttClientFactory.getConnectionOptions().setPassword("foo".toCharArray());
handler.stop();
handler.start();
};
}
foo
2020-03-10 17:42:33.560 INFO 95638 --- [iSampleConsumer] siSample
: foo sent to MQTT, received from MQTT
Changing password
foo
2020-03-10 17:43:08.705 ERROR 95638 --- [ask-scheduler-3] o.s.integration.handler.LoggingHandler
: org.springframework.messaging.MessageHandlingException: error occurred in message handler [bean 'mqttOutbound' for component 'mqttOutFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#1'; defined in: 'com.example.demo.So60610337Application'; from source: 'org.springframework.core.type.StandardMethodMetadata#79da8dc5']; nested exception is org.springframework.messaging.MessagingException: Failed to connect; nested exception is Bad user name or password (4), failedMessage=GenericMessage [payload=foo sent to MQTT, headers={id=4eab5b52-726f-7ea3-252d-77c4d0401cc8, timestamp=1583876588662}]
...
Caused by: Bad user name or password (4)

Failed to get destinations... timed-out and fallback disabled

We use CAP to query data through ODATA V2 caller. But there is exception during my local env testing. I could successfully get the result in beginning. After I tested several times, it would raise exception every time. Please help me fix this issue.
Destination setup:
URL=https\://cxs-calmdevprovider-calmdev-sap-calm-imp-pjm-srv.cfapps.sap.hana.ondemand.com
Name=PJM-SRV-DEST
TrustAll=TRUE
ProxyType=Internet
Type=HTTP
Authentication=AppToAppSSO
Two applications bind to one XSUAA. And set the VCAP_SERVICE for local run.
try {
logger.debug("==> now execute query on Products");
FilterExpression filter = new FilterExpression("project_guid", "eq",
ODataType.of(UUID.fromString("10c0b945-1ff5-4510-a160-24294bfe3b58")));
CacheKey cKey = CacheKey.ofTenantAndUserIsolation();
ODataQueryResult result = ODataQueryBuilder.withEntity("/odata/v2/CBLD_PROJECT_SRV", "CBLD_C_PROJECT_MS_TP")
.filter(filter).enableMetadataCache(cKey).build().execute("PJM-SRV-DEST");
logger.debug("==> After calling backend OData V2 service: result: " + result);
List<ProjectMilestone> projectMilestones = result.asList(ProjectMilestone.class);
logger.info(projectMilestones.toString());
} catch (Exception e) {
logger.error("==> Exception calling backend OData V2 service for Query of Products: " + e.getMessage());
ErrorResponse errorResponse =
ErrorResponse.getBuilder().setMessage("There is an error. Check the logs for the details.")
.setStatusCode(500).setCause(e).response();
return UpdateResponse.setError(errorResponse);
}
Exception stack:
2019-08-20 14:05:03,446 DEBUG [http-nio-8081-exec-2] - [com.sap.calm.imp.tkm.srv.odata.handler.TaskServiceHandler] [tenant_id=05e65d26-f3e2-4937-9987-eb412f4cd732, component_id=7d44b23e-c4c1-42ba-9c60-0048c14a6937, component_name=cxs-calm-build-tkm-srv, organization_name=-, component_type=application, endpoint=PATCH: /odata/v2/CBLD_TASK_SRV/CBLD_C_TASK_TP(guid%27cba383f1-809f-40f1-8c76-cddd2b381c4c%27), space_name=CALMDev, component_instance=0, organization_id=-, correlation_id=825395c1-583d-4e5c-8ae4-6f9eaf19aae2, space_id=3bbd276d-7965-4625-aae9-da06139845ea, request_id=-, container_id=-] - ==> now execute query on Products
2019-08-20 14:05:12,322 WARN [http-nio-8081-exec-2] - [com.netflix.config.sources.URLConfigurationSource] [tenant_id=05e65d26-f3e2-4937-9987-eb412f4cd732, component_id=7d44b23e-c4c1-42ba-9c60-0048c14a6937, component_name=cxs-calm-build-tkm-srv, organization_name=-, component_type=application, endpoint=PATCH: /odata/v2/CBLD_TASK_SRV/CBLD_C_TASK_TP(guid%27cba383f1-809f-40f1-8c76-cddd2b381c4c%27), space_name=CALMDev, component_instance=0, organization_id=-, correlation_id=825395c1-583d-4e5c-8ae4-6f9eaf19aae2, space_id=3bbd276d-7965-4625-aae9-da06139845ea, request_id=-, container_id=-] - No URLs will be polled as dynamic configuration sources.
2019-08-20 14:05:19,270 ERROR [http-nio-8081-exec-2] - [com.sap.cloud.sdk.odatav2.connectivity.ODataQuery] [tenant_id=05e65d26-f3e2-4937-9987-eb412f4cd732, component_id=7d44b23e-c4c1-42ba-9c60-0048c14a6937, component_name=cxs-calm-build-tkm-srv, organization_name=-, component_type=application, endpoint=PATCH: /odata/v2/CBLD_TASK_SRV/CBLD_C_TASK_TP(guid%27cba383f1-809f-40f1-8c76-cddd2b381c4c%27), space_name=CALMDev, component_instance=0, organization_id=-, correlation_id=825395c1-583d-4e5c-8ae4-6f9eaf19aae2, space_id=3bbd276d-7965-4625-aae9-da06139845ea, request_id=-, container_id=-] - Could not connect to destination service [No Access] :com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException: Failed to get destinations: com.sap.cloud.sdk.cloudplatform.connectivity.DestinationServiceCommand#t=05e65d26-f3e2-4937-9987-eb412f4cd732#u= timed-out and fallback disabled. If your application is running on Cloud Foundry, make sure to have a binding to both the destination service and the authorization and trust management (xsuaa) service, AND that you either properly secured your application or have set the "ALLOW_MOCKED_AUTH_HEADER" environment variable to true. Please note that authentication types with user propagation, for example, principal propagation or the OAuth2 SAML Bearer flow, require that you secure your application and will not work when using the "ALLOW_MOCKED_AUTH_HEADER" environment variable. If your application is not running on Cloud Foundry, for example, when deploying to a local container, consider declaring the "destinations" environment variable to configure destinations.
2019-08-20 14:05:19,271 ERROR [http-nio-8081-exec-2] - [com.sap.cloud.sdk.odatav2.connectivity.ODataQuery] [tenant_id=05e65d26-f3e2-4937-9987-eb412f4cd732, component_id=7d44b23e-c4c1-42ba-9c60-0048c14a6937, component_name=cxs-calm-build-tkm-srv, organization_name=-, component_type=application, endpoint=PATCH: /odata/v2/CBLD_TASK_SRV/CBLD_C_TASK_TP(guid%27cba383f1-809f-40f1-8c76-cddd2b381c4c%27), space_name=CALMDev, component_instance=0, organization_id=-, correlation_id=825395c1-583d-4e5c-8ae4-6f9eaf19aae2, space_id=3bbd276d-7965-4625-aae9-da06139845ea, request_id=-, container_id=-] - Could not connect to destination service [No Access] : [Ljava.lang.StackTraceElement;#7beb95af
2019-08-20 14:05:19,280 ERROR [http-nio-8081-exec-2] - [com.sap.calm.imp.tkm.srv.odata.handler.TaskServiceHandler] [tenant_id=05e65d26-f3e2-4937-9987-eb412f4cd732, component_id=7d44b23e-c4c1-42ba-9c60-0048c14a6937, component_name=cxs-calm-build-tkm-srv, organization_name=-, component_type=application, endpoint=PATCH: /odata/v2/CBLD_TASK_SRV/CBLD_C_TASK_TP(guid%27cba383f1-809f-40f1-8c76-cddd2b381c4c%27), space_name=CALMDev, component_instance=0, organization_id=-, correlation_id=825395c1-583d-4e5c-8ae4-6f9eaf19aae2, space_id=3bbd276d-7965-4625-aae9-da06139845ea, request_id=-, container_id=-] - ==> Exception calling backend OData V2 service for Query of Products: Unable to execute the OData operation : Failed to execute OData request.
It takes almost 9 seconds from your DEBUG message to the first WARN, complaining about the timeout. In fact the default timeout for SAP Cloud SDK internal DestinationServiceCommand is set to 6 seconds.
If querying all destinations really takes longer than 6 seconds in your case, then I would suggest a workaround until we find a proper solution:
1.) Create a new class CustomHystrixPropertiesStrategy
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesCommandDefault;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import javax.annotation.Nonnull;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class CustomHystrixPropertiesStrategy extends HystrixPropertiesStrategy
{
private final Map<HystrixCommandKey, HystrixCommandProperties> commandProperties = new ConcurrentHashMap<>();
#Nonnull
private HystrixPropertiesStrategy delegate;
public static CustomHystrixPropertiesStrategy register()
{
final HystrixPropertiesStrategy delegate = HystrixPlugins.getInstance().getPropertiesStrategy();
final CustomHystrixPropertiesStrategy strategy = new CustomHystrixPropertiesStrategy();
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerPropertiesStrategy(strategy);
strategy.delegate = delegate;
return strategy;
}
public void addProperties(
#Nonnull final HystrixCommandKey key,
#Nonnull final HystrixCommandProperties.Setter customSetter )
{
final HystrixCommandProperties properties = new HystrixPropertiesCommandDefault(key, customSetter);
commandProperties.put(key, properties);
}
#Override
public
HystrixCommandProperties
getCommandProperties( final HystrixCommandKey key, final HystrixCommandProperties.Setter builder )
{
if( commandProperties.containsKey(key) ) {
return commandProperties.get(key);
}
return delegate.getCommandProperties(key, builder);
}
}
2.) Register the custom strategy instance
You need to run the following operation and keep the returned instance.
This can happen at a central point of your application or statically in your service class, such that the instruction is only run once.
final CustomHystrixPropertiesStrategy strategy = CustomHystrixPropertiesStrategy.register();
Note: If you want to make sure this is run only once, you can put it into a static block:
static {
final CustomHystrixPropertiesStrategy strategy = CustomHystrixPropertiesStrategy.register();
}
3.) Add the custom timeout property for a specific Hystrix command.
You can add the following code in front of the (currently) breaking call.
// reconstruct existing command key
final String tenantId = TenantAccessor.getCurrentTenantIfAvailable().map(Tenant::getTenantId).orElse(null);
final String commandKey = HystrixUtil.getCommandKey(DestinationServiceCommand.class, tenantId, null);
final HystrixCommandKey destinationServiceCommandKey = HystrixCommandKey.Factory.asKey(commandKey);
// construct custom command properties
final HystrixCommandProperties.Setter customSetter =
HystrixCommandProperties
.Setter()
.withExecutionTimeoutInMilliseconds(60000) // 60 SECONDS INSTEAD OF 6 SECONDS
.withCircuitBreakerEnabled(true)
.withCircuitBreakerSleepWindowInMilliseconds(1000)
.withFallbackEnabled(false);
// apply custom Command properties
strategy.addProperties(destinationServiceCommandKey, customSetter);
Make sure the reference to the custom strategy is in the scope.
CustomHystrixPropertiesStrategy strategy;
If it is not working as expected, please try to add a breakpoint or some log output to ensure the new code is actually reached at application runtime.

Why am I getting javax.xml.bind.UnmarshalException when calling a soap web service

I am trying consume a SOAP web Service and I have written my SOAP client using Spring-ws. This also included a message signing. However when I am trying to trigger a request I am getting the exception. From the server standpoint, the server is sending a successful response but I am not able to unmarshall it.
I followed quite a few suggestion including one from this
UnmarshallingFailureException, unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault")
Following the above link however removed the exception but in turn I am getting a null response object and I could not trace out where the response went missing.
This is my Config class methods:
public WebServiceTemplate accountsInquiryWebServiceTemplate() throws Exception {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
Jaxb2Marshaller accountsInquiryMarshaller = accountsInquiryMarshaller();
webServiceTemplate.setMarshaller(accountsInquiryMarshaller);
webServiceTemplate.setUnmarshaller(accountsInquiryMarshaller);
ClientInterceptor[] clientInterceptors = new ClientInterceptor[1];
clientInterceptors[0] = wsClientSecurityInterceptor();
webServiceTemplate.setInterceptors(clientInterceptors);
webServiceTemplate.setCheckConnectionForFault(true);
webServiceTemplate.setDefaultUri(serviceURL);
return webServiceTemplate;
}
private Jaxb2Marshaller accountsInquiryMarshaller() {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setContextPaths(new String[]{
"com.accounting.integrationservice"
});
return jaxb2Marshaller;
}
private Wss4jSecurityInterceptor wsClientSecurityInterceptor() throws Exception {
Wss4jSecurityInterceptor wss4jSecurityInterceptor = new Wss4jSecurityInterceptor();
wss4jSecurityInterceptor.setSecurementActions("Signature");
wss4jSecurityInterceptor.setSecurementUsername(username);
wss4jSecurityInterceptor.setSecurementPassword(password);
wss4jSecurityInterceptor.setSecurementSignatureCrypto(clientCrypto());
wss4jSecurityInterceptor.setValidationActions("Signature");
return wss4jSecurityInterceptor;
}
private Crypto clientCrypto() throws Exception {
CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean();
cryptoFactoryBean.setKeyStoreLocation(resourceLoader.getResource("classpath:accountsInquiry/keystore/" + keyStoreLocation));
cryptoFactoryBean.setKeyStorePassword(keyStorePassword);
cryptoFactoryBean.afterPropertiesSet();
return cryptoFactoryBean.getObject();
}
The client calls as below:
Response response = accountsWebServiceTemplate.marshalSendAndReceive(request);
The exception stack that I am getting is as below : Its similar to the referred link above, so not putting the entire exception stack
org.springframework.ws.soap.security.AbstractWsSecurityInterceptor.handleResponse(AbstractWsSecurityInterceptor.java:247)
- Could not validate request: No WS-Security header found
org.springframework.oxm.UnmarshallingFailureException: JAXB unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException: unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault"). Expected elements are http://www.qualityaccounts.com/IntegrationService/schemas/ProductInfo/v1}
at org.springframework.oxm.jaxb.Jaxb2Marshaller.convertJaxbException(Jaxb2Marshaller.java:911)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:784)
at org.springframework.ws.support.MarshallingUtils.unmarshal(MarshallingUtils.java:62)
at org.springframework.ws.client.core.WebServiceTemplate$3.extractData(WebServiceTemplate.java:413)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:619)
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:555)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:390)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:383)
Ok So, I was trying to use "Signature" to validate the Security Header of the response even though there was no header. But thing is if I didn't specify the ValidationAction it was throwing NullPointerException so it was expecting at least one ValidationAction.
To counter all this I told the Spring security not to validate the response by this piece of code.
wss4jSecurityInterceptor.setValidateResponse(false);
Thanks to this article
Spring Security - Wss4jSecurityInterceptor - Nullpointer

Getting error "Target object of type ...has no eligible methods for handling Messages"

I am using spring integration in my project.
I have recently added the below method for my service activator PQMessageHandler:
#SuppressWarnings("unchecked")
public Message<?> getDatabaseProductName(Message<?> inMessage){
Message<?> outMessage = null;
String databaseProductName = null;
try {
//get the database name.
databaseProductName = DataSourceUtils.getConnection(rd).getMetaData().getDatabaseProductName();
} catch (SQLException e) {
throw new RuntimeException(e);
}
Document mainDom = XmlParserUtil.convertString2Document((String)inMessage.getPayload());
//include database name in the response xml
Element databaseName = mainDom.getRootElement().addElement("databaseName");
databaseName.setText(databaseProductName);
MessageHeaders inMsgHdrs = inMessage.getHeaders();
MessageBuilder<?> msgBuilder = MessageBuilder.withPayload(mainDom.asXML()).copyHeaders(inMsgHdrs);
outMessage= msgBuilder.build();
logger.debug("\n ************************** External System Request Message ********************* \n");
logger.debug(outMessage);
logger.debug("\n ******************************************************************************** \n");
return outMessage;
}
I have configured the code that calls the above service activator is as follows:
<int:channel id="PQPutUserBAInformation-Add-DatabaseProductName" />
<int:service-activator input-channel="PQPutUserBAInformation-Add-DatabaseProductName"
output-channel="PQPutUserBAInformation-Database-Router"
ref="msgHandler"
method="getDatabaseProductName" />
<int:router input-channel="PQPutUserBAInformation-Database-Router" default-output-channel="PQPutUserBAInformation-Default-InsertUserId-Channel" resolution-required="false" expression="#xpath(payload, '//databaseName').toLowerCase().contains('db2')">
<int:mapping value="true" channel="PQPutUserBAInformation-DB2-Request"/>
</int:router>
The above code works fine without any issues in the development environment. Weirdly when I deploy my application in client environment, I am getting the following error:
2017-04-10 10:41:50,525 INFO [org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler] (ServerService Thread Pool -- 65) Shutting down ExecutorService 'taskScheduler'
2017-04-10 10:41:50,526 ERROR [org.springframework.web.context.ContextLoader] (ServerService Thread Pool -- 65) Context initialization failed: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.integration.config.ConsumerEndpointFactoryBean#345': Cannot resolve reference to bean 'org.springframework.integration.config.ServiceActivatorFactoryBean#61' while setting bean property 'handler'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.integration.config.ServiceActivatorFactoryBean#61': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Target object of type [class com.dsths.cs.awd.jobs.PQMessageHandler] has no eligible methods for handling Messages.
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:329) [spring-beans-3.2.2.RELEASE.jar:3.2.2.RELEASE]
I am assuming that the recent changes are causing the issue. The log message is not clearly saying which service activator is causing the issue.
I googled about the error and confirmed that
1) There is no misspelled method attribute in the
configuration
2)The method is public
3)I haven't defined requires-reply="true" attribute in the configuration.
Any thoughts on troubleshooting the issue?
Figured out the root cause for the issue. The exception was due to EAR caching. Cleaning the EAR folder on Linux server and redeploying the war file freshly has worked out.

No LoginModules configured for DefaultPrincipalMapping WAS Libert y profile 8.5.5.9

I used the info in this link https://www.ibm.com/support/knowledgecenter/SSEQTP_8.5.5/com.ibm.websphere.wlp.doc/ae/twlp_dev_prog_login_auth_data.html As per my info which I got from this discussion thread. https://developer.ibm.com/answers/questions/261209/websphere-liberty-profile-and-j2c-authdata-retriev-1.html
The DefaultPrincipalMapping programmatic login is avaiable and should work. I configured everything updated to Liberty profile v 8.5.5.9 , yet my code throws exception at [err] javax.security.auth.login.LoginException: No LoginModules configured for DefaultPrincipalMapping [err] at javax.security.auth.login.LoginContext.init(LoginContext.java:264) [err] at javax.security.auth.login.LoginContext.(LoginContext.java:417) Here is my code snippet..
Context context = new InitialContext();
String user = (String) context.lookup("cell/persistent/ADBindUser");
String password = (String) context.lookup("cell/persistent/ADBindPwd");
Map<String, String> map = new HashMap<String, String>();
map.put(Constants.MAPPING_ALIAS, "WPSBIND");
// CallbackHandler callbackHandler = // WSMappingCallbackHandlerFactory.getInstance().getCallbackHandler(map, null);
CallbackHandler callbackHandler =new WSMappingCallbackHandler(map, null);
LoginContext loginContext =
new LoginContext("DefaultPrincipalMapping", callbackHandler);
try{
loginContext.login();
}catch(LoginException le) {
}
Can any one please tell me what I am missing here.
Instead of "DefaultPrincipalMapping" the login configuration "system.DEFAULT" did the job for me.
The final hint was hidden in the documentation:
system.DEFAULT
Used for login when no JAAS configuration is specified.
...
The system.WEB_INBOUND and system.DEFAULT configurations have these
default login modules in this order: hashtable, userNameAndPassword,
certificate, and token. The WSLogin configuration has the proxy login module
as the default login module, and the proxy delegates all the
operations to the real login module in system.DEFAULT.
WSLogin fails with an Unrecognized Callback in case the application uses e.g. org.jboss.security.auth.callback.UsernamePasswordHandler as the callback. If the application cannot be modified, system.DEFAULT provides an alternative.

Resources