IllegalStateException when trying to .getSessionMap() from a Session Scoped Bean - session

I'm new to Java and JSF. I need help with an IllegalStateException. Here's the scenario:
In my current project i have this Session Scoped bean for the application menu:
public final class MenuBean implements Serializable{
private MenuModel model;
private FacesContext context = FacesContext.getCurrentInstance();
public MenuModel getModel() {
return model;
}
public MenuBean() {
updateMenu();
}
public void updateMenu(){
Map session = (Map<String,Object>) context.getExternalContext().getSessionMap();
EUser user = (EUser) session.get(UserBean.USER_SESSION_KEY);
...
}
private MethodExpression createMethodExpression(String action) {
...
}
}
At some point of my logic, i need to update the menu, so i do this:
ExternalContext extContext = context.getExternalContext();
Map sMap = (Map<String,Object>) extContext.getSessionMap();
MenuBean menu = (MenuBean) sMap.get("menuBean");
menu.updateMenu();
The bean constructs fine, but when i try to manually update it as shown above, i get and IllegalStateException on the 1st line of the update method updateMenu()
I don't understand what's wrong, since I can get the session map with that same call whe the menu is build in the first time.
Also, using the NetBeans debugger, i can see that the instance of MenuBean is correctly recovered.
Can you guys help me?

The FacesContext is stored in the HTTP request thread. You should absolutely not declare and assign it as an instance variable of an instance which lives longer than the HTTP request (and preferably also just not when it's already request based -it's bad design). The FacesContext instance is released and invalidated when the HTTP request finishes. In any subsequent HTTP request the instance is not valid anymore. There's means of an illegal state. That explains the IllegalStateException you're seeing.
You need to remove the following line:
private FacesContext context = FacesContext.getCurrentInstance();
And fix your code to get it only threadlocal in the method block:
Map<String, Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
// ...
You can always assign it as a variable, but that should only be kept threadlocal:
FacesContext context = FacesContext.getCurrentInstance();
Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
// ...
Unrelated to the concrete problem, using #ManagedProperty has been easier in this particular case.
public final class MenuBean implements Serializable {
#ManagedProperty("#{user}")
private EUser user;
// ...
}
JSF will then inject it for you.

Related

Hibernate LazyInitialization exception in console Spring Boot with an open session

I'm not sure if anyone has experienced this particular twist of the LazyInitialization issue.
I have a console Spring Boot application (so: no views - everything basically happens within a single execution method). I have the standard beans and bean repositories, and the typical lazy relationship in one of the beans, and it turns out that unless I specify #Transactional, any access to any lazy collection automatically fails even though the session stays the same, is available, and is open. In fact, I can happily do any session-based operation as long as I don't try to access a lazy collection.
Here's a more detailed example :
#Entity
class Path {
... `
#OneToMany(mappedBy = "path",fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#OrderBy("projectOrder") `
public List<Project> getProjects() {
return projects; `
}`
}
Now, the main method does something as simple as this:
class SpringTest {
... `
#Autowired
private PathRepository pathRepository;
void foo() {
Path path = pathRepository.findByNameKey("...");
System.out.println(path.getProjects()); // Boom <- Lazy Initialization exception}
}
Of course if I slap a #Transactional on top of the method or class it works, but the point is - why should I need that? No one is closing the session, so why is Hibernate complaining that there´s no session when there is one?
In fact, If I do:
void foo() {
System.out.println(entityManager.unwrap(Session.class));
System.out.println(entityManager.unwrap(Session.class).isOpen());
Path basic = pathRepository.findByNameKey("...");
System.out.println(entityManager.unwrap(Session.class));
System.out.println(entityManager.unwrap(Session.class).isOpen());
System.out.println(((AbstractPersistentCollection)basic.projects).getSession());
Path p1 = pathRepository.findByNameKey("....");
}
I get that the session object stays the same the whole time, it stays open the whole time, but the internal session property of the collection is never set to anything other than null, so of course when Hibernate tries to read that collection, in its withTemporarySessionIfNeeded method it immediately throws an exception
private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
SharedSessionContractImplementor tempSession = null;
if (this.session == null) {
if (this.allowLoadOutsideTransaction) {
tempSession = this.openTemporarySessionForLoading();
} else {
this.throwLazyInitializationException("could not initialize proxy - no Session");
}
So I guess my question would be - why is this happening? Why doesn't Hibernate store or access the session from which a bean was fetched so that it can load the lazy collection from it?
Digging a bit deeper, it turns out that the repository method executying the query does a
// method here is java.lang.Object org.hibernate.query.Query.getSingleResult()
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
if (SharedEntityManagerCreator.queryTerminatingMethods.contains(method.getName())) {
...
EntityManagerFactoryUtils.closeEntityManager(this.entityManager); // <--- Why?!?!?
this.entityManager = null;
}
...
}
and the above closeEntityManager calls unsetSession on all collections:
SharedSessionContractImplementor session = this.getSession();
if (this.collectionEntries != null) {
IdentityMap.onEachKey(this.collectionEntries, (k) -> {
k.unsetSession(session);
});
}
But why?!
(Spring Boot version is 2.7.8)
So, after researching more it appears that this is standard behavior in Spring Boot - unless you use your own EntityManager, the one managed automatically by Spring is either attached to a #Transactional boundary, or opens and closes for each query.
Some relevant links:
Does Entity manager needs to be closed every query?
Do I have to close() every EntityManager?
In the end, I ended using a TransactionTemplate to wrap my code into a transaction without having to mark the whole class #Transactional.

SOLVED. Mock service.insert() works with ArgumentMatchers.any() only, triggers exception otherwise

Im creating a test class using Mockito and everything is runnig OK, except by one mock that calls a service layer method and it only works if the input parameter is any() and it throws the following exception if the input parameter is native from the method.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "com.devsuperior.dscatalog.dto.ProductDTO.getId()" because "productDTO" is null at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
Lets go through the code:
The service layer method, productDTO is a DTO class from a ordinary Product entity.
#Transactional
public ProductDTO insert(ProductDTO productDTO) {
Product entity = new Product();
copyDtoToEntity(productDTO, entity);
entity = repository.save(entity); // reposity.save() returns a reference to object saved in DB
return new ProductDTO(entity);
}
the controller layer method:
#PostMapping
public ResponseEntity<ProductDTO> insert(#RequestBody ProductDTO productDTO){
productDTO = service.insert(productDTO); //[1]
URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(productDTO.getId()).toUri();
return ResponseEntity.created(uri).body(productDTO);
}
Before I continue I ran in debug mode this code and the ProdutDTO was correctly instanciated until the mock captured the service.insert(productDTO) call and after this line [1] productDTO = null
my test class:
#WebMvcTest(ProductResource.class)
public class ProductResourceTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private ProductService service;
#Autowired
private ObjectMapper objectMapper;
private PageImpl<ProductDTO> page;
private ProductDTO productDTO;
private long existingId;
private long nonExistingId;
private long dependentId;
private long productBadCategoryId;
private String jsonBody;
#BeforeEach
void setUp() throws Exception {
productDTO = Factory.createProductDTO();
existingId = Factory.getExistingProductId();
nonExistingId = 100L;
dependentId = 50L;
productBadCategoryId = 200L;
page = new PageImpl<>(List.of(productDTO));
when(service.findAllPaged(ArgumentMatchers.any())).thenReturn(page);
when(service.findById(existingId)).thenReturn(productDTO);
when(service.findById(nonExistingId)).thenThrow(ResourceNotFoundException.class);
when(service.update(eq(existingId), any())).thenReturn(productDTO);
when(service.update(eq(nonExistingId), any())).thenThrow(ResourceNotFoundException.class);
when(service.update(eq(productBadCategoryId), any())).thenThrow(NestedResourceNotFoundException.class);
doNothing().when(service).delete(existingId);
doThrow(ResourceNotFoundException.class).when(service).delete(nonExistingId);
doThrow(DatabaseException.class).when(service).delete(dependentId);
when(service.insert(any(ProductDTO.class))).thenReturn(productDTO); // IT WORKS! [2]
//when(service.insert( productDTO )).thenReturn(productDTO); // IT IT DOESNT WORKS! [3]
jsonBody = objectMapper.writeValueAsString(productDTO);
}
and finally the test method where the mock call triggers the exception
#Test
public void insertShouldReturnProductDTOCreated() throws Exception {
ResultActions result = mockMvc.perform(post("/products").content(jsonBody)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON));
result.andExpect(status().isCreated());
result.andExpect(jsonPath("$.id").exists());
result.andExpect(jsonPath("$.name").exists());
result.andExpect(jsonPath("$.description").exists());
}
Whenever I uncomment line [3] and comment line [2] the exception above is rised.
Of course I could let this way (its working), but If I want to raise an exception for the case the object to be inserted has some issue,I could build a new service.insert() mock to test the throw of the exception. As it is I can´t because I cant diferentiate a any() object from another one.I have read some similar problems like mine and the proposed solution was to add #Autowired annotation with service variable, but in my case still the issue remains.
The solution and a tentative of explanation what was happening:
Override HashCode/Equals methods in ProducDTO class.
When serializing ProductDTO class (this line-> jsonBody = objectMapper.writeValueAsString(productDTO) ) and upon reception on resource layer it turns again into an object but its new reference address it is not the same and for instance the non override equals will fail (it compares the reference address and not their content).

Session Fixation and Session Scoped Beans in JSF 2.3 with CDI

It's a common best practice to renew the HTTP session when logging in a user. This will force a new session ID, avoiding session fixation vulnerabilities.
Is there a preferred pattern for implementing this with CDI when #SessionScoped beans are involved? The difficulty is that by invalidating the current HTTP session, you'll then get a different session-scoped bean with the next request, but not until the next request.
For example, assume a session bean for storing user login information:
#Named("sessionbean")
#SessionScoped
public class SessionBean implements Serializable {
private int userId;
private String username;
private List<String> privileges;
// Accessors omitted
}
And another bean for managing the login:
#Named("loginbean")
#ViewScoped
public class LoginBean implements Serializable {
private String username;
private String password;
#Inject private SessionBean session;
#Inject private SessionManager sessionManager;
#Inject private PrivilegeManager privilegeManager;
public String doLogin() {
String destinationUrl;
if (validate(username, password)) {
FacesContext context = FacesContext.getCurrentInstance();
// force renewal of HTTP session
context.getExternalContext().invalidateSession();
// retrieve new session bean ** No longer works with CDI **
Application app = context.getApplication();
session = app.evaluateExpressionGet(context, "#{sessionbean}", SessionBean.class);
session.setUsername(username);
session.setSessionId(sessionManager.createNewSession(username));
session.setPrivileges(privilegeManager.getPrivileges(username));
destinationUrl = createLandingPageUrl();
} else {
destinationUrl = createFailureUrl("Unknown user or password");
}
return destinationUrl;
}
}
With Managed Beans this would retrieve a new SessionBean, but with CDI, the code above would just return the same SessionBean. Any recommendations or clever ideas?
The difficulty is that by invalidating the current HTTP session, you'll then get a different session-scoped bean with the next request, but not until the next request.
Then don't invalidate the session, but change the session ID. In other words, don't use HttpSession#invalidate(), but use HttpServletRequest#changeSessionId() (new since Servlet 3.1, which you should undoubtedly already be using given that you're using JSF 2.3).
In code, replace
// force renewal of HTTP session object
context.getExternalContext().invalidateSession();
by
// force renewal of HTTP session ID
((HttpServletRequest) context.getExternalContext().getRequest()).changeSessionId();
This basically changes the JSESSIONID cookie without changing the HttpSession. It's perfect for session fixation prevention.
Explicitly invalidating the session is usually only useful during logout.
I'm going to restrict this answer to be solely about CDI since I am not a security expert. I also don't know whether the general thing being asked for is a good idea or not. Regardless, here is how I think you would do what you're asking for.
Expressed in purely CDI terms, the question can be rephrased like:
I have an object that I know came from a particular Context. I know the lifecycle of objects produced by this Context. How can I properly tell the Context to invalidate the current object that it is managing, and load or create a new one?
The general approach is going to be:
#Inject a Provider<SessionBean> instead of SessionBean directly (this will let you ask CDI for the "new" object properly)
#Inject a BeanManager (so you can get the right Context that manages SessionScoped objects)
ask the BeanManager to give you the AlterableContext corresponding to the SessionScoped annotation
tell the AlterableContext to destroy the current bean's contextual instance
call Provider.get() to cause a new one to be created
So the relevant parts of your doLogin method might look like this (untested):
final AlterableContext context = (AlterableContext) this.beanManager.getContext(SessionScoped.class);
assert context != null;
final Bean<?> bean = beanManager.resolve(beanManager.getBeans(SessionBean.class));
assert bean != null;
context.destroy(bean);
final SessionBean newSessionBean = this.sessionBeanProvider.get();
assert newSessionBean != null;
I think that should work.

Getting LazyInitializationException within in a #Transactional method

I’m getting this exception when I access this method from my controller:
{"status":"failure","exception":"LazyInitializationException","exceptionMessage":"failed
to lazily initialize a collection of role:
org.mainco.subco.lessonplan.domain.LessonPlan.classrooms, could not
initialize proxy - no Session","errorMessage":"failed to lazily
initialize a collection of role:
org.mainco.subco.lessonplan.domain.LessonPlan.classrooms, could not
initialize proxy - no Session"}
Controller:
#Autowired
private ThirdPartyService m_thirdPartySvc;
…
#RequestMapping(value = "/launch", method = RequestMethod.GET)
#Transactional
public String launchLti(final #RequestParam String assignmentId,
final Model model,
final HttpServletRequest request,
final HttpServletResponse response,
final Principal principal) throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException
{
final subcoAuthenticationUser auth = (subcoAuthenticationUser) ((Authentication) principal).getPrincipal();
String nextPage = null;
final User user = m_userSvc.findById(auth.getId());
// Provision the assignment in ThirdParty if not already done so
final Assignment assmt = m_lessonPlanDao.getAssignment(assignmentId);
if (!assmt.isSentToThirdParty())
{
m_thirdPartySvc.sendAssignment(assignmentId);
} // if
Is the #Transactional annotation unnecessary? Especially since I already have it on my #Service class…
#Service
#Transactional
public class ThirdPartyServiceImpl implements ThirdPartyService
{
#Override
public void sendAssignment(final String assignmentId)
{
final Assignment assignment = m_lessonPlanDao.getAssignment(assignmentId);
if (isThirdPartyAssignment(assignment))
{
final String ThirdPartyPromptId = assignment.getTocItem().getThirdPartyPromptId();
// Gather the teacher id
final LessonPlan lessonPlan = m_lessonPlanDao.getLessonPlan(assignment.getLessonPlan().getId());
final String teacherId = lessonPlan.getOwnerId();
// Gather the students who have been assigned this assignment
final List<Classroom> classes = lessonPlan.getClassrooms();
// Send one request for each class assignment
for (final Classroom classroom : classes)
{
The error occurs on the for (final Classroom classroom : classes) line. I have #Transactional everywhere, yet I’m getting this LazyInitializationException. Why? And how do I create a transaction so that I can run my method?
I’m using Spring 3.2.11.RELEASE, Hibernate 4.3.6.Final, and JPA 2.1 on JBoss 7.1.3.Final. If upgrading any of these would solve my problem, let me know.
The #Transactional boundary is being respected by application during runtime. You can find this out by calling: TransactionSynchronizationManager#isActualTransactionActive()
Add some code to print out the value of above method. If it's false, then maybe you need to make sure the component-scan is set up right.
Example: <context:component-scan base-package="com.application.dao" />
This would totally miss the classes in the com.application.service package.

Several beans implementating the same interface

The exact usage is like this:
#Slf4j
public class Client<E, Key> {
#Getter #NonNull private final UpdateListener<E, Key> updateListener;
#NonNull private final SubscriptionFactory subscriptionFactory;
#NonNull private final Map<Key, Instant> updatedRegistry = new ConcurrentHashMap<>();
public Client(UpdateListener<E, Key> updateListener,
SubscriptionFactory subscriptionFactory) {
this.updateListener = updateListener;
this.subscriptionFactory = subscriptionFactory;
this.subscriptionFactory.registerSnapshotClient(updateListener);
log.info("Created new snapshot client for entity key [{}], update type [{}] and component qualifier [{}]",
updateListener.getEntityKey(),
updateListener.getOptionalChangeType(),
updateListener.getComponentQualifier());
}
#RabbitListener(queues = {"#{#queueNameCreator.createUpdateQueueName(snapshotClient.getUpdateListener())}",
"#{#queueNameCreator.createSnapshotQueueName(snapshotClient.getUpdateListener())}"})
public void handleMessage(Message<E> rawUpdate, #Header("last_updated") Instant newUpdatedTime) {
...//more code
}
}
Each 'Client' instance has its own bean id to not clash with each other.
How can I call get the exact updateListener of this object using SpEl?
Update
After using programattical approach and registering method I get the following exception:
Apr 28, 2015 3:22:47 PM org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler handleError
WARNING: Execution of Rabbit message listener failed.
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener method 'public void com.everymatrix.om2020.messaging.model.SnapshotClient.handleMessage(org.springframework.messaging.Message<E>,java.time.Instant)' threw exception
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:126)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:93)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:756)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:679)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:82)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:167)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1241)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:660)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1005)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:989)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:82)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1103)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalStateException: No suitable resolver for argument [0] [type=org.springframework.messaging.Message]
Update
Done, you need to do the following to achieve the desired behaviour.
#Configuration
#EnableRabbit
public static class OmbeRabbitListenerConfigurer implements RabbitListenerConfigurer {
#Autowired ApplicationContext applicationContext;
#Autowired SnapshotClientQueueNamesCreator snapshotClientQueueNamesCreator;
#Autowired RabbitListenerContainerFactory rabbitListenerContainerFactory;
#Autowired MessageConverter messageConverter;
#Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
final Collection<SnapshotClient> snapshotClients = applicationContext.getBeansOfType(SnapshotClient.class).values();
System.out.println(snapshotClients);
snapshotClients.stream().forEach(bean -> {
final String snapshotQueueName = snapshotClientQueueNamesCreator.createSnapshotQueueName(bean.getUpdateListener());
final String updateQueueName = snapshotClientQueueNamesCreator.createUpdateQueueName(bean.getUpdateListener());
Method method = Stream.of(bean.getClass().getMethods()).filter(x -> x.getName().equals("handleMessage")).findAny().get();
MethodRabbitListenerEndpoint endpoint = new MethodRabbitListenerEndpoint();
final DefaultMessageHandlerMethodFactory messageHandlerMethodFactory = new DefaultMessageHandlerMethodFactory();
messageHandlerMethodFactory.afterPropertiesSet();
endpoint.setMessageHandlerMethodFactory(messageHandlerMethodFactory);
endpoint.setBean(bean);
endpoint.setMethod(method);
endpoint.setId(snapshotQueueName + ":" + updateQueueName + UUID.randomUUID());
endpoint.setQueueNames(snapshotQueueName, updateQueueName);
endpoint.setExclusive(false);
registrar.registerEndpoint(endpoint, rabbitListenerContainerFactory);
});
}
}
Your question is not clear - you seem to be mixing runtime and initialization time concepts.
For example, "#{#queueNameCreator.createUpdateQueueName(e.c.doSomething())}" is evaluated once during initialization - it's not clear from this expression what e is, or where it comes from.
But, you seem to be passing in an E in the payload of message: Message<E> rawUpdate. This message came from the queue and therefore can't influence the queue name.
Perhaps if you can explain what you are trying to do rather than how you have attempted to do it, I can update this "answer" with possible solutions.
EDIT:
If you mean you want to reference some field in the current (listener) bean in your SpEL then it can't be done directly.
EDIT2:
I can't think of any way to get a reference to the current bean in the SpEL expression - it has to be a constant; that's just the way annotations work in Java; they are tied to the class, not the instance.
I think to do what you want, you would need to revert to using programmatic endpoint registration. However, you'd need to wire in a MethodRabbitListenerEndpoint (rather than the SimpleRabbitListenerEndpoint) to get the benefits of the annotation you are looking for (#Header etc).
We don't really cover it in the documentation; it's a little advanced, but essentially, you need to inject the bean and Method (for the listener), and a DefaultMessageHandlerMethodFactory.

Resources