CDI conversation and primefaces wizard component - primefaces

I just realized that my wizard component forgets the steps that lay in the past as I'm using a #RequestScoped wizard backing bean. Using #SessionScoped will work but is ugly.
Thus I tried to get it working using #ConversationScoped but had to realize some strange effect. (maybe out of J2EE experience)
Given this kind of wizard backing bean:
#Named
#RequestScoped
public class EvaluationWizard implements Serializable {
...
#Inject
private Conversation conversation;
#Inject
private Song selectedSong;
...
public void setSelectedSong(final Song song) {
selectedSong = song;
}
public Song getSelectedSong() {
return selectedSong;
}
public void onDialogOpen(final ActionEvent actionEvent) {
conversation.begin();
}
public void onDialogClose(final CloseEvent closeEvent) {
conversation.end();
}
...
}
My Song object looks like this:
#Named
#ConversationScoped
public class Song extends SelectItem implements Serializable {
private String title;
public void setTitle(final String title) {
this.title = title;
}
#Override
public String toString() {
return title;
}
}
The wizard contains several steps in order to set things up. The selectedSong property is an item of a list and represents the currently selected song.
This selection is saved in the "EvaluationWizard" backing bean and my debugging confirms that this is the case - but it's only the case for one wizard step.
Any help on that would be very appreciative.
Greetings, Marcel.

The Primefaces wizard component will not work with RequestScoped beans you are correct. You must either use #SessionScoped or #ViewScoped.
I personally like using ViewScoped as the bean will be created when you navigate to the page and will die when you leave the page. This gives you the benefit of a persisted bean without cluttering up the session.

Yes #RequestScoped won't work. Until today we also used #SessionScoped. Today I learned that it's better to use #ViewAccessScoped because you get window isolation compared to #SessionScoped. In our App we got a lot of bugs caused by #SessionScoped. I just replaced it with #ViewAccessScoped and I solved 17 different tickets in 10 minutes with it.

Related

LazyInitializationException when returning JSON in REST Webservice in Quarkus

I'm trying to build a simple application with Quarkus. Currently, I have two entity classes, which are related one-to-many:
#Entity
public class Person extends PanacheEntity {
public String name;
public LocalDate birthdate;
#OneToMany(mappedBy = "person")
public List<Address> addresses;
public static Person findByNameFirst(String name) {
return find("name", name).firstResult();
}
}
#Entity
public class Address extends PanacheEntity {
public String street;
...etc...
#ManyToOne
public Person person;
}
These are used by a simple REST webservice, which should store a Person to the database, select it again an return it:
#GET
#Path("storePerson")
#Produces(MediaType.APPLICATION_JSON)
#Transactional
public Person storePerson(
#QueryParam("name")String name,
#QueryParam("birthdate")String birthdate)
{
LocalDate birth = LocalDate.parse(birthdate, DateTimeFormatter.BASIC_ISO_DATE);
Person person = new Person(name, birth);
person.persistAndFlush();
Person p2 = Person.findByNameFirst(name);
return p2;
}
When calling the webservice the first time, the result is a JSON object with the stored data, which is as expected. When called again, an internal server error is thrown:
org.hibernate.LazyInitializationException: Unable to perform requested lazy initialization [Person.addresses] - no session and settings disallow loading outside the Session
As I understand, the error is thrown because the transaction only lasts until the storePerson method ends, but the conversion to JSON is happening outside of the method.
How can I prevent this error? I have read about the hibernate parameter "enable_lazy_load_no_trans" but it seems it is not supported in Quakus' application.properties.
The idea is to use a mapper framework such as MapStruct.
We don't recommend to directly expose your entities for 2 reasons:
the issue you have,
API management in the long run: you might have to change your model and not your API or the opposite.
There is an example here: https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-quarkus .
The Quarkus version used is a bit old but AFAICS it should still work with latest Quarkus.
You can make the error go away by using Hibernate.initialize(person.addresses), then the collection gets initialized before the transaction ends.

inject model data into spring webflow in cas

I am upgrading a CAS 4 to a CAS 6. I have done several Spring Boot 2 apps, so I know what I am doing there. I can even do some webflow, but only from scratch.
The documentation clearly states not to mess with the base webflow xml, and to "inject" your own services.
How does one "inject" a service? I really just need to add a message of the day to the login page.
Does anyone have an example of something this simple?
Find below my approach, tested on a cas-maven-overlay installation with cas version at 5.3.x. Some things maybe different on cas 6 branch but I assume the main idea remains.
First, we should create an Action class that will be injected in the login flow and will add the desired message in the flow scope in order to be available at the template(view).
public class DailyMessageAction extends AbstractAction{
#Override
protected Event doExecute(RequestContext context) throws Exception {
context.getFlowScope().asMap().put("dailyMessage", "YOUR_AWESOME_MESSAGE");
return success();
}
}
Then create a WebflowConfigurer class and inject our newly created DailyMessageAction in the actions list(see doInitialize method).
public class DailyMessageWebflowConfigurer extends AbstractCasWebflowConfigurer{
final Action dailyMessageAction;
public DailyMessageWebflowConfigurer(FlowBuilderServices flowBuilderServices,
FlowDefinitionRegistry flowDefinitionRegistry,
ApplicationContext applicationContext,
CasConfigurationProperties casProperties,Action dailyMessageAction){
super(flowBuilderServices, flowDefinitionRegistry, applicationContext, casProperties);
this.dailyMessageAction = dailyMessageAction;
}
#Override
protected void doInitialize() {
final Flow flow = super.getLoginFlow();
flow.getStartActionList().add(dailyMessageAction);
}
}
After that we should inject DailyMessageWebflowConfigurer in cas runtime. This is achieved by creating a configuration class and inject our configurer.
#Configuration
public class CustomWebflowConfiguration {
#Autowired
private CasConfigurationProperties casProperties;
#Autowired
#Qualifier("loginFlowRegistry")
private FlowDefinitionRegistry loginFlowDefinitionRegistry;
#Autowired
private ApplicationContext applicationContext;
#Autowired
private FlowBuilderServices flowBuilderServices;
#RefreshScope
#ConditionalOnMissingBean(name = "dailyMessageAction")
#Bean
public Action dailyMessageAction(){
return new DailyMessageAction();
}
#ConditionalOnMissingBean(name = "dailyMessageWebflowConfigurer")
#Bean
#RefreshScope
public CasWebflowConfigurer dailyMessageWebflowConfigurer(){
final DailyMessageWebflowConfigurer w = new DailyMessageWebflowConfigurer(flowBuilderServices,
loginFlowDefinitionRegistry,
applicationContext,
casProperties,
dailyMessageAction());
w.initialize();
return w;
}
}
Include our CustomWebflowConfigurationclass in META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=your_package.CustomWebflowConfiguration
The final step is to present the added message in the view. Achieved by adding this line
<div th:utext="${dailyMessage}"></div>
in the templates/casLoginView.html file.
... add a message of the day to the login page...
Modifying the spring webflow directly is not recommended in CAS. read this for more info
So if I were you instead of tinkering with spring webflow, I would try to do something like the following:
Note:
Bare in mind this might not be the recommended way to do so, but I think this will work, and much less work than overriding spring webflow
As you said you are quite familiar with Spring boot, so I won't bored you with detail implementation, I can follow up if you / other reader are confused
If your message of the day can be hard coded, just skip 1-3 and go straight with 4.
Ok here we go:
Override the CasSupportActionsConfiguration, only adding the initialFlowSetupAction bean
Adding a custom class (let named it MyInitialFlowSetupAction) and implement the InitialFlowSetupAction
In MyInitialFlowSetupAction, add something like this:
#Override
public Event doExecute(final RequestContext context) {
Event returnEvent = super.doExecute(context);
configureMyAwesomeMessageOfTheDay(context)
return returnEvent;
}
private void configureMyAwesomeMessageOfTheDay(final RequestContext context) {
String messageOfTheDay = "Spring is the best season!";//Your logic here
context.getFlowScope().put("MESSAGE_OF_THE_DAY", messageOfTheDay);
}
4 . CAS 6 is using WAR overlay, so you can overlay the html file, including this one
https://github.com/apereo/cas/blob/v6.0.3/webapp/resources/templates/casLoginView.html
overlay that file, and add your MESSAGE_OF_THE_DAY to it
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout}">
...
<body class="login">
<main role="main" class="container mt-3 mb-3">
Message of the day is: ${MESSAGE_OF_THE_DAY}
...
</main>
</body>
</html>
See if this helps you

make #jsonignore use a setter over a isMethod?

This is my class
public class HouseJPAImpl implements House {
public RoomJPAImpl room;
public RoomJPAImpl getRoom(){
return this.room;
}
public void setRoom(RoomJPAImpl room){
this.room = room;
}
#Override
public boolean isRoom(){
return false;
}
}
My code gets confused with getRoom and isRoom.
Caused by: java.lang.IllegalArgumentException: Conflicting getter definitions for property "room": com.shared.model.restimpl.jpa.HouseJPAImpl#getRoom(0 params) vs com.shared.model.restimpl.jpa.HouseJPAImpl#isRoom(0 params)
I tried putting the #jsonignore on the isRoom method but then i dont get the room property in JSON.
Is there a way to use the getRoom over isRoom?
First of all, this is something that Jackson 2.3 will handle gracefully (see https://github.com/FasterXML/jackson-databind/issues/238).
But until it gets released, there are 2 main ways to handle this:
Add #JsonIgnore on isRoom(), but keep #JsonProperty on getRoom()
Change visibility settings to filter out all isXxx() methods: can either set global settings (ObjectMapper has something like setVisibility), or use annotation #JsonAutoDetect on classes
If this is an isolated case, you are probably better off by just using first one.

Jaxb json missing brackets for one element array

I'm using JAXB/Jersey (1.3) to convert java to json in a REST API.
I read a lot about this problem, I tryed this solution, it work a half:
#XmlRootElement
public class ArrayWrapper
{
public List<String> list = new LinkedList<String>();
}
and my ContextResolver:
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
private Class[] types = {ArrayWrapper.class,Wrapper.class};
public JAXBContextResolver() throws Exception {
MappedBuilder builder = JSONConfiguration.mapped();
builder.arrays("list");
builder.rootUnwrapping(true);
this.context = new JSONJAXBContext(builder.build(), types);
}
ArrayWrapper aw=new ArrayWrapper();
aw.list.add("test");
I get {"list":["test"]} so it works but when I wrapp ArrayWrapper in an other class it don't work:
#XmlRootElement
public class Wrapper
{
public ArrayWrapper aw;
public Wrapper()
{
aw=new ArrayWrapper();
aw.list.add("test");
}
}
new Wrapper();
I get {"aw":{"list":"test"}}
Anyone know how to fix it?
I am not quite sure if you got it working so I am contributing my bit.
I also stumbled upon this issue recently. I found a post in stackoverflow that helped me, but even more helpful was this article (introducing Jackson might help).
I hope this helps you, too. For me it was a matter of 5 minutes to fix the issue.

Struts2 JSON Plugin With Annotations

I have a Struts2 Action Class configured via annotations. All of the "normal" methods that are annotated with #Action work fine.
However, I need to add a method into the action that returns JSON.
Here is a trimmed down version of my class (dao autowired with Spring):
#Namespace("featureClass")
// define success and input actions for class here
public class FeatureClassAction extends ActionSupport {
FeatureClassDao featureClassDao;
#Autowired
public setFeatureClassDao(FeatureClassDeao featureClassDao) {
this.featureClassDao = featureClassDao;
}
List<FeatureClass> featureClasses;
// snip normal actions
#Action("/featureClassesJSON")
#JSON
public String getFeatureClassesJSON() throws Exception {
featureClasses = featureClassDao.getAll();
return SUCCESS;
}
}
Can anyone assist? If I have to go the struts.xml route, that means moving all of my other actions (which work fine) into it.
I figured I would share the answer, since anyone else with the same problem would likely also face the silence.
I created two actions: FeatureClassAction and FeatureClassJsonAction. FeatureClassAction was annotated as such:
#ParentPackage("struts-default")
#Namespace("/featureClass")
public class FeatureClassAction extends ActionSupport {
FeatureClassJsonAction is annotated like this:
#ParentPackage("json-default")
#Namespace("/featureClass")
public class FeatureClassJsonAction extends ActionSupport {
The method in the JSON Action was annotated like this:
#Action(value="featureClassesJson", results = {
#Result(name="success", type="json")
})
public String getFeatureClassesJSON() throws Exception {
Hope it helps someone.