When attempting to edit a new (proxy) entity using RequestFactoryEditorDriver.edit() I am getting the following error: "Exception caught: Attempting to edit an EntityProxy previously edited by another RequestContext". I am fairly sure that this is a result of my misunderstanding of the request factory/editor framework architecture. Here is the editor code that I think pertains to this problem:
public class OrgMaintenanceWidget extends Composite implements Editor<IOrgProxy> {
... other fields ...
private IOrgEditorDriver _orgEditorDriver;
interface IOrgEditorDriver extends RequestFactoryEditorDriver<IOrgProxy, OrgMaintenanceWidget> {}
public OrgMaintenanceWidget(final IClientFactory clientFactory) {
... widget initialization ...
_orgEditorDriver = GWT.create(IOrgEditorDriver.class);
_orgEditorDriver.initialize(_clientFactory.getRequestFactory().getEventBus(),
_clientFactory.getRequestFactory(), this);
}
#UiHandler("newButton")
public void onNewButtonClick(final ClickEvent clickEvent) {
_org = _clientFactory.getCache().getOrgCache().newOrg();
_orgEditorDriver.edit(_org, _clientFactory.getRequestFactory().orgRequestContext());
}
...
}
It's the "_orgEditorDriver.edit()" line that causes the exception. The "newOrg()" method is:
public IOrgProxy newOrg() {
return _clientFactory.getRequestFactory().orgRequestContext().create(IOrgProxy.class);
}
The RequestFactory is simply:
public interface IRequestFactory extends RequestFactory {
IOrgRequestContext orgRequestContext();
}
I am sure that I'm missing something fundamental about editing a new entity. When I edit an existing entity everything is fine ... the UI components are populated automatically, and flushing the editor back to the entity works very nicely. Here's the code that initiates editing for an existing entity:
#UiHandler("newButton")
public void onNewButtonClick(final ClickEvent clickEvent) {
_org = _clientFactory.getCache().getOrgCache().newOrg();
_orgEditorDriver.edit(_org, _clientFactory.getRequestFactory().orgRequestContext());
}
Any help would be greatly appreciated, and I'll try to publish any lessons learned.
This code:
_clientFactory.getRequestFactory().orgRequestContext().create(IOrgProxy.class);
Means:
Create new orgRequestContext()
Create new IOrgProxy using this context
Edit new IOrgProxy using this context, because as docs say: "Returns a new mutable proxy that this request can carry to the server, perhaps to be persisted.", it means that the proxy is edited by this request.
This code:
_orgEditorDriver.edit(_org, _clientFactory.getRequestFactory().orgRequestContext());
Means:
Again, create new orgRequestContext() (because each invocation of getRequestFactory().orgRequestContext() provides new instance of orgRequestContext()
"Start driving the Editor and its sub-editors with data." as docs say. But as a part of it, use passed orgRequestContext() to edit passed IOrgProxy instance, so that the proxy is editable.
Because the proxy was already edited while created by other RequestContext, you get the exception, because there is fundamental rule in RequestFactory, that proxy can be edited only by one RequestContext.
See also this thread.
I think you can't create an object with one RequestContext and then edit it with another one.
So you can solve this in two ways:
Persist the created object with the RequestContext you used when you created the object. The save method should return the persisted object and this persisted object can be passed to the editor with a fresh new RequestContext
Somewhere save the RequestContext you used for creating the object and pass it to the edit function of your Driver
Solution two could look something like this:
#UiHandler("newButton")
public void onNewButtonClick(final ClickEvent clickEvent) {
IOrgRequestContext ctx = _clientFactory.getRequestFactory().orgRequestContext();
_org = ctx.create(IOrgProxy.class);
_orgEditorDriver.edit(_org,ctx );
}
Related
I'm currently working on a simple GWT project. One of the things I'd like to do is that when the page loads I can dynamically populate the contents of a ListBox based on certain criteria. I actually don't see any handlers for a ListBox to handle the initial render event but I see change handlers.
How does one populate a ListBox contents with data from the server side on pageload with GWT?
Right now I have a class that implements EntryPoint that has a
final ListBox fooList = new ListBox();
I also have a set of beans but I also have a class implementing RemoteService. Since I can't seem to get direct calls to my user defined packages directly in the EntryPoint (which makes sense) how do I populate that ListBox with server side content on initial page load? Right now I'm using a List but I figure if I cant get that to work I can get a DB call to work...
I've tried things in the EntryPoint like:
for (String name : FOOS) {
fooList.addItem(name, name);
}
However FOOS would derive from a server side data and the EntryPoint is supposed to be largerly limited to what can compile to JS! I can't get user defined classes to be recognized on that side as that string is the result of a set of user defined classes.
I also tried creating a method in the class implementing RemoteService that returns a ListBox. This also didn't compile when I tried to call this method. Perhaps I don't fully understand how to call methods in a RemoteService service implementing class.
I've searched a lot and I can't find anything that clearly explains the fundamentals on this. My background is much more ASP.NET and JSPs so perhaps I'm missing something.
I'm using GWT 2.6 is that is relevant.
The usual procedure is the following:
Create a bean class for the data you want to transmit between client and server. Let's call it MyBean.
Place MyBean in the shared package of your project.
This class has to implement either Serializable or IsSerializable, otherwise GWT will complain that it doesn't know how to transmit it.
Create your RemoteService that contains the method you want to use to transmit MyBean from/to the server.
Once you get your data on the client using an AsyncCallback and your RemoteService, fill the ListBox using your beans, e.g. by calling MyBean#getName() or MyBean#toString().
Success!
I based my example on the GWT sample project ( I named it example), just replace the classes and it should work :
public class Example implements EntryPoint {
/**
* Create a remote service proxy to talk to the server-side Greeting
* service.
*/
private final GreetingServiceAsync greetingService = GWT
.create(GreetingService.class);
/**
* This is the entry point method.
*/
public void onModuleLoad() {
final ListBox listBox = new ListBox();
RootPanel.get("sendButtonContainer").add(listBox);
greetingService.getSomeEntries(new AsyncCallback<String[]>() {
#Override
public void onSuccess(String[] result) {
for (int i = 0; i < result.length; i++) {
listBox.addItem(result[i]);
}
}
#Override
public void onFailure(Throwable caught) {
}
});
}
}
This is our EntryPoint, it creates a listbox and calls the server with a AsyncCallback to get some dynamic data. If the call is successfull (onSuccess), the data is written into the listbox.
The GreetingService interface define the synchronous methods, it is implemented in the GreetingServiceImpl class :
#RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
String[] getSomeEntries() ;
}
The asynchronous counterpart is the GreetingServiceAsync interface, we used it before to call the server :
public interface GreetingServiceAsync {
void getSomeEntries(AsyncCallback<String[]> callback) ;
}
The GreetingServiceImpl class is located on the server. Here you could call for example a database:
#SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements
GreetingService {
#Override
public String[] getSomeEntries() {
String[] entries = { "Entry 1","Entry 2","Entry 3" };
return entries;
}
}
Now if you want to use some Bean/Pojo between the server and client, replace the String[] in each class/interface with the object name, put the class in the shared package and consider that it implements Serializable/IsSerializable.
I want to achieve two goals:
I want my model to be loaded every time from the DB when it's in a life-cycle (for every request there will be just one request to the DB)
I want my model to be attached dynamically to the page and that wicket will do all this oreable binding for me
In order to achieve these two goals I came to a conclusion that I need to use both CompoundPropertyModel and LoadableDetachableModel.
Does anyone know if this is a good approach?
Should I do new CompoundPropertyModel(myLoadableDetachableModel)?
Yes, you are right, it is possible to use
new CompoundPropertyModel<T>(new LoadableDetachableModel<T> { ... })
or use static creation (it does the same):
CompoundPropertyModel.of(new LoadableDetachableModel<T> { ... })
that has both features of compound model and lazy detachable model. Also detaching works correctly, when it CompoudPropertyModel is detached it also proxies detaching to inner model that is used as the model object in this case.
I use it in many cases and it works fine.
EXPLANATION:
See how looks CompoundPropertyModel class (I'm speaking about Wicket 1.6 right now):
public class CompoundPropertyModel<T> extends ChainingModel<T>
This mean, CompoundPropertyModel adds the property expression behavior to the ChainingModel.
ChainingModel has the following field 'target' and the constructor to set it.
private Object target;
public ChainingModel(final Object modelObject)
{
...
target = modelObject;
}
This take the 'target' reference to tho object or model.
When you call getObject() it checks the target and proxies the functionality if the target is a subclass of IModel:
public T getObject()
{
if (target instanceof IModel)
{
return ((IModel<T>)target).getObject();
}
return (T)target;
}
The similar functionality is implemented for setObject(T), that also sets the target or proxies it if the target is a subclass of IModel
public void setObject(T object)
{
if (target instanceof IModel)
{
((IModel<T>)target).setObject(object);
}
else
{
target = object;
}
}
The same way is used to detach object, however it check if the target (model object) is detachable, in other words if the target is a subclass if IDetachable, that any of IModel really is.
public void detach()
{
// Detach nested object if it's a detachable
if (target instanceof IDetachable)
{
((IDetachable)target).detach();
}
}
Is there a way to get the proxy that editor is editing?
The normal workflow would be:
public class Class implments Editor<Proxy>{
#Path("")
#UiField AntoherClass subeditor;
void someMethod(){
Proxy proxy = request.create(Proxy.class);
driver.save(proxy);
driver.edit(proxy,request);
}
}
Now if i got a subeditor of the same proxy
public class AntoherClass implements Editor<Proxy>{
someMethod(){
// method to get the editing proxy ?
}
}
Yes i know i can just set the proxy to the Child editor with setProxy() after its creation, but i want to know if there is something like HasRequestContext but for the edited proxy.
This usefull when you use for example ListEditor in non UI objects.
Thank you.
Two ways you can get a reference to the object that a given editor is working on. First, some simple data and a simple editor:
public class MyModel {
//sub properties...
}
public class MyModelEditor implements Editor<MyModel> {
// subproperty editors...
}
First: Instead of implementing Editor, we can pick another interface that also extends Editor, but allows sub-editors (LeafValueEditor does not allow sub-editors). Lets try ValueAwareEditor:
public class MyModelEditor2 implements ValueAwareEditor<MyModel> {
// subproperty editors...
// ValueAwareEditor methods:
public void setValue(MyModel value) {
// This will be called automatically with the current value when
// driver.edit is called.
}
public void flush() {
// If you were going to make any changes, do them here, this is called
// when the driver flushes.
}
public void onPropertyChange(String... paths) {
// Probably not needed in your case, but allows for some notification
// when subproperties are changed - mostly used by RequestFactory so far.
}
public void setDelegate(EditorDelegate<MyModel> delegate) {
// grants access to the delegate, so the property change events can
// be requested, among other things. Probably not needed either.
}
}
This requires that you implement the various methods as in the example above, but the main one you are interested in will be setValue. You do not need to invoke these yourself, they will be called by the driver and its delegates. The flush method is also good to use if you plan to make changes to the object - making those changes before flush will mean that you are modifying the object outside of the expected driver lifecycle - not the end of the world, but might surprise you later.
Second: Use a SimpleEditor sub-editor:
public class MyModelEditor2 implements ValueAwareEditor<MyModel> {
// subproperty editors...
// one extra sub-property:
#Path("")//bound to the MyModel itself
SimpleEditor self = SimpleEditor.of();
//...
}
Using this, you can call self.getValue() to read out what the current value is.
Edit: Looking at the AnotherEditor you've implemented, it looks like you are starting to make something like the GWT class SimpleEditor, though you might want other sub-editors as well:
Now if i got a subeditor of the same proxy
public class AntoherClass implements Editor<Proxy>{
someMethod(){
// method to get the editing proxy ?
}
}
This sub-editor could implement ValueAwareEditor<Proxy> instead of Editor<Proxy>, and be guaranteed that its setValue method would be called with the Proxy instance when editing starts.
In your child editor class, you can just implement another interface TakesValue, you can get the editing proxy in the setValue method.
ValueAwareEditor works too, but has all those extra method you don't really need.
This is the only solution I found. It involves calling the context edit before you call the driver edit. Then you have the proxy to manipulate later.
I´m trying to do my first steps with GWT/GIN.
I´ve downloaded the hellomvp example from google and followed this tutorial to get started with gin.
My problem is about this line in the configure-method of the HelloGinModule-class:
bind(ActivityMapper.class).to(AppActivityMapper.class).in(Singleton.class);
In my point of view it should bind my class "AppActivityMapper" as the active ActityManager.
But in fact the class constructor (or any method of the class) is never called, so the fired events are not caught.
The class AppActivityMapper looks like this:
public class AppActivityMapper implements ActivityMapper {
Provider<HelloActivity> helloActivityProvider;
Provider<GoodbyeActivity> goodbyeActivityProvider;
#Inject
public AppActivityMapper(final Provider<HelloActivity> helloActivityProvider, final Provider<GoodbyeActivity> goodbyeActivityProvider) {
this.helloActivityProvider = helloActivityProvider;
this.goodbyeActivityProvider = goodbyeActivityProvider;
}
#Override
public Activity getActivity(Place place) {
if (place instanceof HelloPlace) {
return helloActivityProvider.get();
} else if (place instanceof GoodbyePlace) {
return goodbyeActivityProvider.get();
}
return null;
}
}
In my example this code from my View-Class is called after clicking on a link:
presenter.goTo(new GoodbyePlace(name));
The event is fired to the event bus. But nothing happens.
Thanks in advance
You have defined an activity mapper somewhere in you GIN. But activity mapper have to be used in activity manager. Where do you create activity manager which will use your AppActivityMapper?
UPDATE:
The most logical thing is to keep activity manager out of the gin. E.g. in your ginjector you will have a method:
interface MyInjector extends Ginjector {
... //other methods
ActivityMapper getActivityMapper();
}
Than , when you create ginjector instance, you can create a manager and put correct activity mapper into it. for example:
MyInjector injector = GWT.create(MyInjector.class);
ActivityManager manager = new ActivityManager(injector.getActivityMapper(), injector.getEventBus());
If you have multiple managers and mappers, may be it will be better to extend ActivityManager class (so you can inject stuff into its constructor). Another solution is to use #Provides to initialize ActivityManager.
Making my first steps in RIA Services (VS2010Beta2) and i encountered this problem:
created an EF Model (no POCOs), generic repository on top of it and a RIA Service(hosted in an ASP.NET MVC application) and tried to get data from within the ASP.NET MVC application: worked well.
Next step: Silverlight client. Got a reference to the RIAService (through its context), queried for all the records of the repository and got them into the SL application as well (using this code sample):
private ObservableCollection<Culture> _cultures = new ObservableCollection<Culture>();
public ObservableCollection<Culture> cultures
{
get { return _cultures; }
set
{
_cultures = value;
RaisePropertyChanged("cultures");
}
}
....
//Get cultures
EntityQuery<Culture> queryCultures = from cu in dsCtxt.GetAllCulturesQuery()
select cu;
loCultures = dsCtxt.Load(queryCultures);
loCultures.Completed += new EventHandler(lo_Completed);
....
void loAnyCulture_Completed(object sender, EventArgs e)
{
ObservableCollection<Culture> temp=
new ObservableCollection<Culture>loAnyCulture.Entities);
AnyCulture = temp[0];
}
The problem is this: whenever i try to edit some data of a record (in this example the first record) i get this error:
This EntitySet of type 'Culture' does not support the 'Edit' operation.
I thought that i did something weird and tried to create an object of type Culture and assign a value to it: it worked well!
What am i missing? Do i have to declare an EntitySet? Do i have to mark it? Do i have to...what?
Thanks in advance
It turns out that in the DomainService class one has to implement (or at least to mark "placeholder methods") as "Edit", "Delete",... eg
[Delete]
public void DeleteCulture(Culture currentCulture)
{
throw new NotImplementedException("UpdateCulture not Implemented yet");
}
[Insert]
public void InsertCulture(Culture newCulture)
{
throw new NotImplementedException("InsertCulture not Implemented yet");
}
This way the OrganizationDomainContextEntityContainer class creates an EntitySet with parameter EntitySetOperations.All (meaning that all the CUD operations are available).
Hope it's useful for someone in the future!