I want to marshal/unmarshal 2 custom types. And the type 1 has a List. Marshalling works as expected - I see a separate element for every type2 element in the list, but when I try to unmarshal it this doesn't work.
Workflow
#XmlRootElement
public class Workflow {
private String userEmail;
private List<WorkflowStep> steps = new ArrayList<WorkflowStep>(5);
//required by jaxb
public Workflow() {
}
public void setSteps(List<WorkflowStep> steps) {
this.steps = steps;
}
public List<WorkflowStep> getSteps() {
//do a defensive copy
return new ArrayList<WorkflowStep>(steps);
}
public void setUserEmail(String email) {
userEmail = email;
}
public String getUserEmail() {
return userEmail;
}
}
WorkflowStep
#XmlRootElement
public class WorkflowStep {
private int cpu = 1;
private int mem = 1000;
private Map<String, String> parameters = Collections.emptyMap();
public WorkflowStep() {
}
public void setCpu(int numCores) {
cpu = numCores;
}
public int getCpu() {
return cpu;
}
public void setMem(int mb) {
mem = mb;
}
public int getMem() {
return mem;
}
}
Here is an example output if I access the web service from a browser:
<workflow>
<steps>
<cpu>1</cpu>
<inputId>237</inputId>
<mem>1000</mem>
<parameters/>
<status>NOT-YET-STARTED</status>
<stepId>1509</stepId>
<submoduleId>0</submoduleId>
<workflowId>797</workflowId>
</steps>
<steps>
<cpu>1</cpu>
<inputId>364</inputId>
<mem>1000</mem>
<parameters/>
<status>NOT-YET-STARTED</status>
<stepId>1510</stepId>
<submoduleId>3</submoduleId>
<workflowId>797</workflowId>
</steps>
<userEmail>foo#bar.com</userEmail>
<workflowId>797</workflowId>
<workflowName>test-name</workflowName>
</workflow>
WorkflowStep is marshalled/unmarshalled successfully but a List cannot be unmarshalled why is that? Nor the application server, nor the REST client receive any errors or exceptions, the client just ignores the list of WorkflowSteps? From the above example output I'd expect that every element would be converted into an object of type WorkflowStep and then will automatically be added to the List?
The problem is with this method:
public List<WorkflowStep> getSteps() {
//do a defensive copy
return new ArrayList<WorkflowStep>(steps);
}
Your JAXB implementation checks if there is already a List for this method, and if there is it uses it. The List you are returning is not held by the object you are unmarshalling, so after the unmarshal operation the List is lost.
Option #1 - Change the getSteps() Method
public List<WorkflowStep> getSteps() {
//don't do a defensive copy
return steps;
}
Option #2 - Use Field Access
You could change this your specify that JAXB should use field access.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Workflow {
...
}
For more information
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
Related
What could be the problem ?
I have a DTO class where I get john , after which I need to pull these values out of there and set them to another object.
Previously, I did it via VS code and everything worked fine. I moved these classes to the INTELLIJ IDEA after which I can't get the values from this class.
Example DTO UPDATE:
public class GetModel {
#JsonProperty("vendor")
private String vendor;
private List<Result> result = null;
//get/set
#JsonProperty("result")
public List<Result> getResult() {
return result;
}
#JsonProperty("result")
public void setResult(List<Result> result) {
this.result = result;
}
class Result {
#JsonProperty("FIELDS")
private Fields fields;
//get/set
class Fields {
#JsonProperty("adress")
private String adress;
//get/set
}
}
}
I am creating an object to set a value from a class GetModel
Example:
GetModel info;
Show show = new Show();
show.setAuthorityCyr(info.getResult().get(0).getFields().getAdress());
but I cannot get to it, respectively, and get the value.
the same project with the same code (only Java 11 was used) it gave these parameters.
Here Java 17 is used and does not give meaning.
Only in the context menu it shows incomprehensible fields as indicated in the image:
how to get to the values to set them in this case?
UPDATE
POJO :
public class Show {
// other fields
#JsonProperty("cyr")
private String authorityCyr;
#JsonProperty("cyr")
public String getAuthorityCyr() {
return authorityCyr;
}
#JsonProperty("cyr")
public void setAuthorityCyr(String authorityCyr) {
if(authorityCyr !=null){
this.authorityCyr = authorityCyr;
} else if(authorityCyr==null){
this.authorityCyr = "null";
}
}
// other get/set
}
I am in a situation, where I'm trying to implement a (relatively simple) abstract syntax tree. All of the nodes inherit from a type called SimpleNode containing some code to store line and column information and accepting a visitor.
Now, some of the nodes should also be nameable, while others should have a property "accessible" (eg. public or private). Some nodes should even support both interfaces.
I'd preferably implement this using virtual inheritance and write two classes NameableNode and AccessibleNode, but Java doesn't support MI.
Eg NameableNode might have field "name" and implement simple getters and setters for this field. Similarly, AccessibleNode might also have a field "accessibility" and getters/setters.
What is a good way to implement this and avoid introducing code duplication in a huge part of the code base?
Small code example:
public class SimpleNode {
private int line = 0;
private int column = 0;
/* Getters and setters for line/column. */
/* ... */
}
public class NameableNode extends SimpleNode {
private String name = "";
/* Getters and setters for name */
}
public class AccessibleNode extends SimpleNode {
private boolean isPublic = false;
/* Getters and setters for accessibility */
}
You're looking for composition. There are many flavors of this - I will propose one that, from my understanding of what you're trying to build, should suit your purpose.
First, let's create some interfaces for yours Nodes:
public interface Nameable {
/* Getters and setters for name */
}
public interface Accessible {
/* Getters and setters for accessibility */
}
Next, you probably don't want to repeat the same implementation for every Node, so let's create those implementations:
public class NameDelegate() {
private String name = "";
/* Getters and setters for name */
}
public class AccessDelegate() {
private boolean isPublic = false;
/* Getters and setters for accessibility */
}
Now, let's put everything together:
public class SomeNodeA extends SimpleNode implements Nameable {
private NameDelegate nameDelegate;
public SomeNodeA(NameDelegate nameDelegate) {
this.nameDelegate = nameDelegate;
}
#Override
public String getName() {
return nameDelegate.getName();
}
#Override
public String setName(String name) {
nameDelegate.setName(name);
}
}
You can also have both behaviours in a single class:
public class SomeNodeB extends SimpleNode implements Nameable, Accessible {
private NameDelegate nameDelegate;
private AccessDelegate accessDelegate;
public SomeNodeB(NameDelegate nameDelegate, AccessDelegate accessDelegate) {
this.nameDelegate = nameDelegate;
this.accessDelegate = accessDelegate;
}
#Override
public String getName() {
return nameDelegate.getName();
}
#Override
public String setName(String name) {
nameDelegate.setName(name);
}
#Override
public boolean getAccessibility() {
return accessDelegate.getAccessibility();
}
/* etc... */
}
The idea is, you can package the state and the functionality of the different "features" into individual delegates, and expose them as corresponding interfaces in your Nodes.
Also, when operating on the Nodes, if you need to know whether a given instance of a Node supports a specific feature, you can use instanceof - e.g.:
if (someNode instanceof Nameable) {
// do naming stuff
}
In this case I would use the composition approach over inheritance:
public class Node {
private int line = 0;
private int column = 0;
/* Getters and setters for line/column. */
/* ... */
private String name = null;
public String getName() {
return this.name;
}
public void setName(String name) {
this._name = name;
}
private Boolean _isPublic = null;
public String isPublic() {
return this.name;
}
public void setIsPublic(boolean isPublic) {
this._isPublic = isPublic;
}
public boolean hasAccessibility() {
return this._isPublic != null;
}
public boolean hasName() {
return this._name != null;
}
}
Another solution that I like a bit more is creating these attributes dynamically using a HashMap and an enum that indicates all the possible attributes of a node. This way is more generic, as it requires to write less code for supporting new attributes, But it is also less typesafe(ish), as the additional attributes need to be casted at runtime:
import java.util.HashMap;
enum NodeAttribute {
NAME,
ACCESSIBILTY
}
enum NodeAccessibility {
PUBLIC,
PRIVATE
}
public class Node {
private int line = 0;
private int column = 0;
// Notice that this Object usage might involve some boxing for attributes of premitive type
private HashMap<NodeAttribute, Object> additionalAttributes = new HashMap<NodeAttribute, Object>();
/* Getters and setters for line/column. */
/* ... */
public boolean hetAttribute(NodeAttribute attribute) {
return this.additionalAttributes.containsKey(attribute);
}
public <T> T getAttributeValue(NodeAttribute attribute, Class<T> attributeClass) {
Object attributeValue = this.additionalAttributes.get(attribute);
// You may want to wrap the ClassCastException that may be raisen here to a more specfic error
T castedAttributeValue = attributeClass.cast(attributeValue);
return castedAttributeValue;
}
public void setAttributeValue(NodeAttribute attribute, Object value) {
// Notice that this implemintation allows changing the type of an existing attribute,
// If this is invalid behavior in your case you can throw an exception instead
this.additionalAttributes.put(attribute, value);
}
}
// Example usage
public class Program {
public static void main(String[] args) {
Node nodeWithNameOnly = new Node();
nodeWithNameOnly.setAttributeValue(NodeAttribute.NAME, 'node1');
Node nodeWithBoth = new Node();
nodeWithBoth.setAttributeValue(NodeAttribute.NAME, 'node2');
nodeWithBoth.setAttributeValue(NodeAttribute.ACCESSIBILTY, NodeAccessibility.PRIVATE);
Program.doStuffWithNode(nodeWithNameOnly);
/* output:
Node name: node1
*/
Program.doStuffWithNode(nodeWithBoth);
/* output:
Node name: node2
Node is public: False
*/
}
public static void doStuffWithNode(Node node) {
if (nodeWithNameOnly.hetAttribute(NodeAttribute.NAME)) {
String nodeName = nodeWithNameOnly.getAttributeValue(NodeAttribute.NAME, String.class);
system.out.println("Node name: " + nodeName);
}
if (nodeWithNameOnly.hetAttribute(NodeAttribute.ACCESSIBILTY)) {
NodeAccessibility nodeAccessibilty =
nodeWithNameOnly.getAttributeValue(NodeAttribute.ACCESSIBILTY, NodeAccessibility.class);
boolean nodeIsPublic = nodeAccessibilty == NodeAccessibility.PUBLIC;
system.out.println("Node is public: " + String.valueOf(nodeIsPublic));
}
}
}
In any case, this is the main rule of thumb - Inheritance should be used for an "is a" relation, whereas composition should be used for an "has a" relation.
For instance:
Fish extends Animal because a Fish is an Animal.
Post holds comments because a Post has comments.
And in our case, a node has a name and an accessibility level so it should hold them.
I have used One-to-Many Mapping in my project. I have stored a list of clicks for every user.
But when I retrieve the list by calling getClicks() methodm Hibernate returns list in different format.
Something like this.
"[com.zednx.tech.persistence.Click#29df9a77]"
So I tried Reading Every value from the list and assign to a new List.
List<Click> clicks=new ArrayList<Click>();
for(Click c: e.getClicks()){
Click temp = new Click();
temp.setAff_source(c.getAff_source());
temp.setCb_to_award(c.getCb_to_award());
temp.setCb_type(c.getCb_type());
clicks.add(temp);
}
But when i print the items of new List it stills prints the same way.
I need to build a JSON from the resulting String of this list.
So if the list is returned in format, it wont help me.
I couldn't find anything regarding this except How to pretty print Hibernate query results?
I tried Arrays.ToString(Object o). But it doesn't work.
GSON builder part-
Gson gson = new GsonBuilder()
.registerTypeAdapter(Click.class, new MyTypeAdapter<Click>())
.create();
List<Click> clicks=new ArrayList<Click>();
for(Click c: e.getClicks()){
Click temp = new Click();
temp.setAff_source(c.getAff_source());
temp.setCb_to_award(c.getCb_to_award());
temp.setCb_type(c.getCb_type());
temp.setCom_to_recieve(c.getCom_to_recieve());
temp.setStore_name(c.getStore_name());
temp.setT_date(c.getT_date());
temp.setT_status(c.getT_status());
temp.setT_ticket(c.getT_ticket());
temp.setUid(c.getUid());
System.out.println(c.toString());
clicks.add(temp);
}
String json = gson.toJson(clicks, Click.class);
Click.java
#Entity
#Table(name="click")
public class Click {
#Id
#Column(name="t_ticket")
private String t_ticket;
#Column(name="uid",nullable=false)
private long uid;
public long getUid() {
return uid;
}
public void setUid(long uid) {
this.uid = uid;
}
#ManyToOne
#JoinColumn(name="uid",
insertable=false, updatable=false,
nullable=false)
private Earning earning;
#Column(name="store_name")
private String store_name;
#Column(name="t_status")
private String t_status;
#Column(name="aff_source")
private String aff_source;
#Column(name="com_to_recieve")
private float com_to_recieve;
#Column(name="t_date")
private Date t_date;
#Column(name="cb_to_award")
private float cb_to_award;
#Column(name="cb_type")
private String cb_type;
public String getT_ticket() {
return t_ticket;
}
public void setT_ticket(String t_ticket) {
this.t_ticket = t_ticket;
}
public Earning getEarning() {
return earning;
}
public void setEarning(Earning earning) {
this.earning = earning;
}
public String getStore_name() {
return store_name;
}
public void setStore_name(String store_name) {
this.store_name = store_name;
}
public String getT_status() {
return t_status;
}
public void setT_status(String t_status) {
this.t_status = t_status;
}
public String getAff_source() {
return aff_source;
}
public void setAff_source(String aff_source) {
this.aff_source = aff_source;
}
public float getCom_to_recieve() {
return com_to_recieve;
}
public void setCom_to_recieve(float com_to_recieve) {
this.com_to_recieve = com_to_recieve;
}
public Date getT_date() {
return t_date;
}
public void setT_date(Date t_date) {
this.t_date = t_date;
}
public float getCb_to_award() {
return cb_to_award;
}
public void setCb_to_award(float cb_to_award) {
this.cb_to_award = cb_to_award;
}
public String getCb_type() {
return cb_type;
}
public void setCb_type(String cb_type) {
this.cb_type = cb_type;
}
Any Help is appreciated.
You need to implement a toString method, as your current Click class likely doesn't have one, so it just prints as the name of the class and instance identifier.
Okay, I could solve my problem finally.
I made another POJO without any annotations and Mapped the List items to that POJO class.
I think the problem was with Annotation of mapping on another class which I had in original POJO.
Also getString() method only helps in changing format of identifier. So basically it has nothing to do with JSON building unless you format getString() in form of JSON.
Hope it helps. If anyone wants new temp POJO I made I can post it if requested.
Thanks.
We are currently attempting to implement an extension to SlingModels, to allow a slingmodel to be persisted to the JCR directly.
Our strategy has 2 considered starting conditions:
1. A new object that is to be persisted
2. An object that has been retrieved from the JCR, altered, and is then to be persisted again
For situation 1, we are using reflection to examine the object, create a new node for the model, insert properties for any of the primitive variables found, and recursively use the same persistence approach for any complex model objects found as variables, and collections.
My question on best approach relates to situation 2. If we pull out an object from the repository, we cannot be guaranteed that the node will not be synchronously changed in the meantime. Thus, we would like to implement a change watcher on the SlingModel that keeps a transaction journal on any changes made. The transactions can then be used to set the relevant properties when persisting the object back to the JCR again.
I have considered using an observer pattern, but this would mean that we would need to implement a function within the setter on each SlingModel, which is not ideal at all, as it requires a developer to remember to add the code and do it correctly.
Ideally, I would like to implement something like an interceptor directly on the variable, or if not possible, on the setter itself, and mandate that each model would then need to use a getter/setter for each variable. We can configure code scanning tools to enforce developers to implement getter/setters.
What would the be the best way to approach the change watcher here?
import java.util.List;
public class Teacher {
private String userName;
private String cource;
private List<Student> students;
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getCource() {
return cource;
}
public void setCource(String cource) {
this.cource = cource;
}
}
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class ClassFacadeCglib implements MethodInterceptor{
private Object target;
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// callback method
enhancer.setCallback(this);
// create proxy object
return enhancer.create();
}
#Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
if(method.getName().startsWith("set")){
System.out.println(method.getName()+" start");
proxy.invokeSuper(obj, args);
System.out.println(method.getName()+" end..");
}
if(method.getName().startsWith("get")){
System.out.println(method.getName()+" start");
proxy.invokeSuper(obj, args);
System.out.println(method.getName()+" end");
}
return null;
}
}
public class Main {
public static void main(String[] args) {
ClassFacadeCglib cglib=new ClassFacadeCglib();
Teacher teacher=(Teacher)cglib.getInstance(new Teacher());
teacher.setCource("Math");
teacher.getUserName();
}
}
Note :
cglib-full-2.0.2.jar is required for running.
see https://repo1.maven.org/maven2/cglib/cglib-full/2.0.2/
I am using Tapestry 5.3.6 for a web application and I want the user to edit an instance of a Java class (a "bean", or POJO) using a web form (which immediately suggests the use of beaneditform) - however the Java class to be edited has a fairly complex structure. I am looking for the simplest way of doing this in Tapestry 5.
Firstly, lets define some utility classes e.g.
public class ModelObject {
private URI uri;
private boolean modified;
// the usual constructors, getters and setters ...
}
public class Literal<T> extends ModelObject {
private Class<?> valueClass;
private T value;
public Literal(Class<?> valueClass) {
this.valueClass = valueClass;
}
public Literal(Class<?> valueClass, T value) {
this.valueClass = valueClass;
this.value = value;
}
// the usual getters and setters ...
}
public class Link<T extends ModelObject> extends ModelObject {
private Class<?> targetClass;
private T target;
public Link(Class<?> targetClass) {
this.targetClass = targetClass;
}
public Link(Class<?> targetClass, T target) {
this.targetClass = targetClass;
this.target = target;
}
// the usual getters and setters ...
}
Now you can create some fairly complex data structures, for example:
public class HumanBeing extends ModelObject {
private Literal<String> name;
// ... other stuff
public HumanBeing() {
name = new Literal<String>(String.class);
}
// the usual getters and setters ...
}
public class Project extends ModelObject {
private Literal<String> projectName;
private Literal<Date> startDate;
private Literal<Date> endDate;
private Literal<Integer> someCounter;
private Link<HumanBeing> projectLeader;
private Link<HumanBeing> projectManager;
// ... other stuff, including lists of things, that may be Literals or
// Links ... e.g. (ModelObjectList is an enhanced ArrayList that remembers
// the type(s) of the objects it contains - to get around type erasure ...
private ModelObjectList<Link<HumanBeing>> projectMembers;
private ModelObjectList<Link<Project>> relatedProjects;
private ModelObjectList<Literal<String>> projectAliases;
// the usual constructors, getters and setters for all of the above ...
public Project() {
projectName = new Literal<String>(String.class);
startDate = new Literal<Date>(Date.class);
endDate = new Literal<Date>(Date.class);
someCounter = new Literal<Integer>(Integer.class);
projectLeader = new Link<HumanBeing>(HumanBeing.class);
projectManager = new Link<HumanBeing>(HumanBeing.class);
projectMembers = new ModelObjectList<Link<HumanBeing>>(Link.class, HumanBeing.class);
// ... more ...
}
}
If you point beaneditform at an instance of Project.class, you will not get very far before you have to supply a lot of custom coercers, translators, valueencoders, etc - and then you still run into the problem that you can't use generics when "contributing" said coercers, translators, valueencoders, etc.
I then started writing my own components to get around these problems (e.g. ModelObjectDisplay and ModelObjectEdit) but this would require me to understand a lot more of the guts of Tapestry than I have time to learn ... it feels like I might be able to do what I want using the standard components and liberal use of "delegate" etc. Can anyone see a simple path for me to take with this?
Thanks for reading this far.
PS: if you are wondering why I have done things like this, it is because the model represents linked data from an RDF graph database (aka triple-store) - I need to remember the URI of every bit of data and how it relates (links) to other bits of data (you are welcome to suggest better ways of doing this too :-)
EDIT:
#uklance suggested using display and edit blocks - here is what I had already tried:
Firstly, I had the following in AppPropertyDisplayBlocks.tml ...
<t:block id="literal">
<t:delegate to="literalType" t:value="literalValue" />
</t:block>
<t:block id="link">
<t:delegate to="linkType" t:value="linkValue" />
</t:block>
and in AppPropertyDisplayBlocks.java ...
public Block getLiteralType() {
Literal<?> literal = (Literal<?>) context.getPropertyValue();
Class<?> valueClass = literal.getValueClass();
if (!AppModule.modelTypes.containsKey(valueClass))
return null;
String blockId = AppModule.modelTypes.get(valueClass);
return resources.getBlock(blockId);
}
public Object getLiteralValue() {
Literal<?> literal = (Literal<?>) context.getPropertyValue();
return literal.getValue();
}
public Block getLinkType() {
Link<?> link = (Link<?>) context.getPropertyValue();
Class<?> targetClass = link.getTargetClass();
if (!AppModule.modelTypes.containsKey(targetClass))
return null;
String blockId = AppModule.modelTypes.get(targetClass);
return resources.getBlock(blockId);
}
public Object getLinkValue() {
Link<?> link = (Link<?>) context.getPropertyValue();
return link.getTarget();
}
AppModule.modelTypes is a map from java class to a String to be used by Tapestry e.g. Link.class -> "link" and Literal.class -> "literal" ... in AppModule I had the following code ...
public static void contributeDefaultDataTypeAnalyzer(
MappedConfiguration<Class<?>, String> configuration) {
for (Class<?> type : modelTypes.keySet()) {
String name = modelTypes.get(type);
configuration.add(type, name);
}
}
public static void contributeBeanBlockSource(
Configuration<BeanBlockContribution> configuration) {
// using HashSet removes duplicates ...
for (String name : new HashSet<String>(modelTypes.values())) {
configuration.add(new DisplayBlockContribution(name,
"blocks/AppPropertyDisplayBlocks", name));
configuration.add(new EditBlockContribution(name,
"blocks/AppPropertyEditBlocks", name));
}
}
I had similar code for the edit blocks ... however none of this seemed to work - I think because the original object was passed to the "delegate" rather than the de-referenced object which was either the value stored in the literal or the object the link pointed to (hmm... should be [Ll]inkTarget in the above, not [Ll]inkValue). I also kept running into errors where Tapestry couldn't find a suitable "translator", "valueencoder" or "coercer" ... I am under some time pressure so it is difficult to follow these twisty passages through in order to get out of the maze :-)
I would suggest to build a thin wrapper around the Objects you would like to edit though the BeanEditForm and pass those into it. So something like:
public class TapestryProject {
private Project project;
public TapestryProject(Project proj){
this.project = proj;
}
public String getName(){
this.project.getProjectName().getValue();
}
public void setName(String name){
this.project.getProjectName().setValue(name);
}
etc...
}
This way tapestry will deal with all the types it knows about leaving you free of having to create your own coersions (which is quite simple in itself by the way).
You can contribute blocks to display and edit your "link" and "literal" datatypes.
The beaneditform, beaneditor and beandisplay are backed by the BeanBlockSource service. BeanBlockSource is responsible for providing display and edit blocks for various datatypes.
If you download the tapestry source code and have a look at the following files:
tapestry-core\src\main\java\org\apache\tapestry5\corelib\pages\PropertyEditBlocks.java
tapestry-core\src\main\resources\org\apache\tapestry5\corelib\pages\PropertyEditBlocks.tml
tapestry-core\src\main\java\org\apache\tapestry5\services\TapestryModule.java
You will see how tapestry contributes EditBlockContribution and DisplayBlockContribution to provide default blocks (eg for a "date" datatype).
If you contribute to BeanBlockSource, you could provide display and edit blocks for your custom datatypes. This will require you reference blocks by id in a page. The page can be hidden from your users by annotating it with #WhitelistAccessOnly.
http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/BeanBlockSource.html
http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/DisplayBlockContribution.html
http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/EditBlockContribution.html
http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/WhitelistAccessOnly.html
Here's an example of using an interface and a proxy to hide the implementation details from your model. Note how the proxy takes care of updating the modified flag and is able to map URI's from the Literal array to properties in the HumanBeing interface.
package com.github.uklance.triplestore;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
public class TripleStoreOrmTest {
public static class Literal<T> {
public String uri;
public boolean modified;
public Class<T> type;
public T value;
public Literal(String uri, Class<T> type, T value) {
super();
this.uri = uri;
this.type = type;
this.value = value;
}
#Override
public String toString() {
return "Literal [uri=" + uri + ", type=" + type + ", value=" + value + ", modified=" + modified + "]";
}
}
public interface HumanBeing {
public String getName();
public void setName(String name);
public int getAge();
public void setAge();
}
public interface TripleStoreProxy {
public Map<String, Literal<?>> getLiteralMap();
}
#Test
public void testMockTripleStore() {
Literal<?>[] literals = {
new Literal<String>("http://humanBeing/1/Name", String.class, "Henry"),
new Literal<Integer>("http://humanBeing/1/Age", Integer.class, 21)
};
System.out.println("Before " + Arrays.asList(literals));
HumanBeing humanBeingProxy = createProxy(literals, HumanBeing.class);
System.out.println("Before Name: " + humanBeingProxy.getName());
System.out.println("Before Age: " + humanBeingProxy.getAge());
humanBeingProxy.setName("Adam");
System.out.println("After Name: " + humanBeingProxy.getName());
System.out.println("After Age: " + humanBeingProxy.getAge());
Map<String, Literal<?>> literalMap = ((TripleStoreProxy) humanBeingProxy).getLiteralMap();
System.out.println("After " + literalMap);
}
protected <T> T createProxy(Literal<?>[] literals, Class<T> type) {
Class<?>[] proxyInterfaces = { type, TripleStoreProxy.class };
final Map<String, Literal> literalMap = new HashMap<String, Literal>();
for (Literal<?> literal : literals) {
String name = literal.uri.substring(literal.uri.lastIndexOf("/") + 1);
literalMap.put(name, literal);
}
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass().equals(TripleStoreProxy.class)) {
return literalMap;
}
if (method.getName().startsWith("get")) {
String name = method.getName().substring(3);
return literalMap.get(name).value;
} else if (method.getName().startsWith("set")) {
String name = method.getName().substring(3);
Literal<Object> literal = literalMap.get(name);
literal.value = args[0];
literal.modified = true;
}
return null;
}
};
return type.cast(Proxy.newProxyInstance(getClass().getClassLoader(), proxyInterfaces, handler));
}
}