How to solve waiting for RPC response in GWT? - gwt

I know that the RPC is asynchronous but how Can in other way (other than waiting for response) to solve this problem:
public static String htsl(String sentence)
{
final DataBaseAsync db = GWT.create(DataBase.class);
String cookie = staticContent.getCookie("ll");
String shortcut = cookie.split("/")[1];
final String[] lala = new String[1];
database.getTranslated(sentence, shortcut, new AsyncCallback<String>() {
#Override
public void onSuccess(String result) {
lala[0]=result;
//this result I want to return in static function htsl... ?
}
#Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
});
//here I have blank result, because RPC is slower than te return..
And i have always blank result
return lala[0];
}
I know that is not good, but.. If it is no working solution for this, how to wait for response?

Make htsl asynchronous (non-blocking, i.e. with a callack for the response rather than a return value)

Do it like so:
public static String htsl(String sentence, AsyncCallback<String> myCallback) {
final DataBaseAsync db = GWT.create(DataBase.class);
String cookie = staticContent.getCookie("ll");
String shortcut = cookie.split("/")[1];
final String[] lala = new String[1];
database.getTranslated(sentence, shortcut, myCallback);
}
Then you let some other class implement AsyncCallback, pass it as the myCallback argument to the htsl method and Bob's your uncle.

Related

JsonpRequestBuilder with typed response throws InCompatibleClassChangeError

I have an existing app that I'm adding a "Suggested Products" feature to and I'm having trouble with my JSONP response not being properly transformed to the typed JsArray. I'm hoping someone can give me an idea of what I'm doing wrong?
I have defined my type that will be returned from the server in its own class:
import com.google.gwt.core.client.JavaScriptObject;
public class SuggestedProduct extends JavaScriptObject {
protected SuggestedProduct() {}
public final native String getFormName();
public final native String getImageURL();
}
I have a method that uses the JsonpRequestBuilder to fire off a request to get my JSON.
private void loadSuggestedProducts() {
JsonpRequestBuilder builder = new JsonpRequestBuilder();
builder.requestObject(buildSuggestedProductURL(), new AsyncCallback<JsArray<SuggestedProduct>>() {
public void onFailure(Throwable caught) {
//Handle errors
}
public void onSuccess(JsArray<SuggestedProduct> data) {
if ( data == null) {
//Handle empty data
return;
}
SafeHtmlBuilder sb = new SafeHtmlBuilder();
sb.appendHtmlConstant("<h4>Suggested Products:</h4>");
for (int i=0; i < data.length(); i++) {
SuggestedProduct product = data.get(i); //<- This line throws the exception
sb.appendHtmlConstant("<div class=\"card\">");
sb.appendHtmlConstant("<img class=\"card-img-top\" src=\"" + product.getImageURL() + "\" alt=\"" + product.getFormName() + "\">");
sb.appendHtmlConstant("<div class=\"card-body\">");
sb.appendHtmlConstant("<h5 class=\"card-title\">" + product.getFormName() + "</h5>");
sb.appendHtmlConstant("<a onclick=\"javascript:addItems();\" class=\"cmd-add\">Add <i aria-hidden=\"true\" class=\"fa fa-plus-circle\"></i></a>");
sb.appendHtmlConstant("</div></div>");
}
view.getSuggestedProducts().setInnerSafeHtml(sb.toSafeHtml());
}
});
}
When I try to use a SuggestedProduct from the response, I get an error:
java.lang.IncompatibleClassChangeError: Found interface
com.google.gwt.cor.client.JsArray, but class was expected
I've been following the guide in the GWT documentation. I don't see any difference between what I'm trying and what they say will work. When I debug, it looks as though the returned data is an array of SuggestedProducts, so I'm stumped as to how to proceed. Any help would be appreciated.
After closer inspection I realized my overlay type was missing method bodies for what fields to return from the JSON object they represented. The fix was to include the proper JSNI method definitions.
import com.google.gwt.core.client.JavaScriptObject;
public class SuggestedProduct extends JavaScriptObject {
protected SuggestedProduct() {}
public final native String getFormName() /*-{ return this.formname; }-*/;
public final native String getImageURL() /*-{ return this.imageurl; }-*/;
}

Gwt Simple pager issues with a column sort handler

I have set up an AsyncDataProvider for my CellTable and added it to a SimplePager. I have hooked up a ListHandler to take care of sorting based on a column.
When I click the header of that column, the data doesn't change but on going to the next/previous page within the pager the data is then sorted. Also before the column is clicked there is no visual indicator on the column that would indicate that it is meant to be sortable.
How can I get the data to update when I click the header of the Column?
Here's my code snippet
service.getHosts(environment, new AsyncCallback<Set<String>>() {
#Override
public void onSuccess(final Set<String> hosts) {
final List<String> hostList = new ArrayList<String>(hosts);
//Populate the table
CellTable<String> hostTable = new CellTable<String>();
TextColumn<String> hostNameColumn = new TextColumn<String>(){
#Override
public String getValue(String string){
return string;
}
};
NumberCell numberCell = new NumberCell();
Column<String, Number> lengthColumn = new Column<String, Number>(numberCell){
#Override
public Number getValue(String string) {
return new Integer(string.length());
}
};
AsyncDataProvider<String> dataProvider = new AsyncDataProvider<String>() {
#Override
protected void onRangeChanged(HasData<String> data) {
int start = data.getVisibleRange().getStart();
int end = start + data.getVisibleRange().getLength();
List<String> subList = hostList.subList(start, end);
updateRowData(start, subList);
}
};
// Hooking up sorting
ListHandler<String> columnSortHandler = new ListHandler<String>(hostList);
columnSortHandler.setComparator(lengthColumn, new Comparator<String>(){
#Override
public int compare(String arg0, String arg1) {
return new Integer(arg0.length()).compareTo(arg1.length());
}
});
hostTable.setPageSize(10);
hostTable.addColumnSortHandler(columnSortHandler);
hostTable.addColumn(hostNameColumn,"Host Name");
lengthColumn.setSortable(true);
hostTable.addColumn(lengthColumn, "Length");
VerticalPanel verticalPanel = new VerticalPanel();
SimplePager pager = new SimplePager();
pager.setDisplay(hostTable);
dataProvider.addDataDisplay(hostTable);
dataProvider.updateRowCount(hosts.size(), true);
verticalPanel.add(hostTable);
verticalPanel.add(pager);
RootPanel.get().add(verticalPanel);
}
#Override
public void onFailure(Throwable throwable) {
Window.alert(throwable.getMessage());
}
});
I'm not sure how to make sure that the list is shared by both the table and the Pager. Before adding the pager I was using
ListDataProvider<String> dataProvider = new ListDataProvider<String>();
ListHandler<String> columnSortHandler = new ListHandler<String>(dataProvider.getList());
The AsyncDataProvider doesn't have the method getList.
To summarize I want the data to be sorted as soon as the column is clicked and not after I move forward/backward with the pager controls.
As per the suggestion I have changed the code for the AsyncDataProvider to
AsyncDataProvider<String> dataProvider = new AsyncDataProvider<String>() {
#Override
protected void onRangeChanged(HasData<String> data) {
int start = data.getVisibleRange().getStart();
int end = start + data.getVisibleRange().getLength();
List<String> subList = hostList.subList(start, end);
// Hooking up sorting
ListHandler<String> columnSortHandler = new ListHandler<String>(hostList);
hostTable.addColumnSortHandler(columnSortHandler);
columnSortHandler.setComparator(lengthColumn, new Comparator<String>(){
#Override
public int compare(String v0, String v1) {
return new Integer(v0.length).compareTo(v1.length);
}
});
updateRowData(start, subList);
}
};
But there is no change in the behavior even after that. Can someone please explain the process. The GWT showcase app seems to have this functionality but how they've done it isn't all that clear.
When using an AsyncDataProvider both pagination and sorting are meant to be done on the server side. You will need an AsyncHandler to go with your AsyncDataProvider:
AsyncHandler columnSortHandler = new AsyncHandler(dataGrid) {
#Override
public void onColumnSort(ColumnSortEvent event) {
#SuppressWarnings("unchecked")
int sortIndex = dataGrid.getColumnIndex((Column<Entry, ?>) event.getColumn());
boolean isAscending = event.isSortAscending();
service.getPage(0, sortIndex, isAscending, new AsyncCallback<List<Entry>>() {
public void onFailure(Throwable caught) {
}
public void onSuccess(List<Entry> result) {
pager.setPage(0);
provider.updateRowData(0, result);
}
});
}
};
dataGrid.addColumnSortHandler(columnSortHandler);
Clicking on a column header will then fire a columnSortEvent. Then you have to get the column clicked. I am overloading my servlet to provide both sorting and pagination, so I pass a -1 for the column index when only pagination is desired.
provider = new AsyncDataProvider<Entry>() {
#Override
protected void onRangeChanged(HasData<Entry> display) {
final int start = display.getVisibleRange().getStart();
service.getPage(start, -1, true, new AsyncCallback<List<Entry>>() {
#Override
public void onFailure(Throwable caught) {
}
#Override
public void onSuccess(List<Entry> result) {
provider.updateRowData(start, result);
}
});
}
};
provider.addDataDisplay(dataGrid);
provider.updateRowCount(0, true);
Then your servlet implementation of getPage performs the sorting and pagination. The whole thing is much easier to follow with separate event handlers.
I think the problem is with the ListHandler initialization. You are passing hostList as a parameter to List Handler and in onRangeChange method you are calling updateRowData with a different list (sublist).
Make sure you use the same list in both the places.
or
Move your ListHander initialization and cellTable.addColumnSortHandler method call to onRangeChange method after updateRowData call.

Can't get progressdialog bar to show no matter how I try

OK, this one is getting frustrating. I've reviewed the posts here regarding progressdialog bars in AsyncTasks and in regular threads, and nothing is working how I want it to.
In the AsyncTask I've done it this way:
#Override
protected void onPreExecute()
{
super.onPreExecute();
// initialize the dialog
progressDialog.setTitle("Please wait...");
progressDialog.setMessage("Downloading team data...");
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(true);
progressDialog.show();
}
#Override
protected Boolean doInBackground(String... parms) {
... stuff
#Override protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
}
When I do the above I get nothing until the doInBackground job finishes. The notes I've read say that using the get() method in the main is blocking the progressbar.
OK. I have to wait for the task to finish anyway before I can continue, so I wrote the same thing without an AsyncTask:
public class LoadTeamData2 {
Context mContext;
String teamName = "";
Boolean result;
String dataload = "";
ProgressDialog progressDialog;
public LoadTeamData2(Context mContext, String team) {
this.mContext = mContext;
teamName = team;
}
public Boolean LoadData () {
ProgressDialog progressDialog = new ProgressDialog(mContext);
progressDialog.setTitle("Please wait...");
progressDialog.setMessage("Downloading team data...");
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(true);
// progressDialog.show();
ProgressDialog.show(mContext, "Title", "Message", true, true);
... more stuff
progressDialog.dismiss();
return true;
}
From the above I get the briefest flash of the progressdialog bar.
I've even taken the progressdialog bar out of the called procedures and put the show() and dismiss() methods on both sides of the call to DoStuff. Still nothing.
At my wit's end here. Any ideas? Thanks!
You can start the ProgressDialog before you start executing the AsyncTask, for example:
gpsProgress = ProgressDialog.show(this, "Searching...", "Getting...", true, true, new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
}
});
(new SampleAsyncTask(this)).execute(param..);
and onPostExecute(Result) you may dismiss the dialog.
Please note, that the DialogBox show isn't in preExecute(), but in the activity which starts the AsyncTask.
The invoke asyncTask.get(XX,XX) blocks the UIThread,
My solution is put the UIlogic in onPostExecute() method, remove asyncTask.get(XX,XX)

GWT new EntityProxy in #OneToOne with another EntityProxy from server

I am just creating a new Proxy:
LayoutExampleRequest r = requestFactory.employeeRequest();
DepartmentProxy d = r.create(DepartmentProxy.class);
r.save(d);
departmentEditor.editProxy(d, r);
Then pass the Proxy and the Request(LayoutExampleRequest ) to my editor
driver.edit(proxy, request);
Until here ! everything works as espected. I can save Department objects with null EmployeeProxy. Now iam getting with a suggest box Proxys of EmployeeProxy from the server.
search = new SuggestBox(new SuggestOracle() {
#Override
public void requestSuggestions(final Request request,final Callback callback) {
System.out.println(request.getQuery());
//ignore less than 3
if(request.getQuery().length() > 3){
requestFactory.employeeRequest().search(request.getQuery()).fire(new Receiver<List<EmployeeProxy>>(){
#Override
public void onSuccess(List<EmployeeProxy> response) {
List<MySuggestion<EmployeeProxy>> suggestions = new ArrayList<MySuggestion<EmployeeProxy>>();
for(EmployeeProxy e:response){
MySuggestion<EmployeeProxy> suggestion = new MySuggestion<EmployeeProxy>();
suggestion.setModel(e,e.getFirstName(),e.getFirstName()+" "+e.getLastName());
suggestions.add(suggestion);
}
callback.onSuggestionsReady(request, new Response(suggestions));
}
});
}
}
});
MySuggestion is a wrapper class to handle the EmployeeProxy.
Now i want to add this EmployeeProxy to my DeparmentProxy since i have a #OneToOne on JPA.
search.addSelectionHandler(new SelectionHandler<SuggestOracle.Suggestion>() {
#Override
public void onSelection(SelectionEvent<Suggestion> event) {
MySuggestion<EmployeeProxy> s = (MySuggestion<EmployeeProxy>)event.getSelectedItem();
proxy.setSupervisor(s.getModel());
}
});
proxy is the EntityProxy for Department (I sent to my editor) driver.edit(proxy, request);
then i fire the driver:
departmentEditor.getDriver().flush().fire(new Receiver<Void>() {
#Override
public void onSuccess(Void response) {
Window.alert("Success");
// refresh the datagrid
Range range = dataGrid.getVisibleRange();
dataGrid.setVisibleRangeAndClearData(range, true); //1st way
// create a new DepartmentProxy to bind to the Editor.
createProxy();
// change button text
updateButton.setText("Save");
}
#Override
public void onConstraintViolation(Set<ConstraintViolation<?>> violations) {
for(ConstraintViolation v :violations){
Window.alert(v.getMessage()+" "+v.getPropertyPath());
}
}
#Override
public void onFailure(ServerFailure error) {
Window.alert(error.getMessage());
}
});
The problem is iam getting ConstraintViolations from the EmployeeProxy, is like the driver atach the EmployeeProxy but with null values.
(Iam validating my Entityes with JSR-330 )
Dont know how to make a relationship with a new Proxy with other taked from the server. in a #OneToOne relationship
Any help would be nice!
Thank you
/* UPDATE */
Something like this but with editor
final LayoutExampleRequest r = requestFactory.employeeRequest();
final DepartmentProxy d = r.create(DepartmentProxy.class);
d.setName("Name");
d.setService(Service.CONTRACT_MANAGMENT);
// get some random employee
requestFactory.employeeRequest().findById(1).fire(new Receiver<EmployeeProxy>() {
#Override
public void onSuccess(EmployeeProxy response) {
d.setSupervisor(response);
r.save(d).fire(new Receiver<DepartmentProxy>() {
#Override
public void onSuccess(DepartmentProxy response) {
Window.alert("Kidding me! why editor cant get it work =p?");
}
});
}
});
The problem was i put on my editor properties of the EmployeeProxy so when a user select the employeproxy would see information about it, so i delete them and then do the same and now works.
Is like GWT when detects properties from another proxy on the editor thinks you will fill it. And the line:
proxy.setSupervisor(s.getModel());
doesn't works.

How to redirect to an anchor in JSF?

Let's say I have this action in a JSF Managed Bean:
public String doSomething() {
FacesContext.getCurrentInstance().getExternalContext().getFlash().put("msg", "Something was done successfully");
return "view?faces-redirect=true";
}
My view has an anchor element with the id msg. I want the url to have this anchor (for accessibility matters), like:
view.jsf#msg
Or whatever is my FacesServlet filter pattern.
return "view#msg?faces-redirect=true"; obviously will not work because JSF (mojarra at least) will try to evaluate view#msg as a view.
So my question is how to make JSF redirect to a URL with #msg in the end.
because JSF (mojarra at least) will try to evaluate view#msg as a view
Oh, that's nasty. It's definitely worth an enhancement request at the JSF/Mojarra boys.
Your best bet is to send the redirect manually with help of ExternalContext#redirect().
public void doSomething() throws IOException {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.getFlash().put("msg", "Something was done successfully");
ec.redirect("view.xhtml#msg");
}
(assuming that FacesServlet is mapped on *.xhtml)
Alternatively, you could conditionally render a piece of JS which does that instead.
<ui:fragment rendered="#{not empty flash.msg}">
<script>window.location.hash = 'msg';</script>
</ui:fragment>
You try to build an illegal URL - the fragment (#) is always the last part of an URL.
return "view?faces-redirect=true#msg" would be the correct URL.
Unfortunately that fragment is stripped by the default NavigationHandler, at least in JSF 2.2.
While the two options of BalusC are working as well, I have a third option to offer. Wrap the NavigationHandler and ViewHandler with a small patch:
public class MyViewHandler extends ViewHandlerWrapper {
public static final String REDIRECT_FRAGMENT_ATTRIBUTE = MyViewHandler.class.getSimpleName() + ".redirect.fragment";
// ... Constructor and getter snipped ...
public String getRedirectURL(final FacesContext context, final String viewId, final Map<String, List<String>> parameters, final boolean includeViewParams) {
final String redirectURL = super.getRedirectURL(context, viewId, removeNulls(parameters), includeViewParams);
final Object fragment = context.getAttributes().get(REDIRECT_FRAGMENT_ATTRIBUTE);
return fragment == null ? redirectURL : redirectURL + fragment;
}
}
public class MyNavigationHandler extends ConfigurableNavigationHandlerWrapper {
// ... Constructor and getter snipped ...
public void handleNavigation(final FacesContext context, final String fromAction, final String outcome) {
super.handleNavigation(context, fromAction,
storeFragment(context, outcome));
}
public void handleNavigation(final FacesContext context, final String fromAction, final String outcome, final String toFlowDocumentId) {
super.handleNavigation(context, fromAction,
storeFragment(context, outcome), toFlowDocumentId);
}
private static String storeFragment(final FacesContext context, final String outcome) {
if (outcome != null) {
final int hash = outcome.lastIndexOf('#');
if (hash >= 0 && hash + 1 < outcome.length() && outcome.charAt(hash + 1) != '{') {
context.getAttributes().put(MyViewHandler.REDIRECT_FRAGMENT_ATTRIBUTE, outcome.substring(hash));
return outcome.substring(0, hash);
}
}
return outcome;
}
}
(I had to create the wrapper for the ViewHandler anyway, because of a fix for JAVASERVERFACES-3154)