In Primefaces Datatable does not sort with sortBy on the column - primefaces

I decided to ask here so I will solve my problem. Before I write to you I can ensure you
that I googled a lot, but no answer found.
In my case IN PRIMEFACES, the sortBy, and filterBy in the p:column in p:dataTable, do not work.
Lets start with the sortBy.
()
In all the versions I tried as you see in the following pom.xml: 6.2, 7.0, 7.0.RC3, 8.0, 8.0.RC3,
it has this behavior:
It displays the column headerText, with the 2 up/down arrows. When I click to the arrows they
DO NOT change "up", after "down". And obviously NO SORTING is taking place.
()
Only in the versions as you see in the following pom.xml: 6.0, 6.1 it has this behavior:
It displays the column headerText, with the 2 up/down arrows. When I click to the arrows they
DO change "up", after "down". But again NO SORTING is taking place.
In some posts they said, that I have to apply filtering first, and on the filtered list to
make sorting. I tried with the filterBy... No filtering worked, and no sorting...
The magic thing is that the filter did not work in all the previously mentioned versions...
At the following I write all the files I used...
I use Spring MVC 5.2.1 (Spring beans), Hibernate 5.4.3/JPA, JSF 2.2.20, Primefaces.
I tried to reproduce the example of the "Primefaces Showcase / DataTable / Sorting".
-------------- pom.xml --------------
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.2.20</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.2.20</version>
</dependency>
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>8.0.RC3</version>
<!-- <version>8.0</version> -->
<!-- <version>7.0</version> -->
<!-- <version>7.0.RC3</version> -->
<!-- <version>6.0</version> -->
<!-- <version>6.1</version> -->
<!-- <version>6.2</version> -->
</dependency>
-------------- page_table.xml --------------
In here I have tried only sorting in the first column, and filtering nad sorting in
the second column.
<h:form>
<p:dataTable var="car" value="#{sortViewBackingBean.cars}">
<f:facet name="header">
Single Column Sort
</f:facet>
<p:column headerText="Id" sortBy="#{car.id}">
<h:outputText value="#{car.id}"/>
</p:column>
<p:column headerText="Year" sortBy="#{car.year}" filterBy="#{car.year}">
<h:outputText value="#{car.year}"/>
</p:column>
</p:dataTable>
</h:form>
-------------- Car model --------------
public class Car {
private String id;
private String brand;
private int year;
private String color;
private int price;
private boolean soldState;
// Constructors, Getters, Setters
}
-------------- CarService --------------
#Service(value = "carService")
public class CarService {
private final static String[] colors;
private final static String[] brands;
// Populate colors, brands
public List<Car> createCars(int size) {
List<Car> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
list.add(new Car(getRandomId(), getRandomBrand(), getRandomYear(),
getRandomColor(), getRandomPrice(), getRandomSoldState()));
}
return list;
}
// Other methods
}
-------------- CarService --------------
Here is the heart of the code...
As I wrote I use Spring beans (the first 2 annotations in the SortViewBackingBean).
But, I overrode the Spring way, and I used CDI the one time / JSF way the other time,
but it did not work as well (the commented annotations in the SortViewBackingBean).
I will show you now how I feed the DataTable with the list "cars".
Googling I found out that the sorting can work only if we feed the datatable with the same
list. If every time tye list is different, the sorting does not work...
That is why, as you see in the following code, in 2 versions...
No one of these versions work...
-------------- SortViewBackingBean version 1 --------------
#Component(value = "sortViewBackingBean")
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
/*
I overrode the Spring way, and I used CDI/JSF way, but it did not work as well
#Named("sortViewBackingBean")
#ManagedBean("sortViewBackingBean")
#ViewScoped
#SessionScoped
*/
public class SortViewBackingBean implements Serializable {
private final List<Car> cars;
private CarService carService;
public SortViewBackingBean(CarService carService) {
this.carService = carService;
// At creation of the bean, the cars is created once only!
cars = this.carService.createCars(10);
}
#PostConstruct
public void init() {
// Or this version, as is in the primefaces showcase
// cars = carService.createCars(10);
}
public List<Car> getCars() {
return cars;
}
public void setService(CarService carService) {
this.carService = carService;
}
}
-------------- SortViewBackingBean version 2 --------------
#Component(value = "sortViewBackingBean")
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SortViewBackingBean implements Serializable {
private List<Car> cars;
private CarService carService;
public SortViewBackingBean(CarService carService) {
this.carService = carService;
}
// Or this version I found googling, the cars is created once only!
public List<Car> getCars() {
if (cars == null) {
cars = this.carService.createCars(10);
}
return cars;
}
public void setService(CarService carService) {
this.carService = carService;
}
}
So, ok!
These features in Primefaces work, or it is just in me only!
Can someone help on this please?
Thanks a lot

Based on your code: you need to use a scope longer than request like viewscope or sessionscope (not both) to keep the filteredValue so that filtered list is still accessible after filtering.
e.g.
#Named("sortViewBackingBean")
#SessionScoped
if you want to sort more than one column, you need to enable Multiple sorting by setting sortMode to multiple. In this mode, clicking a sort column while metakey is on adds sort column to the order group.
e.g.
<p:dataTable var="car" value="#{carBean.cars}" sortMode="multiple">
//columns
</p:dataTable>

Related

Iterate over nested a list of objects in primefaces tabView and dataTable

I am trying to combine primefaces (v6.0) tabView and editable dataTable in jsf 2.2 framework. From reading many excellent threads from this forum, I was able to add/remove tab dynamically and implement editable table which allows add/remove a row dynamically.
However I am having a trouble to read a property "name" belongs to Hobby object. To be precise, instead of viewing a property belong to Hobby object, IDE displays properties of Person object.
I am hoping experienced JSF developers be able to find my mistake and advise me what I've done wrong and how to correct it.
FYI-The example I pasted here is water down version.
<p:tabView value="#{bean.people}" var="person">
<p:tab title="#{person.name}">
<h:panelGrid>
<p:dataTable value="#{person.hobbies}" var="hobby">
<p:column headerText="my hobby">
#{hobby.name}
</p:column>
</p:datatTable>
</h:panelGrid>
</p:tab>
Managed bean:
#ManagedBean(name="bean")
#ViewScoped
public class Bean {
private List<Person> people;
#PostConstruct
public void init() {
people = new ArrayList<>();
}
// getter/setter for people
.....
}
Person model:
public class Person {
private List<Hobby> hobbies;
private String name;
public Person() {
hobbies = new ArrayList<>();
}
// getter/setter for hobbies and name
....
}
Hobby Model:
public class Hobby {
private String name;
// getter/setter for name
....
}

Primefaces Paginate Lazy DataTable without count row

in my page i have :
<p:dataTable id="myTable" lazy="true" widgetVar="myTable" value=".." paginator="true" paginatorPosition="bottom" rows="10" ...>
<p:ajax event="page" oncomplete="myTable" />
...
</p:dataTable>
in my bean :
...
private LazyDataModel list;
...
public void search() {
list = new LazyDataModel<SomeDTO>() {
private static final long serialVersionUID = 1L;
List<SomeDTO> result = null;
#Override
public List<SomeDTO> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
List<SomeDTO> result = service.search(searchedName, first, pageSize);
// SOME CODE HERE FOR PAGINATION ?? BUT WHAT
return result;
}
};
// HOW TO DELETE THIS EXTRA REQUEST
list.setRowCount(service.search(searchedName));
}
my question is how to setRowCount from result (size+1). i dont want to get the count from DB, because i'm sure we can calculate the rowCount without requesting DB.
PrimeFaces did not support this out of the box. A fix has been checked in to trunk on Feb 11th 2016, tagged 6.0 (so it should at least be in the current 6.0RCx releases). I'm not sure if it is in the Elite release >=5.2.20 or >=5.3.7 (from Feb 12th)
One important reason for this not working is that the updated rowCount you might do in the load method serverside is not applied to the paginator client side. However, since it is transferred from the server to the client, you can update it in the oncomplete of each ajax call. In fact, that is a large part of the patch (the other part is reading the value from the ajax response).
Combined calling this in e.g. the oncomplete of a ajax page event will solve the issue:
function updatePaginator(xhr, status, args) {
var paginator = PF('DataTableWidgetVar').paginator;
paginator.cfg.rowCount=args.totalRecords;
paginator.cfg.pageCount = Math.ceil(value / paginator.cfg/rows)||1;
paginator.updateUI();
}
You can then in each call in the load method,
- Try to read pagesize+1 records
- Set the count to this if you can read pageSize+1 (but still return pageSize records)
- Set the count to the number of rows read if they are pageSize or less.
Primefaces pagination Lazy DataTable necessary two queries, a query to return the results of a page as limiting, and another query to know the total size of data.
Ok i got it.
first in your page you need this:
<p:remoteCommand name="updateMyTable" update="#{p:component('myTable')}" process="#this" />
<p:dataTable id="myTable" lazy="true" widgetVar="myTable" value=".." paginator="true" paginatorPosition="bottom" rows="10" ...>
<p:ajax event="page" oncomplete="updateMyTable()" />
...
</p:dataTable>
in your bean change the load method like this:
#Override
public List<SomeDTO> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
List<SomeDTO> result = service.search(searchedName, first, pageSize);
if (first <= pageSize) {
this.setRowCount(this.getRowCount() + result.size() + 1);
} else if (result != null && result.size() > 0 && (result.size() >= pageSize)) {
this.setRowCount(this.getRowCount() + result.size());
}
return result;
}

Cannot refresh Vaadin JPAContainer nested property

Revised on 2014-12-02 - persistence.xml and wildfly config for eclipselink/mysql
Revised on 2014-12-02 - better problem information, complete code as suggested and screenshots.
I have two MYSQL tables in a one-to-many relation. I display them in Vaadin using two tables (tbl_products, tbl_prices) bound to associated JPAContainers (productsContainer, pricesContainer). In tbl_prices, I display a field from the first table, using pricesContainer.addNestedContainerProperty().
The problem is, when I change the QTY field value in tbl_products, the change is immediately reflected to the database, however the nested property in tbl_prices is never aware of the change until a browser refresh. (pricesContainer.getItem(itemId).getProperty("product.qty") returns the old value). Clearly shown in the After quantity update screenshots at the end of the post.
tbl_prices.refreshRowCache() and pricesContainer.refresh() did not help, although a container refresh is supposed to refresh the values from DB)
I will appreciate any help on how to resolve this. Could not get an answer on vaadin.com forums. Here is how the entities are configured:
#Entity
#Table(name="products")
#NamedQuery(name="Product.findAll", query="SELECT p FROM Product p")
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(unique=true, nullable=false)
private int id;
#Column(nullable=false, length=255)
private String name;
#Column(nullable=false)
private int qty;
//bi-directional many-to-one association to Price
#OneToMany(mappedBy="product")
private List<Price> prices;
... constructor, getters and setters
}
#Entity
#Table(name="prices")
#NamedQuery(name="Price.findAll", query="SELECT p FROM Price p")
public class Price implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(unique=true, nullable=false)
private int id;
#Column(nullable=false, length=255)
private String description;
#Column(nullable=false, precision=10, scale=2)
private BigDecimal price;
//bi-directional many-to-one association to Product
#ManyToOne
#JoinColumn(name="prodid", nullable=false)
private Product product;
#Transient
private BigDecimal value;
public BigDecimal getValue() {
return this.price.multiply(new BigDecimal(product.getQty()));
}
... constructor, getters and setters
}
public class UI_Tables_Test extends CustomComponent {
public static final String PERSISTENCE_UNIT = "vaadin_sandbox";
public JPAContainer<Product> productsContainer;
public JPAContainer<Price> pricesContainer;
private FieldGroup formFieldGroup;
public UI_Tables_Test() {
buildMainLayout();
setCompositionRoot(mainLayout);
// Containers
productsContainer = JPAContainerFactory.make(Product.class, PERSISTENCE_UNIT);
pricesContainer = JPAContainerFactory.make(Price.class, PERSISTENCE_UNIT);
pricesContainer.addNestedContainerProperty("product.*");
// Tables
tbl_products.setContainerDataSource(productsContainer);
tbl_products.setSelectable(true);
tbl_products.setImmediate(true);
tbl_prices.setContainerDataSource(pricesContainer);
tbl_prices.setSelectable(true);
tbl_prices.setImmediate(true);
// Columns
tbl_products.setVisibleColumns(new Object[] {"id","name", "qty"});
tbl_prices.setVisibleColumns(new Object[] {"id", "product.name", "product.qty", "price", "value"});
// Form
formFieldGroup = new FieldGroup(new BeanItem<Product>(new Product()));
formFieldGroup.setBuffered(false);
formFieldGroup.bind(tf_name, "name");
formFieldGroup.bind(tf_qty, "qty");
// ValueChangeListener to set form data source
tbl_products.addValueChangeListener(new Property.ValueChangeListener() {
private static final long serialVersionUID = 7133249924369468095L;
#Override
public void valueChange(ValueChangeEvent event) {
Object selectedrow = event.getProperty().getValue();
if (selectedrow != null) {
formFieldGroup.setItemDataSource(tbl_products.getItem(selectedrow));
}
}
});
// Property ValueChangeListener to refresh Prices table
productsContainer.getItem(2).getItemProperty("qty").addValueChangeListener(new Property.ValueChangeListener() {
#Override
public void valueChange(ValueChangeEvent event) {
refreshTable2();
}
});
}
public void refreshTable2() {
// trying various refresh methodologies
pricesContainer.refresh();
pricesContainer.getEntityProvider().refreshEntity(pricesContainer.getItem(3).getEntity());
tbl_prices.refreshRowCache();
// trying removing and adding nested container property again
pricesContainer.removeContainerProperty("product.*");
pricesContainer.addNestedContainerProperty("product.*");
// trying resetting table2 container data source
tbl_prices.setContainerDataSource(pricesContainer);
pricesContainer.addNestedContainerProperty("product.*");
tbl_prices.setVisibleColumns(new Object[] {"id", "product.name", "product.qty", "price", "value"});
// popup message to display the values read from the containers
String msg = "Product container Qty= " +
String.valueOf(productsContainer.getItem(2).getEntity().getQty()) + "\n" +
"Price container Qty= " +
String.valueOf(pricesContainer.getItem(3).getEntity().getProduct().getQty()) + "\n" +
pricesContainer.getContainerProperty(3, "product.qty");
Notification.show("Test", msg, Notification.Type.ERROR_MESSAGE);
}
... build layout
}
Here is two screenshots (dropbox links since reputation points not enough to post images)
Before quantity update:
https://www.dropbox.com/s/ej459r0v80popsb/before_quantity_update.png?dl=0
After quantity update:
https://www.dropbox.com/s/ryl358pyymguonl/after_quantity_update.png?dl=0
And here is the persistence.xml and relevant wildfly configuration
persistence.xml
---------------
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="vaadin_sandbox" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<non-jta-data-source>java:/jdbc/vaadin_sandbox</non-jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<shared-cache-mode>NONE</shared-cache-mode>
<properties>
<property name="eclipselink.deploy-on-startup" value="True" />
<property name="eclipselink.target-server" value="JBoss"/>
<property name="eclipselink.logging.level" value="FINE" />
</properties>
</persistence-unit>
</persistence>
wildfly config:
--------------
eclipselink.jar in /usr/local/wildfly-8.1.0.Final/modules/system/layers/base/org/eclipse/persistence/main
corresponding module.xml
<module xmlns="urn:jboss:module:1.1" name="org.eclipse.persistence">
<resources>
<resource-root path="jipijapa-eclipselink-1.0.1.Final.jar"/>
<resource-root path="eclipselink.jar"/>
</resources>
<dependencies>
...
</dependencies>
</module>
workaround for issueid=414974
$ bin/jboss-cli.sh —connect
[standalone#localhost:9990 /] /system-property=eclipselink.archive.factory:add(value=org.jipijapa.eclipselink.JBossArchiveFactoryImpl)
mysql-connector-java-5.1.30-bin.jar in /usr/local/wildfly-8.1.0.Final/modules/system/layers/base/com/mysql/main
corresponding module.xml
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="com.mysql">
<resources>
<resource-root path="mysql-connector-java-5.1.30-bin.jar"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.transaction.api"/>
</dependencies>
</module>
$ bin/jboss-cli.sh --connect
[standalone#localhost:9990 /] /subsystem=datasources/jdbc-driver=mysql:add(driver-name=mysql,driver-module-name=com.mysql,driver-class-name=com.mysql.jdbc.Driver)
[standalone#localhost:9990 /] /subsystem=datasources/data-source=vaadin_sandbox:add(driver-name=mysql, user-name=secret, password=secret, connection-url=jdbc:mysql://localhost:3306/vaadin_sandbox?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8, min-pool-size=5, max-pool-size=15, jndi-name=java:/jdbc/innodron, enabled=true, validate-on-match=true, valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker, exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter)
Revision: documenting my findings after spending several hours on this issue
Common: Wildfly 8.1.0.Final, MySQL 5.6.20, Vaadin 7.3.5, EclipseLink 2.5.2, javax.persistence 2.1.0, mysql-connector-java 5.1.34
Case 1) persistence.xml transaction-type="RESOURCE_LOCAL", shared_cache_mode="NONE"
In this case clearing the EntityManagerFactory cache seems to be only solution to the problem.
public void refreshTable2() {
//Evict Entity Manager Cache for the specific item
em.getEntityManagerFactory().getCache().evict(Price.class, 3);
// if container is not refreshed, tbl_prices is not updated in the UI.
pricesContainer.refresh();
// popup message to display the values read from the containers
String msg = "Product container Qty= " +
String.valueOf(productsContainer.getItem(2).getEntity().getQty()) + "\n" +
"Price container Qty= " +
String.valueOf(pricesContainer.getItem(3).getEntity().getProduct().getQty()) + "\n" +
pricesContainer.getContainerProperty(3, "product.qty");
Notification.show("Test", msg, Notification.Type.ERROR_MESSAGE);
}
Case 2) persistence.xml transaction-type="RESOURCE_LOCAL", shared_cache_mode="ALL"
In this case no need to evict EntityManagerFactory cache, but pricesContainer needs a manual refresh for the nested property to refresh its value.
public void refreshTable2() {
// if container is not refreshed, tbl_prices is not updated in the UI.
pricesContainer.refresh();
}
Case 3) persistence.xml transaction-type="JTA", shared_cache_mode="ALL" or "NONE".
(Check this blog entry for how to make Vaadin work with JTA transactions)
For both these cases no need to evict EntityManagerFactory cache, but pricesContainer needs a manual refresh for the nested property to refresh its value. The upper side is shared_cache_mode can be set to "NONE", which seems to be required for Vaadin JPAContainer to play nice (according to several recommendations in Vaadin Forums)
public void refreshTable2() {
// if container is not refreshed, tbl_prices is not updated in the UI.
pricesContainer.refresh();
}
Any suggestions are welcome on how to make the nested property refresh without manual intervention. In a real project a manual refresh will require one Container.addItemSetChangeListener() for the source container and one Container.getItem(i).addValueChangeListener() for every item in the container - which would create a massive overhead.

CDI conversation and primefaces wizard component

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.

Struts 2 json annotation

Ive been wanting to create a struts 2 with json return type using the annotation configuration. Ive successfully created this using the xml-type configuration like this snippet:
<action name="FetchJSON" class="com.stikiflem.Json" method="getJSON">
<result type="json"/>
</action>
I have posted a working demo of using an xml-type config here
http://stikiflem.wordpress.com/2008/08/27/struts-2-json-sample/
But how do I convert this to annotation? Here is my sample class:
public class JsonAction extends ActionSupport{
private List sampleList;
public String execute() {
sampleList = new ArrayList();
sampleList.add("stikiflem sample 1");
sampleList.add("stikiflem sample 2");
sampleList.add("stikiflem sample 3");
sampleList.add("stikiflem sample 4");
System.out.println("----------------------------------------------");
System.out.println("----------------------------------------------");
System.out.println("-sample111List:" + sampleList.toString());
System.out.println("----------------------------------------------");
System.out.println("----------------------------------------------");
return SUCCESS;
}
#Action(value="FetchJSON", results = {
#Result(name="success", type="json")
})
public String getJSON(){
System.out.println("get jason ko");
return execute();
}
public List getSampleList() {
return sampleList;
}
public void setSampleList(List sampleList) {
this.sampleList = sampleList;
}
}
Tried calling it by "json.action", it triggers the execute() method of course but cannot return a json type. Calling it by "FetchJSON" doesnt do anything. This question sounds stupid but there are just a small amount of tutorials and example of a detailed annotation in the net. Ive read a Manning Struts 2 in action book but it just barely scratch the surface, just the typical hello world and sucess,input redirection.
Ive searched the net high and low and so far, i havent seen any. I know there are a lot of programmers searching for this too.Hope someone can enlighten me about this one. Ive been banging my head on this for days already. :(
A similar question was asked here:
Struts2 JSON Plugin With Annotations
I got your action working by annotating it as follows:
#ParentPackage("json-default")
#Result(name="success", type="json")
public class JsonAction extends ActionSupport {
Get the JAR Dependencies
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<version>2.3.20</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-json-plugin</artifactId>
<version>2.3.20</version>
</dependency>
Convention Plugin
The Convention Plugin is bundled with Struts since 2.1 and replaces the Codebehind Plugin and Zero Config plugins. It provides the following features :
Action location by package naming conventions
Result (JSP, FreeMarker, etc) location by naming conventions
Class name to URL naming convention
Package name to namespace convention
Action name overrides using annotations
Namespace overrides using annotations
XWork package overrides using annotations
Set Parent Package
Using annotation set the package as json-default to support the JSON.
#ParentPackage("json-default")
Set Result Type
#Result(name="success", type="json")
Define filter in web.xml
Define the struts 2 filter in web.xml and pass the action class by defining actionPackages.
Action Class
In this class data converted into JSON format.
#Result(name = "success", type = "json")
#ParentPackage("json-default")
public class StrutsJsonAnnotationAction extends ActionSupport {
private static final long serialVersionUID = 3516335522937177571L;
private String name = "Narendra Modi";
private String designation = "Prime Minister of India";
private String dob = "17 September 1950";
private String[] education = {"MA", "BA"};
private List<String> favBooks = new ArrayList<String>();
private Map<String, String> assumedOffice = new HashMap<String, String>();
public StrutsJsonAnnotationAction() {
favBooks.add("Ramayan");
favBooks.add("Geeta");
assumedOffice.put("President", "Pranab Mukherjee");
assumedOffice.put("Preceded by", "Manmohan Singh");
}
#org.apache.struts2.convention.annotation.Action("/india")
#Override
public String execute() {
return SUCCESS;
}
Source:
http://www.websparrow.org/struts/struts2-and-json-integration-using-annotation-example