Actually in my JSF application I have this code to compute a component clientId from its id:
public static String getComponentClientId(String id) {
try {
FacesContext fctx = FacesContext.getCurrentInstance();
UIComponent found = getComponentWithId(fctx.getViewRoot(), id);
if (found!=null)
return found.getClientId(fctx);
else
return null;
} catch (Exception e) {
return null;
}
}
public static UIComponent getComponentWithId(UIComponent parent, String id) {
for (Iterator<UIComponent> chs = parent.getFacetsAndChildren(); chs.hasNext();) {
UIComponent ch = chs.next();
if (ch.getId().equalsIgnoreCase(id))
return ch;
else {
UIComponent found = getComponentWithId(ch, id);
if (found!=null)
return found;
}
}
return null;
}
The method works, but it navigate through the view component tree, so it can be very inefficient, specially in presence of highly populated pages. There is a clever way or an API that I don't know to make the job faster/easier?
Not that I know of.
It is difficult to cache this information, because:
The relationship between an instance of UIComponent and its clientIds can be 1:N. This is required for components like UIData that manage the state of their children. The return value from getClientId can be context-sensitive.
The lifetime of a UIComponent instance probably won't exceed the request. Some implementations of StateManager can be configured to save the view state in the session, in which case the lifetime of the objects might be longer, but making your code rely on this makes it fragile and it would be easy to introduce memory leaks.
Views can be dynamic. Admittedly, this is an edge case, but it is possible to add, remove and move components programmatically.
I would prove that this is a problem before trying another approach.
You don't say what you need the clientIds for, but I'm guessing it is for some form of JavaScript support. Consider writing a custom component and emitting your markup via its renderer. This could be used with a for attribute, similar to the label control. Finding a neighbour in the same NamingContainer is relatively easy. You might use code like this in your renderer implementation:
// untested code!
String clientId = mycomponent.getClientId(context);
// get id of target control
String _for = mycomponent.getFor();
int n = clientId.lastIndexOf(NamingContainer.SEPARATOR_CHAR);
String targetClientId = clientId.substring(0, n)
+ NamingContainer.SEPARATOR_CHAR + _for;
ResponseWriter writer = context.getResponseWriter();
// write markup
Components usually need to share a naming container to make use of each other's clientId anyway, so this isn't a big constraint. If you can make the component a child, it is easier to find a parent.
Of course, this approach has problems too. It makes your UI tree even bigger, with a knock-on effect on lifecycle processing and state-management. Whether the trade-off is worth it would require testing.
EDIT:
I thought about this over the weekend. Since 50% of JSF queries seem to be about how to work with IDs, I wrote a post so I can just refer people to it - JSF: working with component IDs. In includes a mechanism for caching the IDs (sort of!) with some sample code.
Related
In my topbar I have a <o:graphicImage> to show picture from my user.
<o:graphicImage dataURI="true" height="32" width="32" styleClass="img-circle"
value="#{employeeProfileMenuPictureRequestController.getPicture_32_32(loginBean.currentEmployee)}"
lastModified="#{employeeProfileMenuPictureRequestController.lastUpdate}" />
My backend bean is the following:
#GraphicImageBean
public class EmployeeProfileMenuPictureRequestController implements Serializable {
private Date lastUpdate = new Date();
public byte[] getPicture_32_32(Employee employee) throws StorageAttachmentNotFoundException, IOException {
try {
String path = employeeProfilePictureService.findProfileImageByEmployee(employee, FileSizeType.SIZE_32_32.toString());
if (employee == null || path == null || path.isEmpty()) {
return Utils.toByteArray(Faces.getResourceAsStream("/resources/images/no-photo-icon.png"));
}
Path fileLocation = Paths.get(path);
byte[] data = Files.readAllBytes(fileLocation);
LOGGER.info("END getPicture_32_32");
return data;
catch (Exception e) {
LOGGER.error(ExceptionUtils.getFullStackTrace(e));
}
return Utils.toByteArray(Faces.getResourceAsStream("/resources/images/no-photo-icon.png"));
}
public Date getLastUpdate() {
return lastUpdate;
}
}
Unfortunatelly the getPicture_32_32(Employee) is called for every page request / page navigation. This means it´s also everytime a request against the database, which takes time.
I´ve tried already to add lastModified to the <o:graphicImage>, but the function is called also everytime for each page request.
Can anybody help me to solve this?
According to <o:graphicImage> documentation:
Data URI
[...]
This approach is however not recommended for "permanent" and/or "large" images as it doesn't offer the browser any opportunity to cache the images for reuse, ~10KB would typically be the max even less so if there are more such images on the same page.
So, it does not support caching at all. The technical reason is that it basically embeds whole contents of the image in the HTML output. It does not embed an URL to the image. The lastModified is basically ignored. I should probably better document that. At least, you should absolutely remove the dataURI attribute. It's only useful for e.g. preview of an uploaded image.
And,
Image streaming
[...]
In case the property is a method expression taking arguments, each of those arguments will be converted to a string HTTP request parameter and back to actual objects using the converters registered by class as available via Application.createConverter(Class). So, most of standard types like Long are already implicitly supported. In case you need to supply a custom object as argument for some reason, you need to explicitly register a converter for it yourself via #FacesConverter(forClass).
So, because your method take a Employee argument, you basically need to have a #FacesConverter(forClass=Employee.class) so that JSF can automatically convert it from and to String. How to create converters can be found here: Conversion Error setting value for 'null Converter' - Why do I need a Converter in JSF?
You should end up with something like this:
#FacesConverter(forClass=Employee.class)
public class EmployeeConverter implements Converter {
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
// Write code here which converts Employee to its unique String representation.
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
// Write code here which coverts the Employee unique String representation
// as created in above method back to the original Employee object.
}
}
An alternative is to adjust your getPicture_32_32() method to take employee ID as e.g. Long instead of employee. Then you don't need a custom converter. JSF has already a built-in converter for Long.
public byte[] getPicture_32_32(Long employeeId) {
// ...
}
<o:graphicImage
value="#{employeeProfileMenuPictureRequestController.getPicture_32_32(loginBean.currentEmployee.id)}" />
Coming back to caching, the documentation says this:
Caching
[...]
When unspecified, then the "default resource maximum age" as set in either the Mojarra specific context parameter com.sun.faces.defaultResourceMaxAge or MyFaces specific context parameter org.apache.myfaces.RESOURCE_MAX_TIME_EXPIRES will be used, else a default of 1 week will be assumed.
So, when you have no resource age settings, it's already by default cached for 1 week. The lastModified is thus optional and only useful when you actually track a timestamp in the same database or filesystem when the image is actually changed. You should then really use that instead for most optimal caching. A "random" date is absolutely not the correct way.
I want to avoid boiler plate code for creating a list of SelectItems to map my entities/dtos between view and model, so I used this snippet of a generic object converter:
#FacesConverter(value = "objectConverter")
public class ObjectConverter implements Converter {
private static Map<Object, String> entities = new WeakHashMap<Object, String>();
#Override
public String getAsString(FacesContext context, UIComponent component, Object entity) {
synchronized (entities) {
if (!entities.containsKey(entity)) {
String uuid = UUID.randomUUID().toString();
entities.put(entity, uuid);
return uuid;
} else {
return entities.get(entity);
}
}
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String uuid) {
for (Entry<Object, String> entry : entities.entrySet()) {
if (entry.getValue().equals(uuid)) {
return entry.getKey();
}
}
return null;
}
}
There are already many answers to similliar questions, but I want a vanilla solution (without *faces). The following points still leave me uncertain about the quality of my snippet:
If it was that easy, why isn't there a generic object converter build into JSF?
Why are so many people still using SelectItems? Isn't there more flexibility by using the generic approach? E.g. #{dto.label} can be quickly changed into #{dto.otherLabel}.
Given the scope is just to map between view and model, is there any major downside of the generic approach?
This approach is hacky and memory inefficient.
It's "okay" in a small application, but definitely not in a large application with tens or hundreds of thousands of potential entities around which could be referenced in a f:selectItems. Moreover, such a large application has generally a second level entity cache. The WeakHashMap becomes then useless and is only effective when an entity is physically removed from the underlying datastore (and thus also from second level entity cache).
It has certainly a "fun" factor, but I'd really not recommend using it in "heavy production".
If you don't want to use an existing solution from an utility library like OmniFaces SelectItemsConverter as you already found, which is basically completely stateless and doesn't use any DAO/Service call, then your best bet is to abstract all your entities with a common base interface/class and hook the converter on that instead. This only still requires a DAO/Service call. This has been fleshed out in detail in this Q&A: Implement converters for entities with Java Generics.
In my XPages application, I use a managed Java bean (scope = application) for translating strings:
public class Translator extends HashMap<String,String> implements Serializable {
private static final long serialVersionUID = 1L;
public String language = "en";
public Translator() { super(); this.init(null); }
public Translator(String language) { super(); this.init(language); }
public boolean init(String language) {
try {
FacesContext context = FacesContext.getCurrentInstance();
if (language!=null) this.language=language;
Properties data = new Properties();
// load translation strings from properties file in WEB-INF
data.load(new InputStreamReader(context.getExternalContext().getResourceAsStream("WEB-INF/translations_"+this.language+".properties"),"UTF-8"));
super.putAll(new HashMap<String,String>((Map) data));
// serializing the bean to a file on disk > this part of the code is just here to easily test how often the bean is initialized
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("C:\\dump\\Translator_"+this.language+"_"+new Date().getTime()+".ser"));
out.writeObject(this);
out.close();
return true;
}
catch (Exception e) { return false; }
}
public String getLanguage() { return this.language; }
public boolean setLanguage(String language) { return this.init(language); }
// special get function which is more tolerant than HashMap.get
public String get(Object key) {
String s = (String) key;
if (super.containsKey(s)) return super.get(s);
if (super.containsKey(s.toLowerCase())) return super.get(s.toLowerCase());
String s1 = s.substring(0,1);
if (s1.toLowerCase().equals(s1)) {
s1=super.get(s1.toUpperCase()+s.substring(1));
if (s1!=null) return s1.substring(0,1).toLowerCase()+s1.substring(1);
} else {
s1=super.get(s1.toLowerCase()+s.substring(1));
if (s1!=null) return s1.substring(0,1).toUpperCase()+s1.substring(1);
}
return s;
}
}
I use "extends HashMap" because in this way i only have to write "${myTranslatorBean['someText']}" (expression language) to get the translations into my XPage. The problem is that the bean is re-initialized at EVERY complete refresh or page reload. I tested this by serializing the bean to a unique file on the disk at the end of every initialisiation. In my other managed Java beans (which do not use "extends HashMap") this problem does not occur. Can anybody tell me what's wrong with my code? Thanks in advance.
EDIT: The entry for the managed Java bean in the faces-config.xml looks like this:
<managed-bean>
<managed-bean-name>myTranslatorBean</managed-bean-name>
<managed-bean-class>com.ic.Translator</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
I concur with David about the faces-config entry - if you could post it, that could shine some light on it.
In its absence, I'll take a stab at it: are you using a managed property to set the "language" value for the app. If you are, I suspect that there's a high chance that the runtime calls the setLanguage(...) method excessively. Since you call this.init(...) in that method, that would re-run that method repeatedly as well.
As a point of code style you are free to ignore, over time I (in part due to reading others' opinions) have moved away from extending collection classes directly for this kind of use. What I do instead in this situation is create an object that implements the DataObject interface and then uses a HashMap internally to store cached values. That's part of a larger industry preference called "Composition over inheritance": http://en.wikipedia.org/wiki/Composition_over_inheritance
Just to make sure nothings weird - I suggest you post your faces-config. I use beans all the time but haven't extended HashMap in any of them. You can add a map and still use EL.
Assuming you have a map getter like "getMyMap()" then EL might be:
AppBean.myMap["myKey"]
Truth be told I don't typically use that syntax but I BELIEVE that works. I gave it a quick test and it didn't work as I expected so I'm missing something. I tried something like:
imageData.size["Large"].url
I THINK it didn't work for me because my bean doesn't IMPLEMENT Map. I notice you're EXTENDING HashMap. You might want to try implementing it. I found an interesting post here: http://blog.defrog.nl/2012/04/settings-bean-parameterized-method-call.html
Usually I do still use SSJS to pass Parameters in. It's really not the end of the would using SSJS for that. And I use EL for everything else.
This is an example of passing an object to a custom control and return a TreeSet with EL.
value="#{compositeData.imageSet.allImages}">
Regarding the bigger issue of the bean re-initializing.. That is odd.. I don't do a ton with ApplicationScope. But I suggest you play with the constructor. I'm not sure what you get by calling super() there. I would suggest use a boolean to only run any init code of the boolean wasn't already set. Obviously you then set it in the init code. See what that does.
My question is :
I want to know when does a xLayout (or ViewGroup in general) add a child view from XML ? And by "when" I mean at what point of code, in what "pass" of the "traversal" of the UI toolkit ?
Which method of xLayout or ViewGroup should I override ?
I have done my homework : I have watched the "Writing Custom Views For Android" presented (by Adam Powell and Romain Guy) in the last Google I/O and I have read Adam Powell comments on this Google+ post.
Looking for the exact point in Android's source code where children are added.
We can look at what setContentView(R.layout.some_id) is doing under the hood.
setContentView(int) calls PhoneWindow#setContentView(int) - PhoneWindowLink is a concrete inplementation of Window:
#Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
The method LayoutInflater#inflate(layoutResID, mContentParent) eventually calls ViewGroup#addView(View, LayoutParams) on mContentParent. In between, child views
I want to know what happens exactly after I set content view to an XML file that contains a custom view. Afer the constructor there has to be a part in the code where the custom view "parse/read/inflate/convert" XML-declared child views to actual views ! (comment by JohnTube)
Ambiquity: From JohnTube's comment, it seems he is more interested in knowing how a custom view is inflated. To know this, we will have to look at the workings of LayoutInflaterLink.
So, the answer to Which method of xLayout or ViewGroup should I override ? is ViewGroup#addView(View, LayoutParams). Note that, at this point, the inflation of all regular/custom Views has already taken place.
Inflation of custom views:
The following method in LayoutInflater is where the addView(View, LayoutParams) is called on the parent/root:
Note: The call mLayoutInflater.inflate(layoutResID, mContentParent); in PhoneWindow#setContentView(int) chains to this. Here mContentParent is the DecorView: the view that's accessible through getWindow().getDecorView().
// Inflate a new view hierarchy from the specified XML node.
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
// Recursive method used to descend down the xml hierarchy and instantiate views,
// instantiate their children, and then call onFinishInflate().
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException
The call of interest in this method(and in the recursive rInflate(XmlPullParser, View, AttributeSet, boolean)) is:
temp = createViewFromTag(root, name, attrs);
Let's see what createViewFromTag(...) is doing:
View createViewFromTag(View parent, String name, AttributeSet attrs) {
....
....
if (view == null) {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
}
....
}
The period(.) decides whether onCreateView(...) or createView(...) is called.
Why this check? Because a View defined in android.view, android.widget or android.webkit package is accessed through its class name. For example:
android.widget: Button, TextView etc.
android.view: ViewStub. SurfaceView, TextureView etc.
android.webkit: WebView
When these views are encountered, onCreateView(parent, name, attrs) is called. This method actually chains to createView(...):
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
return createView(name, "android.view.", attrs);
}
This would deal with SurfaceView, TextureView and other views defined in android.view package. If you are interested in knowing how TextView, Button etc. are dealt with, look at PhoneLayoutInflaterLink - it extends LayoutInflater and overrides onCreateView(...) to check if android.widget and android.webkit are the intended package names. In fact, the call getLayoutInflater() gets you an instance of PhoneLayoutInflater. This is why if you were to subclass LayoutInflater, you couldn't even inflate the simplest of layouts - because LayoutInflater can only deal with views from android.view package.
Anyway, I digress. This extra bit happens for regular Views - which don't have a period(.) in their definition. Custom views do have a period in their names - com.my.package.CustomView. This is how the LayoutInflater distinguishes between the two.
So, in case of a regular view(say, Button), a prefix such as android.widget will be passed as the second argument - for custom views, this will be null. The prefix is then used along with the name to obtain the constructor for that particular view's class. Custom views don't need this because their name is already fully qualified. I guess this has been done for convenience. Else, you would have been defining your layouts in this way:
<android.widget.LinearLayout
...
... />
(Its legal though...)
Also, this is why views coming from a support library (eg. <android.support.v4.widget.DrawerLayout.../>) have to use fully qualified names.
By the way, if you did want to write your layouts as:
<MyCustomView ../>
all you have to do is to extend LayoutInflater and add your package name com.my.package. to the list of strings that are checked during inflation. Check PhoneLayoutInflater for help with this.
Let's see what happens in the final stage for both custom and regular views - createView(...):
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
// Try looking for the constructor in cache
Constructor<? extends View> constructor = sConstructorMap.get(name);
Class<? extends View> clazz = null;
try {
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
....
// Get constructor
constructor = clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name, constructor);
} else {
....
}
Object[] args = mConstructorArgs;
args[1] = attrs;
// Obtain an instance
final View view = constructor.newInstance(args);
....
// We finally have a view!
return view;
}
// A bunch of catch blocks:
- if the only constructor defined is `CustomView(Context)` - NoSuchMethodException
- if `com.my.package.CustomView` doesn't extend View - ClassCastException
- if `com.my.package.CustomView` is not found - ClassNotFoundException
// All these catch blocks throw the often seen `InflateException`.
}
... a View is born.
If you're talking about a ViewGroup defined in XML, it's children are added when the view is inflated. This can be when you inflate explicitly with a LayoutInflater or when you set the content view of an activity. (There are probably a few other times as well, particularly if you are using stub views.)
If you want to add the children yourself to a ViewGroup that is not inflated, you can do that in the view's constructor.
EDIT: If you want to see how the children are added when a view is inflated, this occurs in the call to LayoutInflater.inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot). The source for android.view.LayoutInflater is included in the Android SDK distributions; on-line versions can be found in many places (here at GrepCode, for instance). This method ends up being called when, for instance, you call setContentView(int) for an Activity or when you explicitly inflate a layout resource.
The children are actually added in the call to rInflate(parser, root, attrs, false); ("recursive inflate"), which might be called from a couple of different places in the inflate() method, depending on what the inflater found as the root tag. You can trace through the code logic yourself. An interesting point is that a child is not added to its parent until its own children have been recursively inflated and added to it.
The other interesting method, used by both inflate and rInflate, is createViewFromTag. This might rely on an installable LayoutInflater.Factory (or .Factory2 object) to create the view, or may end up calling createView. There you can see how the call to the view's two-argument constructor ((Context context, AttributeSet attrs)) is made.
I'm creating a SharePoint application, and am trying some new things to create what amounts to an API for Data Access to maintain consistency and conventions.
I haven't seen this before, and that makes me think it might be bad :)
I've overloaded the constructor for class Post to only take an SPListItem as a parameter. I then have an embedded Generic List of Post that takes an SPListItemCollection in the method signature.
I loop through the items in a more efficient for statement, and this means if I ever need to add or modify how the Post object is cast, I can do it in the Class definition for a single source.
class Post
{
public int ID { get; set; }
public string Title { get; set; }
public Post(SPListItem item)
{
ID = item.ID;
Title = (string)item["Title"];
}
public static List<Post> Posts(SPListItemCollection _items)
{
var returnlist = new List<Post>();
for (int i = 0; i < _items.Count; i++) {returnlist.Add(new Post(_items[i]));}
return returnlist;
}
}
This enables me to do the following:
static public List<Post> GetPostsByCommunity(string communityName)
{
var targetList = CoreLists.SystemAccount.Posts(); //CAML emitted for brevity
return Post.Posts(targetList.GetItems(query)); //Call the constructor
}
Is this a bad idea?
This approach might be suitable, but that FOR loop causes some concern. _items.Count will force the SPListItemCollection to retrieve ALL those items in the list from the database. With large lists, this could either a) cause a throttling exception, or b) use up a lot of resources. Why not use a FOREACH loop? With that, I think the SPListItems are retrieved and disposed one at a time.
If I were writing this I would have a 'Posts' class as well 'Post', and give it the constructor accepting the SPListItemCollection.
To be honest, though, the few times I've seen people try and wrap SharePoint SPListItems, it's always ended up seeming more effort than it's worth.
Also, if you're using SharePoint 2010, have you considered using SPMetal?