How to implement a Lazy List using SmartGWT and SQL - java

I was trying all of yesterday to try and integrate a SQL Database with SmartGWT for a lazy list but I just couldn't figure out how to implement it. (JavaDoc, and example of a lazy list)
What I want to do is create a list of a bunch of "sites" all over the world. The problem is there will probably be about a million of them, so I'm trying to load as few as possible at a time. Each site in my DB has an address, so I'm trying to sort them in the tree structure like (Country->State->City->Sites). Every time you go down a level there will be a query to the DB asking for all of the next level (Whether that be all the cities that have sites in the state chosen, or what ever).
Any help is greatly appreciated.
ALSO:
In the example linked the folders and leafs are the type of element, is there a way to keep folders, folders, and then leafs a separate type of object?

After a while I finally got it. I ended up creating my own RPC that would serve up an array of strings that would represent the names of all the TreeNodes for the next level.
So entry point would be:
private NodeServiceAsync nodesRpc; //The RPC that grabs more nodes
private Tree data; //The data structure to hold all of the nodes
private ColumnTree list; //The GUI element that is shown on in the browser
public void onModuleLoad() {
nodesRpc = (NodeServiceAsync) GWT.create(NodeService.class);
data = new Tree();
list = new ColumnTree;
list.setAutoFetchData(true);
list.setLoadDataOnDemand(true);
list.addNodeSelectedHandler(new NodeSelectedHandler () {
public void onNodeSelected(NodeSelectedEvent event) {
if(/*Node is folder and hasn't been opened before*/) {
//Get More Nodes
AsyncCallback<String[]> callback = new NodeGetter<String[]>();
nodesRpc.getData(event.getNode(), callback);
}
else if(/*Node is not a folder (at the end) */) {
//Do something else
}
}
});
list.setData(data); //Make the GUI Element Represent The Data Structure
RootPanel.get().add(list); //Add to screen
}
The serverlet on the server side creates the query, executes, then translates the ResultSet into an array of Strings and passes that back. All the callback has to do, back on the client side, is translate that array into an array of TreeNodes and attach them to the original Node that was clicked on. Finally after all of this the GUI element is redrawn with the new nodes.
I was surprised that there was very little down time between node loads (less then 1 sec) even when sometimes a hundred or so nodes where being queried then displayed.

Note there is also a Pro version of the product which includes SQL connectivity like this out of the box (for Java server platforms). Showcase here:
http://www.smartclient.com/smartgwtee/showcase/
The SQL connector in the Pro product includes load on demand / data paging, search, and all 4 CRUD operations, as well as DataSource Wizards that can generate a working SQL DataSource for an existing database table if you just enter JDBC settings.
Note the Pro product doesn't require SQL, that's just one of the things it can connect to.

Related

Play Framework - How to build a web page with multiple MySQL queries

I've created my first Play Framework Website with Java using the official documentation. It has a single page where I display a list of items that can be filtered or modified.
I have a Controller class with a method:
public CompletionStage<Result> feedpostslist(String domain, String date, String state, int page, int resnum, String search) {
return feedRepository.getArticleList(domain, date, state, page, resnum, search).thenApplyAsync(articles -> {
FeedArticle[] list = new FeedArticle[articles.size()];
articles.toArray(list);
return ok(views.html.feedpostslist.render(list));
}, ec.current());
}
This method does a query to the DB (through feedRepository) and then display the result using the view feedpostslist.
Everything is fine but now I need to get other data from the DB to be used in the same web page (so multiple queries). How do I do this in Play Framework? I don't understand what is the best way to do that.
Should I do multiple DB request inside the method showed before (through feedRepository) and then pass all these informations to my view? I don't want to do a mess or even something too heavy to handle.
If the second query doesn't depend on the first one you can run them in parallel using combineAsync. This is a good example on how to do that:
https://github.com/playframework/play-samples/blob/2.8.x/play-java-ebean-example/app/controllers/HomeController.java#L85
If the second query depends on results on the first then there's nothing you can do but to wait for the first one to complete and run the second one.

IBM Domino Java - optimize searching on $(Rooms) view for specific internet address

I'm fairly new to IBM Domino. I have an XPages Java application that works as an extended API for the standard Rooms & Resoruce management database (allows me to manipulate appointments easily and offers more functionality than the standard API, such as modifying appointments of other people).
When I want to get a list of all appointments for a specific room, I pass the internet (mail) address of the room to my API, and in the Java app I iterate over the $(Rooms) view in names.nsf looking for documents until I find one with the InternetAddress item I'm looking for. I then get some other interesting information from the document such as the auto processing settings.
It works, but I'm pretty sure it's not efficient at all and I'm querying over 50 rooms, getting appointments for all of them takes about 18 seconds, and I'm not really satisfied with this result.
How could I optimize this? I'm pretty sure I can do some simple search on the view that will do everything under the hood in a more optimized way, but I haven't got enough experience to make it work.
My current code:
Session session = ExtLibUtil.getCurrentSession();
Database names = session.getDatabase(session.getServerName(), "names.nsf");
View vw = names.getView("($Rooms)");
lotus.domino.ViewNavigator nav = vw.createViewNav();
ViewEntry nextEntry = nav.getFirst();
Document docRoom = null;
while (nextEntry != null) {
ViewEntry processEntry = nextEntry;
nextEntry = nav.getNext();
if (processEntry.isDocument() && processEntry.isValid()) {
docRoom = processEntry.getDocument();
// found
if(docRoom.getItemValueString("InternetAddress").equals(mail)) {
processEntry.recycle();
break;
} else { // not found
processEntry.recycle();
docRoom.recycle();
docRoom = null;
}
} else processEntry.recycle();
}
if(docRoom == null)
return null;
nav.recycle();
String fileName = docRoom.getItemValueString("MailFile");
String fileServer = docRoom.getItemValueString("MailServer");
String fullName = docRoom.getItemValueString("FullName");
You're running 50 times over all available view entries. Because of that it takes too much time. Time complexity of your approach is O(N*M).
There's more efficient way to get a document from view by key.
Document doc = view.getDocumentByKey(yourKeyValue);
It runs way more faster (O(LogN) time complexity), but it requires that the view should contain the first sorted column with the yourKeyValue values. In our case, we should have a view that has InternetAddress as the first sorted column.
Unfortunately ($Rooms) view does not apply to this rule. The first column of this view contains different data, than we need.
I would create a new view in the names.nsf for the task.
Let's name it (RoomsByInternetAddress) and put the following formula as selection formula for the created view:
SELECT ((Type = "Database") &(ResourceFlag="1") & (ResourceType="1") & (AutoProcessType != "D")) & (Form="Resource":"Database") & #IsUnavailable($Conflict)
It is the actual formula for ($Rooms) view.
Make the first column of the view sorted and set the first column value to the InternetAddress field.
Then save it and close it. As it is closed, in the view properties (in the designer view list), enable "Prohibit design refresh or modify". It will protect the new created view from removing upon refreshing from the original server names.nsf template.
After that in your code you do not need to walk over all entries.
View vw = names.getView("(RoomsByInternetAddress)");
String InternetAddressIWantToFind = "someroom#company.com";
Document foundDoc = vw.getDocumentByKey(InternetAddressIWantToFind, true);
if (foundDoc != null) {
// you've found the document by InternetAddress
}
Update
For cases when you are not authorized to change server's names.nsf there is another option.
IBM Domino Java API Database class supports search() method.
Here's info about search formula syntax: https://stackoverflow.com/a/9770367/12576990

View.getEntryCount() returns 1500, but can't get any document by key?

I'm writing a simple standalone Java class that uses Lotus Domino's NCSO JAR for remote-connecting to a Domino server.
I'm establishing a session, getting access to a database and then to a view:
Session session = NotesFactory.createSession("host", "user", "password");
Database db = session.getDatabase(null, "MyDB.nsf");
View view = db.getView("MyView");
Now, I'm printing the number of entries in the view:
int count = view.getEntryCount();
I get a nonzero number (let's say 1500).
However, I can't seem to load any document by key. For example, for any letter in the alphabet, I'm getting an empty document collection with this call:
System.err.println(view.getAllDocumentsByKey(letter, false));
When I try to load a document by key, when I know that the key exists in the view, I get null.
Document document = view.getDocumentByKey("DocKey"); // Equals null even though
// I know that 'DocKey' is
// the key of an existing
// document within the view.
The very same code is said to be working (although I didn't check it) when using local Notes calls (using Notes.jar).
What am I missing?
EDIT
I just noticed that session.getNotesVersion() returns version 8.5.2. The NCSO.jar file that I'm currently using doesn't appear to have a few methods that were added with Notes 8. Therefore, there is a possibility that the NCSO.jar file I use belongs to an earlier version of Notes than the one I'm trying to communicate with. Could that be the reason?
If the same code is working locally, then that should rule out the possibility that the first column of the view isn't sorted. Assuming that, then the most likely issue is that the documents are protected by ReaderNames fields and the identity that you are using for authenticating your session does not have access to the documents.
Assuming I understand you right, you want to get all documents where the first (lookup) column of the view contains anything that starts with a specific letter?
E.g. you send "A" to veiw.getAllDocumentsByKey() and expect a collection that contains "Apple", "Alpha", "Amoeba" and "Apricot" to be returned?
I would modify the column in the view to only hold the first letter:
#Left(MyField;1)
Then it would be easy to perform the lookup and see if you get the correct result.

How to delete an item from a collection in the DOM, GWT

I am newbie on GWT. I am writing the code of a page which asks a JSON object that contains two collections and I use one of these collection to fill a Flextable.
This is my JSON Object:
public class Users extends JavaScriptObject {
protected Users() {
}
public final native JsArray<Link> getLinks() /*-{
return this.links;
}-*/;
public final native JsArray<User> getCollection() /*-{
return this.collection;
}-*/;
}
In particular I am using the second collection (called collection) to fill a Flextable. But my problem is that when I delete one row from the table, even if I send a request with an http delete method to the server (and the server delete that item successfully), when I try to refresh the table GWT does not generate the GET request to the server, (even if it is written in the code) and Users Object is the same as before with the deleted item also.
I have tried to delete this item from collection using this method:
public static native void remove(JsArray<?> arr, int index, int count) /*-{
arr.splice(index, count);
}-*/;
....
remove(users.getCollection(), index, users.getCollection().length());
And I also tried this other technique:
users.getCollection().set(index, null);
But in both cases, I do not get the expected result, when I refresh the table I find the deleted items again.
I think that I am not managing the DOM properly, Do you have any suggestions? Any Idea? I am sure it is simple problem to solve for an expert.
EDIT:
The user can refresh the data in the table clicking a button, the handler of this event will perform a request to a server, but this request is sent on the first click only.
Basically there are two alternatives, the first is to set the header Cache Control to no-cache in the server side, while if you cannot modify the code in the server side, for example in the case of legacy applications, you can attach a random number in your request in a parameter. A request will have a uri with an additional parameter which is a random generated number, http:\\mydomain.com\something?random=12345. Two different requests will have different numbers and at the second request the response of the first request, which has been cached it will be ignored. It is not a smart practice, but it works.
For getting data like this better use HTTP POST request, because GET requests are being cached (by GWT, proxy servers...).
Also make sure your server code for deleting items is doing right job, and manually check server response in firebug or in chrome console to see if your response contains deleted records.
Please post code you use to fill flex table, are you clearing it before populating with data again ?
EDIT:
see link.
If you can use celltable http://gwt.googleusercontent.com/samples/Showcase/Showcase.html#!CwCellTable (because it holds List of elements it display underneath) or use some collection (ArrayList) to hold data that your FlexTable is displaying. Then you can easily modify that collection and redraw your flex table from it.

Creating a free form BIRT report using Scripted Datasource

I am trying to create a free form BIRT report. The report is not consisted to rows which have the same columnNames in each row.
Instead, it is a free form report, which will be of the following form.
"Name: {FirstName} {LastName} Addess : {Address}
Phone# {Phone#}
....
....
"
I am using a scripted datasource, which essentially returns the Map containing the name value pairs of {FirstName, LastName, Address, Phone, and other fields}..
But I am not sure how to set the variables and how do I get the FirstName, LastName etc.
Should I try to use dynamic text.
I don't know of any way in which BIRT can handle non row related data.
Here's my open script of the dataset.
open:
util = new Packages.test.ReportsUtil();
reportsVO = util.getReportVO("ABC");
in fetch:
if(currentrow < totalrows) {
dataSetRow["FirstName"] = reportsVO.getPropValue("identity.FirstName");
dataSetRow["LastName"] = reportsVO.getPropValue("identity.LastName");
currentrow++;
} else {
return (false);
}
But I am not sure of how do I get access to the FirstName and LastName in the main layout page.
Thank you
The goal of a scripted data source is to allow you to leverage the logic inherent in your data model and benefit from any business rules that manipulate that data. In the end it still wants the data to be formed into a rather traditional row-based set.
You mention dynamic text and I think this would be a great use for the Java-based event handlers. You can use the logic in the Java object you had bound to the scripted data source to instead tie to an event in the life cycle of the report and get your non-relational data that way.
You can even call your Java object directly from a JavaScript event handler (much easier to plug into via the IDE) using the JS "Packages" construct.
There are many examples to help you get this done at the BIRT Exchange.
I did something similar (BIRT 3.7) but I used row["colName"] instead of dataSetRow["colName"] and that seems to work. I have my data in a list, and then each list item is a grid. I set the data binding in the list to the data set. The grid is able to see the value as row["colName"].

Categories