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.
Related
The Hazelcast documentation on their Portable serialization mechanism says that you are not forced to use a no-arg constructor.
https://hazelcast.org/mastering-hazelcast/#serialization-start
But their example code shows fields being set directly.
#Override
public void readPortable(PortableReader reader) throws IOException {
System.out.println("Deserialize");
this.name = reader.readUTF("name");
}
and the factory used to create instances of the class being deserialized is using a no-arg constructor.
#Override
public Portable create(int classId) {
switch (classId) {
case PERSON_CLASS_ID:
return new Person();
}
return null;
}
Am I right in assuming that you are not forced to use a no-arg constructor but you can't use it to set field values when deserializing? How would you handle enforcing invariants such as never-null fields and how would you deserialize a subclass without making the fields in the superclass protected?
While deserializing you can't maintain invariants since the object is being reconstructed; so some fields are just not set. There is no point resisting this.
And you are not forced to have a no-arg constructor. As long as the factory can create an instance, HZ doesn't care how you create it.
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.
Rather than using Bean model objects, my data model is built on Key-Value pairs in a HashMap container.
Does anyone have an example of the GXT's Grid ValueProvider and PropertyAccess that will work with a underlying Map?
It doesn't have one built in, but it is easy to build your own. Check out this blog post for a similar way of thinking, especially the ValueProvider section: http://www.sencha.com/blog/building-gxt-charts
The purpose of a ValueProvider is to be a simple reflection-like mechanism to read and write values in some object. The purpose of PropertyAccess<T> then is to autogenerate some of these value/modelkey/label provider instances based on getters and setters as are found on Java Beans, a very common use case. It doesn't have much more complexity than that, it is just a way to simply ask the compiler to do some very easy boilerplate code for you.
As that blog post shows, you can very easily build a ValueProvider just by implementing the interface. Here's a quick example of how you could make one that reads a Map<String, Object>. When you create each instance, you tell it which key are you working off of, and the type of data it should find when it reads out that value:
public class MapValueProvider<T> implements
ValueProvider<Map<String, Object>, T> {
private final String key;
public MapValueProvider(String key) {
this.key = key;
}
public T getValue(Map<String, Object> object) {
return (T) object.get(key);
}
public void setValue(Map<String, Object> object, T value) {
object.put(key, value);
}
public String getPath() {
return key;
}
}
You then build one of these for each key you want to read out, and can pass it along to ColumnConfig instances or whatever else might be expecting them.
The main point though is that ValueProvider is just an interface, and can be implemented any way you like.
Is it possible to use AutoMapper with Immutable types?
For example my Domain type is immutable and I want to map my view type to this.
I believe it is not but just want this confirmed.
Also as it is best practice to have your domain types immutable, what is the best practice when mapping your view types to domain types?
I typically do the mapping from view types to domain types by hand, as I'll typically be working through a more complex interface, using methods and so on. If you use AutoMapper to go from view to domain, you're now locked in to an anemic domain model, whether you've intentionally decided to or not.
Suppose that you really did want an immutable property on your Domain type, say Id. Your domain type might look something like this:
public class DomainType
{
public DomainType(int id)
{
Id = id;
}
public int Id { get; }
// other mutable properties
// ...
}
Then you can use ConstructUsing using a public constructor of your choice, such as:
CreateMap<ViewType, DomainType>()
.ConstructUsing(vt => new DomainType(vt.Id));
Then map all the mutable properties in the normal way
AutoMapper relies on property setters to do its work, so if you have read-only properties, AutoMapper won't be of much use.
You could override the mapping behaviour and, for example, configure it to invoke a specific constructor, but that basically defeats the purpose of AutoMapper because then you are doing the mapping manually, and you've only succeeded in adding a clumsy extra step in the process.
It doesn't make a lot of sense to me that your domain model is immutable. How do you update it? Is the entire application read-only? And if so, why would you ever need to map to your domain model as opposed to from? An immutable domain model sounds... pretty useless.
P.S. I'm assuming that you mean this AutoMapper and not the auto-mapping feature in Fluent NHibernate or even some other totally different thing. If that's wrong then you should be more specific and add tags for your platform/language.
We have immutable objects using the builder pattern. Mapping them takes a little more boilerplate code, but it is possible
// ViewModel
public class CarModel : IVehicleModel
{
private CarModel (Builder builder)
{
LicensePlate = builder.LicensePlate;
}
public string LicensePlate { get; }
//
public Builder
{
public string LicensePlate { get; set; }
}
}
// Model
public class CarViewModel : IVehicleViewModel
{
private CarViewModel (Builder builder)
{
LicensePlate = builder.LicensePlate ;
}
public ILicensePlate LicensePlate { get; }
//
public Builder
{
public ILicensePlate LicensePlate { get; set; }
}
}
Our AutoMapper Profiles have three mappings registered:
CreateMap<IVehicleModel, CarViewModel.Builder>();
CreateMap<CarViewModel.Builder, IVehicleViewModel>().ConvertUsing(x => x.Build());
CreateMap<IVehicleModel, IVehicleViewModel>().ConvertUsing<VehicleModelTypeConverter>();
The VehicleModelTypeConverter then defines a two stage conversion:
public IVehicleViewModel Convert(IVehicleModel source, IVehicleViewModel destination,
ResolutionContext context)
{
var builder = context.Mapper.Map<CarViewModel.Builder>(source);
var model = context.Mapper.Map<IVehicleViewModel>(builder);
return model;
}
(An implementation of ITypeListConverter<string, ILicensePlate> carries out that mapping).
Usage in our system is as normal:
var result = _mapper<IVehicleViewModel>(_carModel);
This is using AutoMapper v7.0.1
You can use Automapper with classes or records that have properties init only setters. This is new in C# 9.0.
Automapper can set the properties at object creation because the properties have init only setters, but after Automapper has mapped them, they are locked in (immutable).
https://www.tsunamisolutions.com/blog/c-90-records-and-dtos-a-match-made-in-redmond
Writing a simple JSF application I've some across the following Problem:
My entities.controller.EntityNameManager class contains a method getEntityNameSelectList() which I can use to populate a ComboBox with. This works and shows all Entities, since the Method to retrieve the Entities does not have a where clause.
This Method was automatically created.
Now I want to have a second similar Method, that filters the options based on a variable in the sessionscope. To do this I copied the original Method, renamed it to getEntityNameSelectListByUser(User theUser) and changed the Method that queries the database to one that does indeed filter by UserId.
However, when trying to load the page in the browser, I get an error stating that the controller class does not have a "EntityNameSelectListByUser" property. I assume that since my new method expects a parameter it can't be found. Is there a way I can make it aware of the Parameter or the Sessionscope userid?
Support for parameters in EL is slated for the next maintenance release of JSR 245 (announcement here; implementation here).
Assuming you don't want to wait for JEE6, you have several ways to overcome this limitation. These approached are defined in terms of POJO managed beans, so adapt them to your EJBs as appropriate.
1.
Do the session lookup and function call in a backing bean:
public String getFoo() {
FacesContext context = FacesContext
.getCurrentInstance();
ExternalContext ext = context.getExternalContext();
String bar = (String) ext.getSessionMap().get("bar");
return getFoo(bar);
}
Example binding:
#{paramBean.foo}
2.
Use an EL function (defined in a TLD, mapped to a public static method):
public static String getFoo(ParamBean bean, String bar) {
return bean.getFoo(bar);
}
Example binding:
#{baz:getFoo(paramBean, bar)}
3.
Subvert the Map class to call the function (a bit of a hack and limited to one parameter):
public Map<String, String> getFooMap() {
return new HashMap<String, String>() {
#Override
public String get(Object key) {
return getFoo((String) key);
}
};
}
Example binding:
#{paramBean.fooMap[bar]}