Showing posts with label english. Show all posts
Showing posts with label english. Show all posts

Wednesday, July 22, 2009

DivPanel - Creating a new Widget

Create a composite or extend a widget ?

This question has many answers. In the end it all depends on what is your needs.

When you want to make many widgets work together, like the labeltextbox we created before it's better to create a composite. But when you need to create a new behavior for your widget, maybe it's better to extend a base widget.

As example we will extend the simplePanel, that supports only one widget inside it, to support as many widgets as we need.

Unlike the HorizontalPanel and the VerticalPanel, that uses tables to align the widgets inside it, the simplePanel uses a div to wrap its widget.

Here is the code:

public class DivPanel extends SimplePanel {


private WidgetCollection children = new WidgetCollection(this);


public DivPanel() {

super();

}


public Widget getWidget(int index) {

return getChildren().get(index);

}


public int getWidgetCount() {

return getChildren().size();

}


public int getWidgetIndex(Widget child) {

return getChildren().indexOf(child);

}


public Iterator iterator() {

return getChildren().iterator();

}


public boolean remove(int index) {

return remove(getWidget(index));

}


@Override

public boolean remove(Widget w) {

// Validate.

if (w.getParent() != this) {

return false;

}

// Orphan.

orphan(w);


// Physical detach.

Element elem = w.getElement();

DOM.removeChild(DOM.getParent(elem), elem);


// Logical detach.

getChildren().remove(w);

return true;

}

public void add(Widget child){

add(child,getElement());

}


protected void add(Widget child, Element container) {

// Detach new child.

child.removeFromParent();


// Logical attach.

getChildren().add(child);


// Physical attach.

DOM.appendChild(container, child.getElement());


// Adopt.

adopt(child);

}


protected WidgetCollection getChildren() {

return children;

}


}

First and most important we need to rewrite the add method (the simplePanel.add method verify if you already added a widget and throws an exception).
Second we need to create a WidgetCollection to hold all the widgets that will be accessed.
Let's take a look at the add(Container,widget).
If we use the DOM.appendChild method our page will look like we want, but we need also to call the adopt(widget) method, so the events and handlers already associated with the widget still works.

Also when extending an widget it's very important to know what will be your base class.
It could be the widget class, some Panel class, maybe a TextBox... and so on. Choosing the rigth base widget is a key factor.

Note that when extending the widget class you need also to create a DOM element and call the setElement method. This is the method responsable for binding your element to the page.

Here is another example:

public class DockDiv extends DivPanel{


public final static int NORTH = 0;

public final static int SOUTH = 1;

public final static int EAST = 2;

public final static int WEST = 3;

public final static int CENTER = 4;

private Element north = DOM.createDiv();

private Element south = DOM.createDiv();

private Element east = DOM.createDiv();

private Element west = DOM.createDiv();

private Element center = DOM.createDiv();

private Element row = DOM.createDiv();

private Element container = DOM.createDiv();

public DockDiv(){

container.appendChild(north);

container.appendChild(row);

container.appendChild(south);

row.appendChild(west);

row.appendChild(center);

row.appendChild(east);

getElement().appendChild(container);

east.getStyle().setProperty("float", "left");

west.getStyle().setProperty("float", "left");

center.getStyle().setProperty("float", "left");

}

public void add(Widget child){

add(child,center);

}

public void add(Widget child, int pos){

switch (pos) {

case NORTH:

add(child,north);

break;

case SOUTH:

add(child,south);

break;

case EAST:

add(child,east);

break;

case WEST:

add(child, west);

break;

default:

add(child, center);

break;

}

}

public Element getRegion(int region){

switch (region) {

case NORTH:

return north;

case SOUTH:

return south;

case EAST:

return east;

case WEST:

return west;

default:

return center;

}

}

@Override

public void setWidth(String width) {

super.setWidth(width);

north.getStyle().setProperty("width", width);

row.getStyle().setProperty("width", width);

south.getStyle().setProperty("width", width);

}

public void setCenterSize(String west, String center, String east){

if (west != null && !west.equals("")){

this.west.getStyle().setProperty("width", west);

}

if (center != null && !center.equals("")){

this.center.getStyle().setProperty("width", center);

}

if (east != null && !east.equals("")){

this.east.getStyle().setProperty("width", east);

}

}

}

as you see this DockDiv extends the divPanel (working with divs, not tables).
Also there is some DOM Elements and in the add method we decide what element will be the container of the widget. This shows that you may create as many elements you need.

Friday, July 17, 2009

Composites - When you need to group widgets

When developing an user interface it's quite common to need components more complex than the ones that are available to us. By example the textfield widget does not have a label by default and every time we need to put a text field on a form, we also need to put a label with it.
And if we could create a new widget that was composed of a label and a textfield?
That's the main purpose of the Composite class. The Composite is a widget composed of widgets.
You may create a LabelTextBox composite, that has a label and a textfield. This way you don't need anymore to create each time a label and a textbox (and maybe some panel to layout them). Now you just need to create a LabelTextBox.
Also, using the composite helps you to keep your code clean and organized, by encapsulating all the logic and configuration of those widgets inside the composite class.
Here is an example of a composite:

public class LabelTextField extends Composite {


private HorizontalPanel hPanel;

private Label lblField;

private TextBox txtField;

public LabelTextField(Label lbl, TextBox txt) {

lblField = lbl;

txtField = txt;

hPanel = new HorizontalPanel();

hPanel.add(lblField);

hPanel.add(txtField);

initWidget(hPanel);

}

public String getValue(){

return txtField.getText();

}

public String getLabel(){

return lblField.getText();

}

}

One of the rules to use the composite class is that we must associate another widget as the main widget of this composite, through the method initWidget(Widget w).
In this case the main widget is a HorizontalPanel.
Also note that we hided the TextBox and the Label objects from the external access, and only exposed the getText() methods of the objects.

Thursday, July 16, 2009

How to upload the chat application to google AppEngine

In the last post we finished the chat application. Now we will learn how to upload it to google appengine.

First log in to the appEngine site with your google account.
If this is the first time you log in into appengine it will ask to validate your mobile number. (Please, don't forget to put also the country code and the area code. )
After you validate you will be able to create your first application.
Choose an application ID avaliable and the application title hit the save button.

After all you need to do is open your eclipse project. Select the project properties -> Google -> AppEngine and set the id of the application. You may also set the version of the application(the AppEngine keeps track of the versions, and you can make any version the one default). After just click on the appengine icon (its close to the button you hit to create a google project and the button to compile the gwt). Fillin your email and password, and you're done.
That's all you need to create a chat application and publish it to the internet.

Wednesday, July 15, 2009

How to create a simple chat using GWT and AppEngine - Second Part

Now comes the second part of the tutorial to create a basic chat.

In the last part we created the base of the interface and the base of the server.
Now we will create the Persistence Manager.

Create a new package under the server package named persist.

Now create a Class named PMF. Here is the code:

public final class PMF {


private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional");


private PMF(){ }

public static final PersistenceManagerFactory getFactory(){

return pmfInstance;

}

public static final PersistenceManager getManager(){

return getFactory().getPersistenceManager();

}

public static final void saveObject(Object obj){

getManager().makePersistent(obj);

}

}

This class uses the PersistenceManagerFactory to get a PersistenceManager. This manager is responsable for saving, updating and deleting the objects. (Notice that this class is very simple just to explain this example).
Now open the Shout class. We need to set the time that the message was sent.
Add the Date field sentAt to it.

@Persistent

private Date sentAt;

also great the get and set for this field.
Now change the shoutToServer method of the ShoutServiceImpl class. This is the code:

public String shoutToServer(Shout s){

s.setSentAt(new Date());

PMF.saveObject(s);

return s.getMessage();

}

we just set the date to today and save the message.
Now all of our messages been saved. We need now to display them on the browser.
Create a method getAllShouts in the ShoutServiceImpl class. This is the code:

public List getAllShouts() {

Query query = PMF.getManager().newQuery(Shout.class);

query.setOrdering("sentAt ascending");

List resultset = (List)query.execute();

return resultset;

}

Here we get the PersistenceManager and create a new query based on the Shout class.
next we order the results acording to the sentAt field.
This will return all the messages in the server.
Next we need to create a method that will return only messages newer than a date we will pass.
Create a method called get getNewerThan that will receive a Date parameter. This is the code:

public List getNewerThan(Date now) {

PersistenceManager manager = PMF.getManager();

Query q = manager.newQuery(Shout.class);

ArrayList result = new ArrayList();

List resultset = (List)q.execute();

for (Shout a: resultset){

if (a.getSentAt().after(now)){

result.add(a);

}

}

return result;

}

Here we filter the messages to the ones that were sent only after some specific date. We will need this to update the browser with new messages.
Now we need to update the ShoutService and ShoutServiceAsync with this methods too. Here are the codes:
ShoutService:

public List getAllShouts();

public List getNewerThan(Date now);

ShoutServiceAsycn:

void getAllShouts(AsyncCallback> callback);

void getNewerThan(Date now, AsyncCallback> callback);

Now that your interfaces are updated let's change the EntryPoint (The .java).

First create a Date attribute called lastUpdatedAt and a Timer attribute caled t.
Here is the code:

final VerticalPanel messagesPanel = new VerticalPanel();

private Date lastUpdatedAt;

Timer t;

private void checkMessages(){

t.schedule(1000);

}

private void configureTimers(){

t = new Timer(){

@Override

public void run() {

shoutService.getNewerThan(lastUpdatedAt, new AsyncCallback>(){

public void onFailure(Throwable caught) {

checkMessages();

}

public void onSuccess(List result) {

if (result.size() > 0){

for (Shout i: result){

messagesPanel.add(new HTML(i.getMessage()));

}

lastUpdatedAt = result.get(result.size()-1).getSentAt();

}

checkMessages();

};

});

}

};

}

Here we created the Timer and the Date attributes and also changed the scope of the messagesPanel, making it accessible to all methods in the class.
In the checkMessages() method we just start the Timer, and the timer will get all the messages that were sent after the last message received.
Don't forget to remove the messagesPanel from inside the onModuleLoad method.

Finally add this to the end of the onModuleLoad method:

configureTimers();

shoutService.getAllShouts(new AsyncCallback>(){

public void onFailure(Throwable caught) {

checkMessages();

}

public void onSuccess(List result) {

for (Shout i: result){

messagesPanel.add(new HTML(i.getMessage()));

if (i.getSentAt().after(lastUpdatedAt)){

lastUpdatedAt = i.getSentAt();

}

}

checkMessages();

}

});

this will configure the timer object and also get all the messages that were sent before opening the page.
This will also start the Timer, that will loop now forever.

Here we got our chat. Later you may add a name's field to identify who is sending the message if you want.

That's all folks!
Next i will teach how to send this project online to Google AppEngine.

Until there!

Tuesday, July 14, 2009

How to create a simple chat using GWT and AppEngine - First Part

UPDATED - 2009-07-15 - WEB-INF info

Well, today i will start a series of tutorials about making some stuff in gwt.
We will begin transforming the default project the google eclipse plugin generates.

After installing the google plugin for eclipse (you may find here how to install the plugin), let's create a web application project, by clicking the google blue icon in the toolbar

After you choose your Project Name and the initial package of the project

Also select to use both GWT and App Engine sdks.

This will create a simple project with two main packages:
  • Client
  • Server
In the client package will stay all the code GWT will compile to javascript, and in the server package will stay all the code that will run on the app engine server.

Also the plugin already creates a page with a server communication. It has a textbox and a button, and when you click the button it sends the content of the textbox to the server and get a string to show in the page.

We will also add to this page a list with all the messages sent previously and also update it often using ajax.

So these are the steps we will take:
  1. add a new line to the table in the html
  2. Create a Panel and add it to the line created
  3. Create a model class to represent the message sent - We will call this class "Shout"
  4. Create a service to send this shout to the server
  5. implements the client call to the service

First we will create in the html page (it has the name of the project and stay in the war package) a place to put all of the messages.
We will do this by adding a new line to the table:


what we are doing here is creating a new container to put our panel with the messages.


Now we need to edit the java code to add a new panel in this row.
Open the .java (it is the client package) - We will call this file the EntryPoint, as it implements the EntryPoint interface. We will edit the onModuleLoad method.

Add a new final VerticalPanel and call it messagesPanel

public void onModuleLoad() {

final VerticalPanel messagesPanel = new VerticalPanel();

final Button sendButton = new Button("Send");

final TextBox nameField = new TextBox();


now we will add this panel to the row we created in the html :

// Add the nameField and sendButton to the RootPanel

// Use RootPanel.get() to get the entire body element

RootPanel.get("nameFieldContainer").add(nameField);

RootPanel.get("sendButtonContainer").add(sendButton);

RootPanel.get("messagesContainer").add(messagesPanel);


The RootPanel.get(id) method returns the element in the page with the id we passed, so we can add widgets to it.
Now our panel will be placed right under the textbox and the button.
Well now we need to create the Model class tha will represent the message we are sending.
First let's create a new package under the client package, named "model", and inside this package lets create a new Class, called "Shout".
This class must implements the serialize interface.
Also we need to put some JDO information to be able to save it in the database.
This is the code to the Shout class:

@PersistenceCapable(identityType=IdentityType.APPLICATION)

public class Shout implements Serializable{


@PrimaryKey

@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)

private Long id;

@Persistent

private String message;

public Shout(){}

public Shout(String message){

this.message = message;

}


public String getMessage() {

return message;

}


public void setMessage(String message) {

this.message = message;

}


public Long getId() {

return id;

}

}

The @PersistenceCapable annotation mark this class as persistance and each field that shoud be stored must have the @Persistence annotation on it.

Well now that we have our model we must create a service to send it to the server.
First let's create a ShoutService interface in the client package and also a ShoutServiceAsync interface.
In the first we should add a method that will receive a Shout and return a String, this is the code of the ShoutService:

@RemoteServiceRelativePath("shout")

public interface ShoutService extends RemoteService{


String shoutToServer(Shout s);

}

this interface must extends the RemoteService and also must define the path to access this service using the RemoteServiceRelativePath annottation.

In the ShoutServiceAsync we should add a void method that will receive a Shout object and a AsyncCallback object.
This is the code to the ShoutServiceAsync:

public interface ShoutServiceAsync {


void shoutToServer(Shout s, AsyncCallback callback);

}

Now we need to create the server side service.
Let's create the ShoutServiceImpl in the server package.

@SuppressWarnings("serial")

public class ShoutServiceImpl extends RemoteServiceServlet implements ShoutService{


public String shoutToServer(Shout s){

return s.getMessage();

}

}

this class must extend te RemoteServiceServlet and also implements the interface of the Service we created before. Notice that it implements the Service and not the ServiceAsync.
For now our method will just return the message of the Shout object.
Well, now we are almost there.
Let's change now the behavior of the click of the send button.
Get back to the entrypoint class and create a ShoutServiceAsync final variable:

/**

* Create a remote service proxy to talk to the server-side Greeting service.

*/

private final GreetingServiceAsync greetingService = GWT

.create(GreetingService.class);

private final ShoutServiceAsync shoutService = GWT.create(ShoutService.class);


notice that we declare the service using the Asycn interface, but in the GWT method we use the Service class.

Now, look for the method "sendNameToServer". and remove the code inside it, we wont need it anymore.
next put this code:

sendButton.setEnabled(false);

Shout s = new Shout(nameField.getText());

nameField.setText("");

shoutService.shoutToServer(s, new AsyncCallback(){

public void onFailure(Throwable caught) {};

public void onSuccess(String result) {

messagesPanel.add(new HTML(result));

sendButton.setEnabled(true);

};

});

we are just creating a shout using the text in the textfield, reseting the textfield and calling the service method, passing our shout and the asyncCallback.
In the callback we can do something if the method fails, like by connection reset or a exception in the server using the onFailure method.
In the onSuccess method we put our code to treat the answer of the server.
In this case we are just adding the string into a HTML widget inside the panel. As the panel is a VerticalPanel all this widgets will be placed one under the other.

----UPDATED----------

Also you need to edit the web.xml file inside the WEB-INF directory.
You need to configure your servlets (The ShoutServiceImpl).
Just copy and paste the greetServlet and change the servlet-name to shoutServlet and the servlet-class to your.path.to.ShoutServiceImpl . Also copy and paste the servlet-mapping of the greetServlet and change the servlet-name to shoutServlet and the url-pattern to /[module-name]/shout .
Observe that this /shout is the name we put on the service interface annottation: @RemoteServiceRelativePath("shout").

-----UPDATED-----------

In this is it. In the next post we will save this shout in the database and load the shout list when the page is loaded and keeping updating the list while the page is open.

See you there!