How to override the #AdminPresentation for existing attributes [Broadleaf Commerce] - java

I am trying to override the #AdminPresentation of the following attribute defined in ProductImpl:
#Column(name = "DISPLAY_TEMPLATE")
#AdminPresentation(friendlyName = "ProductImpl_Product_Display_Template",
group = GroupName.Advanced)
protected String displayTemplate;
Currently, it is displayed as a text field by default as there is no fieldType attribute provided. But I want to display a dropdown select menu with predefined values such as Product and Plan. Here is what I've tried so far:
I've created a class DisplayTemplateType that implements BroadleafEnumerationType and defined PLAN and PRODUCT enums. Here is the code of that class:
public class DisplayTemplateType implements Serializable, BroadleafEnumerationType {
private static final long serialVersionUID = 7761108654549553693L;
private static final Map<String, DisplayTemplateType> TYPES = new LinkedHashMap<String, DisplayTemplateType>();
public static final DisplayTemplateType PLAN = new DisplayTemplateType("PLAN", "PLAN");
public static final DisplayTemplateType PRODUCT = new DisplayTemplateType("PRODUCT", "PRODUCT");
public static DisplayTemplateType getInstance(final String type) {
return TYPES.get(type);
}
private String type;
private String friendlyType;
public DisplayTemplateType() {
//do nothing
}
public DisplayTemplateType(final String type, final String friendlyType) {
this.friendlyType = friendlyType;
setType(type);
}
#Override
public String getType() {
return type;
}
#Override
public String getFriendlyType() {
return friendlyType;
}
private void setType(final String type) {
this.type = type;
if (!TYPES.containsKey(type)) {
TYPES.put(type, this);
} else {
throw new RuntimeException("Cannot add the type: (" + type + "). It already exists as a type via " + getInstance(type).getClass().getName());
}
}
// equals() and hashCode() implementation is removed for readability
}
Then in applicationContext-admin.xml file, I have added the following override properties:
<mo:override id="blMetadataOverrides">
<mo:overrideItem ceilingEntity="org.broadleafcommerce.core.catalog.domain.Product">
<mo:field name="displayTemplate">
<mo:property name="explicitFieldType" value="BROADLEAF_ENUMERATION"/>
<mo:property name="broadleafEnumeration" value="com.community.core.domain.DisplayTemplateType"/>
</mo:field>
</mo:overrideItem>
</mo:override>
But it didn't change anything. Am I missing something here?

Finally, after trying many things, I came up with a workaround. Instead of going with the XML based approach, I had to extend the ProductImpl class to override #AdminPresentation of its attributes. But for extending I needed to define an #Entity and as a result, I needed to create a useless table to bind to that entity. I know this is not the perfect approach but I couldn't find any better solution for this. Here is my code, so that someone might get help from it in the future:
#Entity
#Immutable
#AdminPresentationMergeOverrides({
#AdminPresentationMergeOverride(name = "displayTemplate", mergeEntries = {
#AdminPresentationMergeEntry(propertyType = PropertyType.AdminPresentation.FIELDTYPE, overrideValue = "BROADLEAF_ENUMERATION"),
#AdminPresentationMergeEntry(propertyType = PropertyType.AdminPresentation.BROADLEAFENUMERATION, overrideValue = "com.community.core.domain.DisplayTemplateType"),
#AdminPresentationMergeEntry(propertyType = PropertyType.AdminPresentation.REQUIREDOVERRIDE, overrideValue = "REQUIRED"),
#AdminPresentationMergeEntry(propertyType = PropertyType.AdminPresentation.DEFAULTVALUE, overrideValue = "PLAN")
})
})
public class CustomProduct extends ProductImpl {
private static final long serialVersionUID = -5745207984235258075L;
}
This is how it is displayed now:

Related

Using #JsonCreator to create two instances of same class in one JSON DTO

I would like to deserialize JSON of this structure:
{
"employee_pricing_type":"COMPUTE_BY_OWN_RATE",
"employee_rate":10,
"customer_pricing_type":"COMPUTE_BY_OWN_RATE",
"customer_rate":200
}
I have such POJO to create price setting from a HTTP request:
public class ObjectPricingSetting {
#JsonProperty("pricing_type") // describes output
private final ObjectPricingType pricingType;
#JsonProperty("own_rate") // describes output
private final BigDecimal ownRate;
public ObjectPricingSetting(final ObjectPricingType pricingType, final BigDecimal ownRate) {
AssertUtils.notNull(pricingType, "pricingType");
this.pricingType = pricingType;
if (ownRate != null) {
AssertUtils.isGtZero(ownRate, "ownRate");
this.ownRate = ownRate;
} else {
this.ownRate = null;
}
}
public ObjectPricingType getPricingType() {
return pricingType;
}
public BigDecimal getOwnRate() {
return ownRate;
}
}
this is DTO:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ObjectPricingCommand extends BaseDto<ObjectId> {
#JsonProperty(value = "employee_pricing_setting")
private ObjectPricingSetting employeePricingSetting;
#JsonProperty(value = "customer_pricing_setting")
private ObjectPricingSetting customerPricingSetting;
}
I would like to create these two instances of ObjectPricingSetting with #JsonCreator.
Q: How should I anotate #JsonProperty parameter in ObjectPricingSetting constructor to recognize what JSON value should use to create these two instances?
You can use #JsonUnwrapped with a prefix in your parent class:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ObjectPricingCommand extends BaseDto<ObjectId> {
#JsonUnwrapped(prefix = "employee_")
private ObjectPricingSetting employeePricingSetting;
#JsonUnwrapped(prefix = "customer_")
private ObjectPricingSetting customerPricingSetting;
}
Then you can use the normal #JsonCreator/#JsonProperty in your nested DTO, without the prefix:
public class ObjectPricingSetting {
#JsonCreator
public ObjectPricingSetting(
#JsonProperty("pricing_type") final ObjectPricingType pricingType,
#JsonProperty("rate") final BigDecimal ownRate) {
...

#RestController autoserialize POJO's

I have a Spring mvc application, with a #RestController like such:
#RestController
#RequestMapping("levels")
public class LevelController {
private final GetLevelOneCount getLevelOneCount;
private final GetLevelTwoCount getLevelTwoCount;
private final GetLevelThreeCount getLevelThreeCount;
#Inject
public LevelController(GetLevelOneCount getLevelOneCount,
GetLevelTwoCount getLevelTwoCount,
GetLevelThreeCount getLevelThreeCount) {
this.getLevelOneCount = getLevelOneCount;
this.getLevelTwoCount = getLevelTwoCount;
this.getLevelThreeCount = getLevelThreeCount;
}
#GetMapping("/level1/{id}")
public LevelModel levelOne(#PathVariable String id) throws SQLException {
LevelModel levelOneModel = new LevelModel();
levelOneModel.setLevelQuery(getLevelOneCount.execute(id));
levelOneModel.setLevelDirQuery(getLevelOneCount.executeDir(id));
levelOneModel.setLevelDateQuery(getLevelOneCount.executeDate(id));
return levelOneModel;
}
my LevelModel is a POJO with private variables, now i wonder, if this can get serialized to propper JSON with private variables?
package com.pwc.tag.service.levels;
public class LevelModel {
private Long LevelQuery;
private Long LevelDirQuery;
private Long LevelDateQuery;
public Long getLevelQuery() {
return LevelQuery;
}
public void setLevelQuery(Long levelQuery) {
LevelQuery = levelQuery;
}
public Long getLevelDirQuery() {
return LevelDirQuery;
}
public void setLevelDirQuery(Long levelDirQuery) {
LevelDirQuery = levelDirQuery;
}
public Long getLevelDateQuery() {
return LevelDateQuery;
}
public void setLevelDateQuery(Long levelDateQuery) {
LevelDateQuery = levelDateQuery;
}
}
Yes, your object will be serialized to a proper JSON structure including the private field, because of the getters and setters.
If these fields should not be present in the output object, you can add the #JsonIgnore annotation to exclude them from the JSON structure.
P.S. the common approach is to start names of java properties with a lower case letter.

How to get specific class and attribute using java 8

I've defined the following set of data
Response response = new Response();
List<ObjectTest> objList = new ArrayList<ObjectTest>();
objList.add(new ObjectTest(new Attributes(new FirstName("ab","1"),new LastName("hernandez","2"))));
objList.add(new ObjectTest(new Attributes(new FirstName("jose","1"),new LastName("perez","2"))));
objList.add(new ObjectTest(new Attributes(new FirstName("paco","2"),new LastName("jackson","2"))));
objList.add(new ObjectTest(new Attributes(new FirstName("pedro","1"),new LastName("herrera","2"))));
objList.add(new ObjectTest(new Attributes(new FirstName("juan","2"),new LastName("flores","2"))));
response.setObjectList(objList);
So based on what the user selects I need to be able to get the specific class and the attribute, for example:
if the user selects [Attributes - FirstName - value] the output would be :
ab
jose
paco
pedro
juan
if the user selects [Attributes - LastName- status] the output would be:
2
2
2
2
2
The problem here is that I dont know how to get the specific class in runtime. Also the main object could have any number of classes inside of it like MainClass.ClassA.ClasstB.ClassX.classAttributeValue. The only thing that I know is that the last value is going to be the one that I have to take in that case I have to print classAttributeValue . Any ideas how to solve this using java 8 ?
Assuming your class structure looks something like this:
public static abstract class Attribute {
public final String value;
public final String status;
public Attribute(String value, String status) {
this.value = value;
this.status = status;
}
}
public static class FirstName extends Attribute {
public FirstName(String value, String status) {
super(value, status);
}
}
public static class LastName extends Attribute {
public LastName(String value, String status) {
super(value, status);
}
}
public static class Attributes {
public final FirstName firstName;
public final LastName lastName;
public Attributes(FirstName firstName, LastName lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
public static class ObjectTest {
public final Attributes attributes;
public ObjectTest(Attributes attributes) {
this.attributes = attributes;
}
}
You can define java.util.function.Function accessors for each stage:
Function<ObjectTest, Attributes> attributes = t -> t.attributes;
Function<Attributes, FirstName> firstName = t -> t.firstName;
Function<Attributes, LastName> lastName = t -> t.lastName;
Function<Attribute, String> value = t -> t.value;
Function<Attribute, String> status = t -> t.status;
And combine them like so:
Function<ObjectTest, String> attributeFirstNameValue =
attributes.andThen(firstName).andThen(value);
Function<ObjectTest, String> attributeLastNameStatus =
attributes.andThen(lastName).andThen(status);
Then apply the combined accessor to the list:
objList.stream().map(attributeFirstNameValue).forEach(System.out::println);
objList.stream().map(attributeLastNameStatus).forEach(System.out::println);
Is it critical to use this class structure?
In your example using a associative container is more suitable.
For example you can create class with structure like this:
Firstly you shoud something for itterate by Tree:
class DynamicObjectNode {
private HashMap<String, DynamicObjectNode> childs = new HashMap<>();
public HashMap<String, DynamicObjectNode> getChilds() {
return childs;
}
}
All values should be in leafs:
class DynamicObjectNodeValue<T> extends DynamicObjectNode {
public DynamicObjectNodeValue(T value) {
this.value = value;
}
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
#Override
public HashMap<String, DynamicObjectNode> getChilds() {
return null; //Tree leafs should not has childs
}
}
If you need to work with this as objects. You can use wrapped class like this:
class FirstNameAttribute extends DynamicObjectNode{
private static final String NameValueProperty = "NameValue";
private static final String StatusProperty = "Status";
private DynamicObjectNodeValue<String> nameValue = new DynamicObjectNodeValue<String>("Default name");
private DynamicObjectNodeValue<Integer> status = new DynamicObjectNodeValue<Integer>(1);
public FirstNameAttribute() {
getChilds().put(NameValueProperty, nameValue);
getChilds().put(StatusProperty, status);
}
public String getName() {
return nameValue.getValue();
}
public Integer getStatus() {
return status.getValue();
}
public void setName(String val) {
nameValue.setValue(val);
}
public void setStatus(Integer val) {
status.setValue(val);
}
}
So, with this code you can iterate it as a Tree and get values Dynamic.
And you can use this as objects to call some methods.
Thank you for your responses, what I finally did was to use JsonNode and based on the attribute I wanted to get I was iterating the same object and assign the result to se same object for example:
Json Response:
Object.Person1.firstName.value
I created an array of that and split it by "." then I created a for and I used this
jsonNode = jsonNode.get(inputArray[x]);
at the end the last element of the array is the one that I need so I added some logic to get it.

JAXB - TreeGrid solve conflict XmlElement

I have this classes structure to serialize TreeGrid (www.treegrid.com) object:
DataGrid.java
#XmlRootElement(name = "Grid")
public class DataGrid implements Serializable {
private static final long serialVersionUID = 337286974296229101L;
#XmlElement(name = "Body")
public DataGridData data = new DataGridData();
#XmlElement(name = "IO")
public XmlAttributeHolder io = new XmlAttributeHolder();
public DataGrid() {
}
}
and
DataGridData.java
public class DataGridData {
#XmlElement(name="B")
public DataGridCurrentPage currentPage = new DataGridCurrentPage();
#XmlElement(name="B")
public List<XmlAttributeHolder> pageList = new ArrayList<XmlAttributeHolder>();
}
These classes will be processed to return an XML structure as follow:
<Grid>
<Body>
<B />
</Body>
</Grid>
but the information encapsuled in B can be different (so exist two properties in DataGridData class mapped by the same XmlElement).
If I run my project under Java 7 that's all OK but with Java 8 is raise an exception about conflict two properties can't use the same XmlElement map.
A possible solution is: Encapsule two properties in two different classes as follow:
DataGridData.java
public class DataGridData {
private DataGridDataCP dataGridDataCP;
private DataGridDataPL dataGridDataPL;
public DataGridData() {
this.dataGridDataCP = new DataGridDataCP();
this.dataGridDataPL = new DataGridDataPL();
}
public DataGridDataCP getDataGridDataCP() {
return dataGridDataCP;
}
public void setDataGridDataCP(DataGridDataCP dataGridDataCP) {
this.dataGridDataCP = dataGridDataCP;
}
public DataGridDataPL getDataGridDataPL() {
return dataGridDataPL;
}
public void setDataGridDataPL(DataGridDataPL dataGridDataPL) {
this.dataGridDataPL = dataGridDataPL;
}
}
DataGridDataCP.java
public class DataGridDataCP {
private DataGridCurrentPage currentPage;
public DataGridDataCP() {
this.currentPage = new DataGridCurrentPage();
}
#XmlElement(name="B")
public DataGridCurrentPage getCurrentPage() {
return currentPage;
}
public void setCurrentPage(DataGridCurrentPage currentPage) {
this.currentPage = currentPage;
}
}
DataGridDataPL.java
public class DataGridDataPL {
private List<XmlAttributeHolder> pageList;
public DataGridDataPL() {
this.pageList = new ArrayList<XmlAttributeHolder>();
}
#XmlElement(name="B")
public List<XmlAttributeHolder> getPageList() {
return pageList;
}
public void setPageList(List<XmlAttributeHolder> pageList) {
this.pageList = pageList;
}
}
But in this way when DataGridData class has been serialized, add a tag <dataGridDataCP> (or <dataGridDataPL>) but I don't want to show this intermediate tag.
I've tried with XmlAccessorType annotation to exclude object DataGridDataCP / DataGridDataPL but this annotation exclude the complete object (with its encapsuled properties and no only the property in DataGridData class)
You can try something, but you will be able only to serialize properly. Deserialization will not work, and I will explain why. The code would look something like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class DataGridData {
#XmlElements({
#XmlElement(name = "B", type = DataGridCurrentPage.class),
#XmlElement(name = "B", type = XmlAttributeHolder.class),
})
List<Object> values;
}
Now if you create your POJOs and serialize you will get the XML you need. If this is your need then you should be fine.
But if you want also to deserialize, since both classes are mapped into #XmlElement with name 'B' it will pick one of the target classes for deserialization (I believe the last specified).

MOXy adds type and uses toString?

Disclaimer: I'm new to all this, so my terminology may be wrong
I've got some Java POJO's I want to serialize to JSON & XML. I'm using MOXy 2.5.0 for JSON and Jersey 2.4.1.
#XmlRootElement
class Root {
// #XmlElements({#XmlElement(name = "destination_address", type = LatLong.class),
// #XmlElement(name = "destination_address", type = Polygon.class)})
public Object[] destination_addresses;
}
public class LatLong {
public double lat, lng;
}
public class Polygon {
protected List<LatLong> points = new ArrayList<LatLong>();
#XmlElements({#XmlElement(name = "lat", type = Lat.class),
#XmlElement(name = "lng", type = Lng.class)})
private LatOrLong[] getLatOrLongs() {
LatOrLong[] retval = new LatOrLong[points.size() * 2];
for (int point = 0; point < points.size(); point++) {
LatLong latLong = points.get(point);
retval[point * 2] = new Lat(latLong.lat);
retval[point * 2 + 1] = new Lng(latLong.lng);
}
return retval;
}
static abstract private class LatOrLong {
#XmlValue
private double latOrLong;
private LatOrLong() {}
private LatOrLong(double latOrLong) {this.latOrLong = latOrLong;}
}
static private class Lat extends LatOrLong {
private Lat() {}
private Lat(double lat) {super(lat);}
}
static private class Lng extends LatOrLong {
private Lng() {}
private Lng(double lng) {super(lng);}
}
}
This doesn't work in XML with the two lines commented out, but in JSON, MOXy is adding a type: latLong attribute to the destination_addresses array, as well as using the toString() method of Polygon.
How can I hide the type?
How can I get MOXy to use getLatOrLongs() instead of toString()?
EDIT: I've simplified Polygon to just serialize points and changed destination_addresses to be a List<Object> instead of Object[] .
The pojo mapping feature is by default enabled using MOXy.
But if for any case, you need to implement a specific marshal/unmarshal (I take here ObjectID from MongoDB which is a t:
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.bson.types.ObjectId;
public class ObjectIdXmlAdapter extends XmlAdapter<String, ObjectId> {
#Override
public String marshal(ObjectId id) throws Exception {
if(id == null) {
return null;
} else {
return id.toString();
}
}
#Override
public ObjectId unmarshal(String id) throws Exception {
return new ObjectId(id);
}
}
And then, on your POJO:
#XmlJavaTypeAdapter(ObjectIdXmlAdapter.class)
public ObjectId getId() {
return id;
}
Will serialize your id element as expected...
Hope this helps, this should be the main trouble.

Categories