Uber…what now?
Disclaimer:
Uberfire has a steep learning curve and I’m still climbing that hill. The information in these blogs is my understanding of the software and may or may not be correct. I’m asking my readers to help me out and correct errors or misunderstandings here – thank you š So with that said, please take heed of my disclaimer*.
Building on UF Tasks
If you haven’t realized it by now, Uberfire isĀ hugeĀ as far as application frameworks go, and it tries very hard to hide many of the complexities of web app development.
While the original UFTasks tutorial does a good job of presenting some of the major UI framework concepts, there are many aspects of Uberfire which, in my mind at least, were still a mystery and left me wanting more. Some of the unanswered questions that I was thinking about while working through the tutorial were:
- How does the VFS work, and what are my options for using the clientĀ andĀ server side file system? I would have liked to be able to persist the Project/Folder/Task hierarchy that was developed by the UFTasks tutorial. I assume that since the tutorial web app requires a user login, how about a personal task list for each user? How do I get the currently logged-in user’s information?
- What is this CDI thing you speak of? Even though I’ve been a java developer for years (I come from a C++ background) I have never really dug very deeply into CDI nor understood all of its complexities. Uberfire depends very heavily on using CDI for intra-app communication, so I was going to have to study up on it.
- Views, shmiews…what I really want is an Editor. Editors are a big part of the Uberfire user experience, but what exactly is a @WorkbenchEditor and how is it different from a @WorkbenchPartView?
Rethinking the Model
The first thing that struck me about the UFTasks tutorial was that the Model part of the MVP pattern was scattered through some of the Presenter and View bits. This was a little infuriating because of, well… MVP! The Model should be contained in Model classes, not View or controller classes.
Also, if I was going to persist the model, I would need the ability to quickly and easily navigate it and gather up all the related bits for serialization. So without further ado, here are my model classes:
TreeNode.java
package org.uberfire.shared.model; import java.util.ArrayList; import java.util.List; public abstract class TreeNode< PARENT extends TreeNode, CHILD extends TreeNode > { private PARENT parent; private List< CHILD > children; public TreeNode() { parent = null; } abstract public String getName(); public PARENT getParent() { return (PARENT) parent; } protected void setParent(PARENT parent) { this.parent = parent; } public List< CHILD > getChildren() { if (children == null) children = new ArrayList< CHILD >(); return children; } public void addChild(CHILD child) { getChildren().add(child); child.setParent(this); } public void removeChild(TreeNode child) { getChildren().remove(child); child.setParent(null); } }
Task.java
package org.uberfire.shared.model; public class Task extends TreeNode< Folder, TreeNode > { private String name; private boolean done; public Task(String name) { this.name = name; this.done = false; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isDone() { return done; } public void setDone(boolean done) { this.done = done; } }
Folder.java
package org.uberfire.shared.model; public class Folder extends TreeNode< Folder, Task > { private final String name; public Folder(String name) { this.name = name; } public String getName() { return name; } }
Project.java
package org.uberfire.shared.model; public class Project extends TreeNode< Project, Folder > { private final String name; private boolean selected; public Project(String name) { // this is the root of the tree so it has no parent this.name = name; this.selected = false; } public String getName() { return name; } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { this.selected = selected; } public int countDoneTasks() { int doneTasks = 0; for (Folder folder : getChildren()) { for (Task task : folder.getChildren()) { if (task.isDone()) { ++doneTasks; } } } return doneTasks; } public int countTotalTasks() { int totalTasks = 0; for (Folder folder : getChildren()) { totalTasks += folder.getChildren().size(); } return totalTasks; } }
TasksRoot.java
package org.uberfire.shared.model; import java.util.ArrayList; import java.util.List; public class TasksRoot { private List< Project > projects = new ArrayList< Project >(); public List< Project > getProjects() { return projects; } }
No surprises here. The only things worth mentioning are:
- TreeNode – a generic tree node class which takes PARENT and CHILD type parameters. Note that Project does not have a parent and Task does not have any children and we could have written TreeRootNode, TreeInternalNode and TreeLeafNode classes, but…meh.
- TasksRoot – a root object to contain the list of Projects. This will become important during serialization. The instance of this thing is also made available to other beans by way of theĀ @Produces CDI annotation. More about this in Part 2.
- Project – this includes two convenience functions for counting the total number of tasks and the number of completed tasks. We’ll use this in the Dashboard later on.
*Ā This product is meant for educational purposes only. Any resemblance to real persons, living or dead is purely coincidental. Void where prohibited. Some assembly required. List each check separately by bank number. Batteries not included. Contents may settle during shipment. Use only as directed. No other warranty expressed or implied. Do not use while operating a motor vehicle or heavy equipment. Postage will be paid by addressee. Subject to CAB approval. This is not an offer to sell securities. Apply only to affected area. May be too intense for some viewers. Do not stamp. Use other side for additional listings. For recreational use only. Do not disturb. All models over 18 years of age. If condition persists, consult your physician. No user-serviceable parts inside. Freshest if eaten before date on carton. Subject to change without notice. Times approximate. Simulated picture. No postage necessary if mailed in the United States. Please remain seated until the ride has come to a complete stop. Breaking seal constitutes acceptance of agreement. For off-road use only. As seen on TV. One size fits all. Many suitcases look alike. Contains a substantial amount of non-tobacco ingredients. Colors may, in time, fade. We have sent the forms which seem right for you. Slippery when wet. For office use only. Not affiliated with the American Red Cross. Drop in any mailbox. Edited for television. Keep cool; process promptly. Post office will not deliver without postage. List was current at time of printing. Return to sender, no forwarding order on file, unable to forward. Not responsible for direct, indirect, incidental or consequential damages resulting from any defect, error or failure to perform. At participating locations only. Not the Beatles. Penalty for private use. See label for sequence. Substantial penalty for early withdrawal. Do not write below this line. Falling rock. Lost ticket pays maximum rate. Nap was here. Your canceled check is your receipt. Add toner. Place stamp here. Avoid contact with skin. Sanitized for your protection. Be sure each item is properly endorsed. Sign here without admitting guilt. Slightly higher west of the Mississippi. Employees and their families are not eligible. Beware of dog. Contestants have been briefed on some questions before the show. Limited time offer, call now to ensure prompt delivery. You must be present to win. No passes accepted for this engagement. No purchase necessary. Processed at location stamped in code at top of carton. Shading within a garment may occur. Use only in a well-ventilated area. Keep away from fire or flames. Replace with same type. Approved for veterans. Booths for two or more. Check here if tax deductible. Some equipment shown is optional. Price does not include taxes. No Canadian coins. Not recommended for children. Prerecorded for this time zone. Reproduction strictly prohibited. No solicitors. No alcohol, dogs or horses. No anchovies unless otherwise specified. Restaurant package, not for resale. List at least two alternate dates. First pull up, then pull down. Call toll free number before digging. Driver does not carry cash. Some of the trademarks mentioned in this product appear for identification purposes only. Objects in mirror may be closer than they appear. Record additional transactions on back of previous stub. Unix is a registered trademark of AT&T. Do not fold, spindle or mutilate. No transfers issued until the bus comes to a complete stop. Package sold by weight, not volume. Your mileage may vary. If the flow controls supplied are not installed, this unit will not operate properly. Keep out of reach of children. When this set is used with other equipment, if the picture is not stable or the buzz sound is heard, try to change the mutual position of relevant equipment or take enough distance between them. This unit not labeled for retail sale. Phenylketonurics: contains phenylalanine. Close cover before striking. Mind the gap. No merchantability expressed or implied. Parental discretion is advised. Sold as a novelty item only. Although robust enough for general use, adventures into the esoteric periphery may reveal unexpected quirks. Not available in stores. May cause abdominal cramping and loose stools. Vitamins A, D, E, and K have been added. Not designed or intended for use in on-line control of aircraft, air traffic, aircraft navigation or aircraft communications; or in the design, construction, operation or maintenance of any nuclear facility. Container may explode if heated. May contain traces of various nuts and seeds.