I'd like to use com.ibm.xsp.model.domino.DominoViewData() in my Java class to filter and sort domino view data, but I'm not sure how to go about it.
There aren't a lot of examples out there, and most I've found are using it on an xPage or with a Data Table.
In a JAVA class, I'd like to:
Create a new DominoViewData object.
Set the view name.
Set the column to sort on.
Optionally set a filter.
Finally, retrieve a ViewEntryCollection for further processing.
Can the DominoViewData class be used in that way?
Thanks for your help and any examples would be appreciated.
-- Jeff
As long as your are using them in an XPage application, this is possible. I am not sure what benefits you will have instead of accessing a view directly, but here is the code:
1.You need a helper class to access the tabular data model
/**
* Returns the tabular data model from a datasource
*
* #author Christian Guedemann, Sven Hasselbach
* #param dsCurrent
* datasource to get the tdm from
* #param context
* current FacesContext instance
* #return
* TabularDataModel
*/
public static TabularDataModel getTDM(DataSource dsCurrent, FacesContext context) {
try {
if (dsCurrent instanceof ModelDataSource) {
ModelDataSource mds = (ModelDataSource) dsCurrent;
AbstractDataSource ads = (AbstractDataSource) mds;
ads.load(context);
DataModel tdm = mds.getDataModel();
if (tdm instanceof TabularDataModel) {
TabularDataModel tds = (TabularDataModel) tdm;
return tds;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
2.You have to create your datasource and add them to a component, f.e. the view root
DominoViewData dvd = new DominoViewData();
dvd.setViewName( "YOUR VIEW NAME" );
dvd.setComponent( FacesContext.getCurrentInstance().getViewRoot() );
3.Now you can add the filter options or any additional options to your datasource, f.e. these:
dvd.setSortOrder( "ascending" );
dvd.setSortColumn( "NAME OF COLUMN" );
4.Then access the TDM of the datasource, get the first entry and you have a handle to the parent, a ViewNavigator
TabularDataModel tdm = getTDM( dvd, FacesContext.getCurrentInstance() );
tdm.setDataControl( new UIDataEx() );
Entry noiEntry = (Entry) tdm.getRowData();
ViewNavigator nav = null;
try {
nav = (ViewNavigator) noiEntry.getParent();
System.out.println( "NAV COUNT: " + nav.getCount() );
nav.recylce();
} catch (NotesException e) {
e.printStackTrace();
}
(OK, you now have a ViewNavigator instead of a ViewEntryCollection)
Related
I am currently doing on a project that requires me to move my data between tabs/fragments.. Let's say the user clicks on the listView item, they will move to another tab, instead of staying in the same tab. May I know how can I achieve that? Can someone help to solve my query? Thank you!
You can trasnfer data between tabs by set and get Arguments,
Here is Example
FragmentTwo fragmentTwo = new FragmentTwo();
Bundle bundle = new Bundle();
bundle.putString("key1", "data1");
bundle.putString("key2", "data2");
bundle.putString("key3", "data3");
fragmentTwo.setArguments(bundle);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.main_container, fragmentTwo);
fragmentTransaction.commit();
There are 3 ways to do this
1) Use Interfaces -use interface to pass data objects. Meshy solution
Read more
public interface onDataCHange{
public void updateData(String data);
}
2) Use Activity Class - Store model object in activity class and set and get using activity Instance. Quick and Dirty solution
Read more
//Get
Object dataModel = (ContainerActivity) getActivity()).getData();
//Set
((ContainerActivity) getActivity()).setData(dataModel );
3) Clean architecture - Center repository hold model objects. View update model via Singleton Center repository object. Single copy of data flow between throughout the App.
Read more
#Singleton
public class UserDataRepository implements UserRepository {
private final UserDataStoreFactory userDataStoreFactory;
private final UserEntityDataMapper userEntityDataMapper;
/**
* Constructs a {#link UserRepository}.
*
* #param dataStoreFactory A factory to construct different data source implementations.
* #param userEntityDataMapper {#link UserEntityDataMapper}.
*/
#Inject
UserDataRepository(UserDataStoreFactory dataStoreFactory,
UserEntityDataMapper userEntityDataMapper) {
this.userDataStoreFactory = dataStoreFactory;
this.userEntityDataMapper = userEntityDataMapper;
}
#Override public Observable<List<User>> users() {
//we always get all users from the cloud
final UserDataStore userDataStore = this.userDataStoreFactory.createCloudDataStore();
return userDataStore.userEntityList().map(this.userEntityDataMapper::transform);
}
#Override public Observable<User> user(int userId) {
final UserDataStore userDataStore = this.userDataStoreFactory.create(userId);
return userDataStore.userEntityDetails(userId).map(this.userEntityDataMapper::transform);
}
}
first, Extension Library is not yet available for some servers. I want to compare document count in all replicas of a db. I want to display the same in web. So I am looping through catalog.nsf entries. Here is the code. Problem is, it's not able to create session. Here goes the code:
package com.apse.replication;
import java.util.*;
import lotus.domino.*;
/**
* #author Arun Agnihotri
*
*/
#SuppressWarnings("unchecked")
public class ReplicationCountMismatches
{
Session s = null;
Database catDb = null;
View catView = null;
Document catDoc = null;
DocumentCollection catCol = null;
Vector v;
String key;
//hashmap object to hold the database replica id as key and document count as value
Hashtable<String, Integer> dbTable = new Hashtable<String, Integer>();
//array to hold server names
String serverNames[] = {"Server1","Serevr2","Server3"};
public DocumentCollection GetMismatchedReplicas(){
//we'll loop through catalog database of every server and update the hashmap
try{
for (int i=0;i<serverNames.length;i++){
s = NotesFactory.createSession(); //this is WHERE I RECEIVE ERROR
catDb = s.getDatabase(serverNames[i], "catalog.nsf");
catView = catDb.getView("Applications\\By Title");
catDoc = catView.getFirstDocument();
while(catDoc!=null){
v = s.evaluate("#Text(ReplicaID;\"*\")", catDoc);
key=v.firstElement().toString();
if (i==0){
//first catalog being scanned.. put all key-value pairs in HashTable
dbTable.put(key, catDoc.getItemValueInteger("DbNumDocuments"));
}
else{
if (!dbTable.containsKey(key)){
dbTable.put(key, catDoc.getItemValueInteger("DbNumDocuments"));
}
else{
if ((Integer) dbTable.get(key) != catDoc.getItemValueInteger("DbNumDocuments")){
catCol.addDocument(catDoc);
}
}
}
catDoc = catView.getNextDocument(catDoc);
}
}
}
catch(Exception e){
e.printStackTrace();
}
return catCol;
}
}
You should use the Jsf session when your Java code runs in XPages context.
import com.ibm.xsp.extlib.util.ExtLibUtil;
...
Session s = ExtLibUtil.getCurrentSession();
If the server your code will be executed on doesn't have the ExtLibUtil you can create your own JsfUtil library like shown here.
I am developing one custom portlet with mvc in eclipse IDE.
Following is my scenario:
I have one table name as restaurant and from addrestaurant.jsp page I am adding the data in restaurant table.
In my addrestaurant.jsp page I have following code of select control which takes multiple value to be selected:
<label>Select Ad Type<span class="f_req">*</span></label>
<select data-placeholder="- Select Ad Type -" class="chzn-select" multiple name="ad_type" value="<%=restaurantOBJ.getAdtypeId()%>">
<option value="1">Standby Ad</option>
<option value="2">Homepage Ad</option>
<option value="3">Slider Ad</option>
<option value="4">Event Based Ad</option>
</select>
Now this adtype selected from addrestaurant.jsp will be inserted in one rest_map_adtype table,
where I want to add these selected value with the reference of restaurant primary-key which is just created last.
So how can I get the primary key of restaurant which is last edited
For more explanation let me give some code snippet:
public void addRestaurant(ActionRequest request, ActionResponse response) {
log.info("Inside addRegistration");
List<String> errors = new ArrayList<String>();
restaurant rest = RestaurantActionUtil
.getRestaurantFromRequest(request);
boolean restValid = RestaurantValidator
.validateRestaurant(rest, errors);
if (restValid) {
try {
log.info(rest);
restaurant test = restaurantLocalServiceUtil
.addrestaurant(rest);
//Above Code will add the all data which is in my addrestaurant.jsp accept just selected control values
String[] adtype_ID=request.getParameterValues("ad_Type");
//here am taking the select tag's multiple value in one string array
So after this line I need to add all the values in rest_map_Adtype table with the reference of restaurant primary key which is just created with the addrestaurant(rest) method just a line above.
How can I make it possible?
or how can I get the primary key in return after inserting the row in table?
My ActionUtil.class follows
public restaurant addRestaurant(restaurant restParam) {
restaurant restVar;
try {
restVar = restaurantPersistence.create(counterLocalService
.increment(restaurant.class.toString()));
} catch (SystemException e) {
e.printStackTrace();
return restVar = null;
}
try {
resourceLocalService.addResources(restParam.getGroupId(),restParam.getGroupId(), restParam.getUserId(),
restaurant.class.getName(),restParam.getPrimaryKey(), false,true,true);
} catch (PortalException e) {
e.printStackTrace();
return restVar = null;
} catch (SystemException e) {
e.printStackTrace();
return restVar = null;
}
restVar.setName(restParam.getName());
restVar.setAdress(restParam.getAdress());
restVar.setCity(restParam.getCity());
restVar.setPin(restParam.getPin());
restVar.setState(restParam.getState());
restVar.setCountry(restParam.getCountry());
restVar.setContactno(restParam.getContactno());
restVar.setEmail(restParam.getEmail());
restVar.setWebsite(restParam.getWebsite());
restVar.setCuisinetype(restParam.getCuisinetype());
restVar.setPersonalmail(restParam.getPersonalmail());
restVar.setPersonalname(restParam.getPersonalname());
restVar.setPersonalPhone(restParam.getPersonalPhone());
restVar.setNoofemenuagent(restParam.getNoofemenuagent());
restVar.setLicensekey(restParam.getLicensekey());
restVar.setRestregId(restParam.getRestregId());
restVar.setNoofdiningtable(restParam.getNoofdiningtable());
restVar.setAvgnoofcustomermonthly(restParam.getAvgnoofcustomermonthly());
restVar.setAveragroupagevisit(restParam.getAveragroupagevisit());
restVar.setImpoflocation(restParam.getImpoflocation());
restVar.setAvgmonthlycheckamount(restParam.getAvgmonthlycheckamount());
restVar.setCostperthousandimpression(restParam.getCostperthousandimpression());
restVar.setAdtypeId(restParam.getAdtypeId());
//restVar.setNoofdiningtable(restParam.getNoofdiningtable());
//restVar.setAvgnoofcustomermonthly(restParam.getAvgnoofcustomermonthly());
restVar.setIsactive(restParam.getIsactive());
restVar.setCreateddate(restParam.getCreateddate());
restVar.setLastmodifiedby(restParam.getLastmodifiedby());
restVar.setModifieddate(restParam.getModifieddate());
restVar.setGroupId(restParam.getGroupId());
restVar.setUserId(restParam.getUserId());
restVar.setIsdeleted(restParam.getIsdeleted());
restVar.setRestregId(restParam.getRestregId());
//restVar.setOrganizationId(restParam.getOrganizationId());
try {
return restaurantPersistence.update(restVar, false);
} catch (SystemException e) {
e.printStackTrace();
return restVar = null;
}
}
following is my object restaurant which returns following value after add restaurant {restId=0, name=aaaa, isactive=false, userId=10158, groupId=10180, createddate=Fri Oct 26 12:40:53 GMT 2012, lastmodifiedby=10158, modifieddate=Fri Oct 26 12:40:53 GMT 2012, restregId=12333, adress=, city=AHMEDABAD, pin=, state=, country=, contactno=, email=, website=, cuisinetype=, noofemenuagent=0, personalname=, personalPhone=, personalmail=, adtypeId=0, isdeleted=false, licensekey=12333, noofdiningtable=0, averagroupagevisit=0, impoflocation=, avgnoofcustomermonthly=0, avgmonthlycheckamount=0, costperthousandimpression=0.0}
restaurantLocalServiceUtil Class
public static emenu.advertise.database.model.restaurant addrestaurant(
emenu.advertise.database.model.restaurant restaurant)
throws com.liferay.portal.kernel.exception.SystemException {
return getService().addrestaurant(restaurant);
}
/**
* Creates a new restaurant with the primary key. Does not add the restaurant to the database.
*
* #param restId the primary key for the new restaurant
* #return the new restaurant
*/
public static emenu.advertise.database.model.restaurant createrestaurant(
long restId) {
return getService().createrestaurant(restId);
}
This statement sets the primary key in your restVar i.e. restaurant object:
restVar = restaurantPersistence.create(counterLocalService
.increment(restaurant.class.toString()));
And when you do this:
return restaurantPersistence.update(restVar, false);
It actually returns the restaurant object which should contain the generated primary-key.
So after the following call:
restaurant test = restaurantLocalServiceUtil.addrestaurant(rest);
you can simply do:
long restId = test.getPrimaryKey(); // this would return the primary-key
Hope this helps.
Is anyone aware of a method to dynamically combine/minify all the h:outputStylesheet resources and then combine/minify all h:outputScript resources in the render phase? The comined/minified resource would probably need to be cached with a key based on the combined resource String or something to avoid excessive processing.
If this feature doesn't exist I'd like to work on it. Does anyone have ideas on the best way to implement something like this. A Servlet filter would work I suppose but the filter would have to do more work than necessary -- basically examining the whole rendered output and replacing matches. Implementing something in the render phase seems like it would work better as all of the static resources are available without having to parse the entire output.
Thanks for any suggestions!
Edit: To show that I'm not lazy and will really work on this with some guidance, here is a stub that captures Script Resources name/library and then removes them from the view. As you can see I have some questions about what to do next ... should I make http requests and get the resources to combine, then combine them and save them to the resource cache?
package com.davemaple.jsf.listener;
import java.util.ArrayList;
import java.util.List;
import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.event.PreRenderViewEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import org.apache.log4j.Logger;
/**
* A Listener that combines CSS/Javascript Resources
*
* #author David Maple<d#davemaple.com>
*
*/
public class ResourceComboListener implements PhaseListener, SystemEventListener {
private static final long serialVersionUID = -8430945481069344353L;
private static final Logger LOGGER = Logger.getLogger(ResourceComboListener.class);
#Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
/*
* (non-Javadoc)
* #see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
*/
public void afterPhase(PhaseEvent event) {
FacesContext.getCurrentInstance().getViewRoot().subscribeToViewEvent(PreRenderViewEvent.class, this);
}
/*
* (non-Javadoc)
* #see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
*/
public void beforePhase(PhaseEvent event) {
//nothing here
}
/*
* (non-Javadoc)
* #see javax.faces.event.SystemEventListener#isListenerForSource(java.lang.Object)
*/
public boolean isListenerForSource(Object source) {
return (source instanceof UIViewRoot);
}
/*
* (non-Javadoc)
* #see javax.faces.event.SystemEventListener#processEvent(javax.faces.event.SystemEvent)
*/
public void processEvent(SystemEvent event) throws AbortProcessingException {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot viewRoot = context.getViewRoot();
List<UIComponent> scriptsToRemove = new ArrayList<UIComponent>();
if (!context.isPostback()) {
for (UIComponent component : viewRoot.getComponentResources(context, "head")) {
if (component.getClass().equals(UIOutput.class)) {
UIOutput uiOutput = (UIOutput) component;
if (uiOutput.getRendererType().equals("javax.faces.resource.Script")) {
String library = uiOutput.getAttributes().get("library").toString();
String name = uiOutput.getAttributes().get("name").toString();
// make https requests to get the resources?
// combine then and save to resource cache?
// insert new UIOutput script?
scriptsToRemove.add(component);
}
}
}
for (UIComponent component : scriptsToRemove) {
viewRoot.getComponentResources(context, "head").remove(component);
}
}
}
}
This answer doesn't cover minifying and compression. Minifying of individual CSS/JS resources is better to be delegated to build scripts like YUI Compressor Ant task. Manually doing it on every request is too expensive. Compression (I assume you mean GZIP?) is better to be delegated to the servlet container you're using. Manually doing it is overcomplicated. On Tomcat for example it's a matter of adding a compression="on" attribute to the <Connector> element in /conf/server.xml.
The SystemEventListener is already a good first step (apart from some PhaseListener unnecessity). Next, you'd need to implement a custom ResourceHandler and Resource. That part is not exactly trivial. You'd need to reinvent pretty a lot if you want to be JSF implementation independent.
First, in your SystemEventListener, you'd like to create new UIOutput component representing the combined resource so that you can add it using UIViewRoot#addComponentResource(). You need to set its library attribute to something unique which is understood by your custom resource handler. You need to store the combined resources in an application wide variable along an unique name based on the combination of the resources (a MD5 hash maybe?) and then set this key as name attribute of the component. Storing as an application wide variable has a caching advantage for both the server and the client.
Something like this:
String combinedResourceName = CombinedResourceInfo.createAndPutInCacheIfAbsent(resourceNames);
UIOutput component = new UIOutput();
component.setRendererType(rendererType);
component.getAttributes().put(ATTRIBUTE_RESOURCE_LIBRARY, CombinedResourceHandler.RESOURCE_LIBRARY);
component.getAttributes().put(ATTRIBUTE_RESOURCE_NAME, combinedResourceName + extension);
context.getViewRoot().addComponentResource(context, component, TARGET_HEAD);
Then, in your custom ResourceHandler implementation, you'd need to implement the createResource() method accordingly to create a custom Resource implementation whenever the library matches the desired value:
#Override
public Resource createResource(String resourceName, String libraryName) {
if (RESOURCE_LIBRARY.equals(libraryName)) {
return new CombinedResource(resourceName);
} else {
return super.createResource(resourceName, libraryName);
}
}
The constructor of the custom Resource implementation should grab the combined resource info based on the name:
public CombinedResource(String name) {
setResourceName(name);
setLibraryName(CombinedResourceHandler.RESOURCE_LIBRARY);
setContentType(FacesContext.getCurrentInstance().getExternalContext().getMimeType(name));
this.info = CombinedResourceInfo.getFromCache(name.split("\\.", 2)[0]);
}
This custom Resource implementation must provide a proper getRequestPath() method returning an URI which will then be included in the rendered <script> or <link> element:
#Override
public String getRequestPath() {
FacesContext context = FacesContext.getCurrentInstance();
String path = ResourceHandler.RESOURCE_IDENTIFIER + "/" + getResourceName();
String mapping = getFacesMapping();
path = isPrefixMapping(mapping) ? (mapping + path) : (path + mapping);
return context.getExternalContext().getRequestContextPath()
+ path + "?ln=" + CombinedResourceHandler.RESOURCE_LIBRARY;
}
Now, the HTML rendering part should be fine. It'll look something like this:
<link type="text/css" rel="stylesheet" href="/playground/javax.faces.resource/dd08b105bf94e3a2b6dbbdd3ac7fc3f5.css.xhtml?ln=combined.resource" />
<script type="text/javascript" src="/playground/javax.faces.resource/2886165007ccd8fb65771b75d865f720.js.xhtml?ln=combined.resource"></script>
Next, you have to intercept on combined resource requests made by the browser. That's the hardest part. First, in your custom ResourceHandler implementation, you need to implement the handleResourceRequest() method accordingly:
#Override
public void handleResourceRequest(FacesContext context) throws IOException {
if (RESOURCE_LIBRARY.equals(context.getExternalContext().getRequestParameterMap().get("ln"))) {
streamResource(context, new CombinedResource(getCombinedResourceName(context)));
} else {
super.handleResourceRequest(context);
}
}
Then you have to do the whole lot of work of implementing the other methods of the custom Resource implementation accordingly such as getResponseHeaders() which should return proper caching headers, getInputStream() which should return the InputStreams of the combined resources in a single InputStream and userAgentNeedsUpdate() which should respond properly on caching related requests.
#Override
public Map<String, String> getResponseHeaders() {
Map<String, String> responseHeaders = new HashMap<String, String>(3);
SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_RFC1123_DATE, Locale.US);
sdf.setTimeZone(TIMEZONE_GMT);
responseHeaders.put(HEADER_LAST_MODIFIED, sdf.format(new Date(info.getLastModified())));
responseHeaders.put(HEADER_EXPIRES, sdf.format(new Date(System.currentTimeMillis() + info.getMaxAge())));
responseHeaders.put(HEADER_ETAG, String.format(FORMAT_ETAG, info.getContentLength(), info.getLastModified()));
return responseHeaders;
}
#Override
public InputStream getInputStream() throws IOException {
return new CombinedResourceInputStream(info.getResources());
}
#Override
public boolean userAgentNeedsUpdate(FacesContext context) {
String ifModifiedSince = context.getExternalContext().getRequestHeaderMap().get(HEADER_IF_MODIFIED_SINCE);
if (ifModifiedSince != null) {
SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_RFC1123_DATE, Locale.US);
try {
info.reload();
return info.getLastModified() > sdf.parse(ifModifiedSince).getTime();
} catch (ParseException ignore) {
return true;
}
}
return true;
}
I've here a complete working proof of concept, but it's too much of code to post as a SO answer. The above was just a partial to help you in the right direction. I assume that the missing method/variable/constant declarations are self-explaining enough to write your own, otherwise let me know.
Update: as per the comments, here's how you can collect resources in CombinedResourceInfo:
private synchronized void loadResources(boolean forceReload) {
if (!forceReload && resources != null) {
return;
}
FacesContext context = FacesContext.getCurrentInstance();
ResourceHandler handler = context.getApplication().getResourceHandler();
resources = new LinkedHashSet<Resource>();
contentLength = 0;
lastModified = 0;
for (Entry<String, Set<String>> entry : resourceNames.entrySet()) {
String libraryName = entry.getKey();
for (String resourceName : entry.getValue()) {
Resource resource = handler.createResource(resourceName, libraryName);
resources.add(resource);
try {
URLConnection connection = resource.getURL().openConnection();
contentLength += connection.getContentLength();
long lastModified = connection.getLastModified();
if (lastModified > this.lastModified) {
this.lastModified = lastModified;
}
} catch (IOException ignore) {
// Can't and shouldn't handle it here anyway.
}
}
}
}
(the above method is called by reload() method and by getters depending on one of the properties which are to be set)
And here's how the CombinedResourceInputStream look like:
final class CombinedResourceInputStream extends InputStream {
private List<InputStream> streams;
private Iterator<InputStream> streamIterator;
private InputStream currentStream;
public CombinedResourceInputStream(Set<Resource> resources) throws IOException {
streams = new ArrayList<InputStream>();
for (Resource resource : resources) {
streams.add(resource.getInputStream());
}
streamIterator = streams.iterator();
streamIterator.hasNext(); // We assume it to be always true; CombinedResourceInfo won't be created anyway if it's empty.
currentStream = streamIterator.next();
}
#Override
public int read() throws IOException {
int read = -1;
while ((read = currentStream.read()) == -1) {
if (streamIterator.hasNext()) {
currentStream = streamIterator.next();
} else {
break;
}
}
return read;
}
#Override
public void close() throws IOException {
IOException caught = null;
for (InputStream stream : streams) {
try {
stream.close();
} catch (IOException e) {
if (caught == null) {
caught = e; // Don't throw it yet. We have to continue closing all other streams.
}
}
}
if (caught != null) {
throw caught;
}
}
}
Update 2: a concrete and reuseable solution is available in OmniFaces. See also CombinedResourceHandler showcase page and API documentation for more detail.
You may want to evaluate JAWR before implementing your own solution. I've used it in couple of projects and it was a big success. It used in JSF 1.2 projects but I think it will be easy to extend it to work with JSF 2.0. Just give it a try.
Omnifaces provided CombinedResourceHandler is an excellent utility, but I also love to share about this excellent maven plugin:- resources-optimizer-maven-plugin that can be used to minify/compress js/css files &/or aggregate them into fewer resources during the build time & not dynamically during runtime which makes it a more performant solution, I believe.
Also have a look at this excellent library as well:- webutilities
I have an other solution for JSF 2. Might also rok with JSF 1, but i do not know JSF 1 so i can not say. The Idea works mainly with components from h:head and works also for stylesheets. The result
is always one JavaScript (or Stylesheet) file for a page! It is hard for me to describe but i try.
I overload the standard JSF ScriptRenderer (or StylesheetRenderer) and configure the renderer
for the h:outputScript component in the faces-config.xml.
The new Renderer will now not write anymore the script-Tag but it will collect all resources
in a list. So first resource to be rendered will be first item in the list, the next follows
and so on. After last h:outputScript component ist rendered, you have to render 1 script-Tag
for the JavaScript file on this page. I make this by overloading the h:head renderer.
Now comes the idea:
I register an filter! The filter will look for this 1 script-Tag request. When this request comes,
i will get the list of resources for this page. Now i can fill the response from the list of
resources. The order will be correct, because the JSF rendering put the resources in correct order
into the list. After response is filled, the list should be cleared. Also you can do more
optimizations because you have the code in the filter....
I have code that works superb. My code also can handle browser caching and dynamic script rendering.
If anybody is interested i can share the code.
I have 3 test web applications, using the same model and controllers, the difference is in JSF session managed beans.
The applications A and C use JSF DataModel to retrieve items :
A JPA Query result set returns a java LIST which is then wrapped in a ListDataModel. The value of this latter is being the items displayed by PrimeFaces dataTable.
The application B uses Java LIST to retrieve items:
A JPA Query result set returns a java List which is being the value of items displayed by PrimeFaces 2.2.1 dataTable.
Sorting and filtering in application B are fully functional and fast, while in application A and C, they are deadly not.
I just want to mention that Filtering in Sorting of other Libraries like Richfaces, OpenFaces, works out of the box using this same code.
The problem also remains in PrimeFaces 3.0.0. Is this a bug?
In App B :
Code:
private List<Customer> items = null;
// remainder of code here
public List<Customer> getCustomerItems() {
if (customerItems == null) {
getPagingInfo();
customerItems = jpaController.findCustomerEntities(pagingInfo.getBatchSize(), pagingInfo.getFirstItem());
}
return customerItems;
}
In App A:
Code:
private DataModel items = null;
public PaginationHelper getPagination() {
if (pagination == null) {
pagination = new PaginationHelper(999999) {
#Override
public int getItemsCount() {
return getJpaController().getChimioCount();
}
#Override // The list of Customers is wrapped in a JSF ListDataModel
public DataModel createPageDataModel() {
return new ListDataModel(getJpaController().findCustomerEntities(getPageSize(), getPageFirstItem()));
}
};
}
return pagination;
}
/**
* this goes for the value attribute in a datatable to list all the Customer items
*/
public DataModel getItems() {
if (items == null) {
items = getPagination().createPageDataModel();
}
return items;
}
In App C:
Code:
private DataModel<Project> items;
// remainder of code here
/**
*The ListDataModel is initialized here
*/
public void init() {
try {
setProjectList(doInTransaction(new PersistenceAction<List<Project>>() {
public List<Project> execute(EntityManager em) {
Query query = em.createNamedQuery("project.getAll");
return (List<Project>) query.getResultList();
}
}));
} catch (ManagerException ex) {
Logger.getLogger(ProjectManager.class.getName()).log(Level.SEVERE, null, ex);
}
projectItems = new LinkedList<SelectItem>();
projectItems.add(new SelectItem(new Project(), "-- Select one project --"));
if (getProjectList() != null) {
projects = new ListDataModel<Project>(getProjectList());
for (Project p : getProjectList()) {
projectItems.add(new SelectItem(p, p.getName()));
}
}
}
Thank you in advance for your help.
This might be a PrimeFaces bug. I have seen some discussion about issues with DataTable sorting when a data model is used. Here is a link to one of the PrimeFaces defects in their tracker.