Tutorial

version 7.1
feedback: 2step@zentense.com


Welcome to the 2step tutorial. This tutorial will give you basic and intermediate skills with 2step, given that you know, at least, the basics of Java programming language. If you don't know anything about Java, probably you will find 2step hard, because is so similar and well integrated with Java. If you are a very proficient Java programmer, you will find 2step very easy to understand and use (I hope).


1. Tutorial 1: Package and application structure.5.1. Conditional blocks
1.1. 2step package contents6. Sample 5: Accessing database
1.2. Structure of a 2step application6.1. Configuring other databases
2. Tutorial 2: 2step basics6.2. Accessing data: using views and models
2.1. 2step programming units7. Sample 6: Looping views through multiple rows.
2.2. Structure of a 2step script file7.1. Sample 7: Table views and filters
2.3. Sessions8. Sample 8: Paging the results
2.4. Understanding how the main menu works9. Sample 9: Getting data from users.
2.5. Sample 1: Hello world9.1. Form
3. Sample 2: Variables9.2. Check
3.1. Local variables9.3. Inserting values into database
3.2. Session variables10. Sample 10: Alerting the user and navigation stack.
3.3. Request variables11. Sample 11: Parametrized blocks.
4. Sample 3: Expressions and types12. Sample 12: Authentication
5. Sample 4: Guess the secret number!13. Sample 13: Model file (shop demo)




   1. Tutorial 1: Package and application structure.

  First you should install 2step.


1.1. 2step package contents

In the <2STEP_HOME>/lib directory there are several jar files. Some of them are the libraries of 2step itself. These are:
  • 2stepcore.jar: This is the 2step core library. It contains a servlet that can execute 2step applications. This library can only work contained in a servlet engine (as Tomcat, or 2step internal server) and is the main library of the 2step system. In production environments the only thing that you need for executing 2step applications is this library.

  • 2step.jar: This is the 2step tools library. It contains the 2step parser and compiler, the server, and all the tools that can be accessed from command line. This library is executable as an standalone application, but usually it relies on the core library for executing 2step appliations.

  • jetty.jar: This is the Jetty server library. It contains web servler and servlet container that can execute 2step runtime and applications. It is a simplified version of Jetty server version 6.1. Take a look at Jetty home page for more information on jetty.

In addition, 2step can use external libraries for doing some specific things.
  • jCharts: Withouth this library, the graphic charts in the administration area will not be displayed. Moreover, you can use it for rendering your own charts in your web applications. jCharts is a open source project. You can find more information about it at http://sourceforge.net/project/jCharts

  • FOP: FOP stands for Formatting Objects Processor. This library is used for generating dyncamic PDF content from XSL-FO. See FOP home page for more information.

  • Kaptcha: Kaptcha is a library for generating CAPTCHA's (Completely Automated Public Turing test to tell Computers and Humans Apart). See Google Code page for more information.



  • 1.2. Structure of a 2step application

    All 2step application have a name, and are inside an application directory that must have the same name as the application. For instance, the application contained in the tutorial directory has the name tutorial (obviously).

    Inside the application directory there may be several files and directories:

    • 2step sources: They have extension .2s. There must be one that is the main script of the application, which is characterized for having the same name as the application. There application may be divided in so many scripts as needed.

    • java: This directory contains Java source files that are automatically compiled with the application. Of course, you can package Java classes into JAR files and access them from the 2step application, but then you will have to take care of recompile it when you change it.

    • Application properties file: This is the file where properties about the application are stored. This file is created automatically, if it did not exist, first time that application is executed. Usually you will never have to edit it manually (but is perfectly possible because it has human readable format) because contents of this file are managed by the setup in application administration area. For the application tutorial, the properties file is called tutorial.properties.

    • Access control properties file: This file is always called access.properties and is the file where are defined which users have access to each part of the application (if application is not public and needs authentication for being accessed). If this file does not exist, it is created the first time the application is executed. Usually you will never have to edit it manualy, because its contents are managed by the access control setup in the administration area.

    • Internationalization files: These files are only needed in internationalized applications (applications that show texts in several languages) are stored into i18n and are properties files that store texts for a language. For instance english texts will be stored in i18n/messages_en.properties and spanish texts in messages_es.properties.

    • Libraries: JARs stored in directory lib will be loaded and available to this application.

    The next files and directories are automatically generated by 2step compiler or runtime:

    • logs: In this directory log files, and statistics file are stored. If the directory does not exists it is created the first time the application is executed.

    • 2step compiled file: Is a file with the extension .2step and is generated when application is compiled. This file stores names and properties of templates and actions of the application.

    • java/step2: In this directory there are several Java source files: one for each template, and one for all the actions defined in the application. This Java files are the translation of the 2step scripts into Java source code. This files are then compiled into Java bytecodes (.class files), so they are an intermediate step of the compilation process but sometimes can be useful for debuggin purposes.

    • classes: In this directory are found all the Java sources in java directory, that is, the compiled form of the 2step application.



   2. Tutorial 2: 2step basics

  Now let's see 2step in action. Click here for accessing the main menu of the template application.

It seems a static page, but it is dynamic. In this chapter we will use this page for presenting the basic concepts of web application programming in 2step. For generate the main menu page, 3 files are involved: tutorial.2s which is the main (and only) script of tutorial application, shell.html which contains header and footer for all the tutorial pages and main.html which contains the body of the main menu.

But before, some key concepts in 2step will be presented


2.1. 2step programming units

The minimum 2step programming unit that can be accessed from a browser is an action. An action is like a Java method: it has a name which is used for invoking the sequence of statements contained into it. An action is just a sequence of code that when executed, does something.

But actions are not the only 2step programming unit that can be invoked through an URL. There is a second one: templates. Templates can have more than one sequence of statements, and the main characteristic of the template is that each one has an associated template file. While actions are executed, it is said that templates are rendered (you can also say that templates are executed and it would be strictly right, but it is more precisse to say that a template is rendered).

A template is more complex than an action and it is divided into several parts. Templates may contain tags and blocks, the last programming units in 2step. Tags are sequences of code, as actions, but they can only be invoked from the template file, and blocks are subparts of the template file that have special behaviour. None of them can be invoked directly from the browser and both of them have the code in the 2step file, but the presentation resided in the template file.

For instance, a tag must be declared in the 2step file, so it is defined what it does, while the same tag name must be in the template file so it is known where it is invoked. The same tag can be present several times in the same template file, the same as you can invoke several times the same method in Java.

Similarly, a block must be declared in the 2step script, so it is defined how it behaves, and the block bounds (start and end) must be defined in the template file, so that it is known where it begins and where it ends.

When a template is invoked, the template file is read from begin to end (in fact, when 2step application is compiled template file gets embedded into Java code so it is not really read at runtime) and the contents of this file define the output of the template. Each time that a tag or a block is found in the template file (they are recognised because are enclosed between <$ and $>), the associated sequence of code (for the tags), or behaviour (for the blocks) is triggered.


2.2. Structure of a 2step script file

Let's take a look to the tutorial main script file. It begins with the keyword application followed by the name of the application. Then, it comes the global variables declarations, which begins with keyword object. The variables declared here are available to all application actions and templates, and its scope is the entire session (the concept of session will be described in the next point).

Following the declaration of global variables, comes the global onbegin section, which is a list of statements that are executed at the beginning of each session.

And following this, there are all the actions and templates that compose the application. The main action is special, because is the default action that is invoked by default when the requests does not specify any action neither template. But before continuing let's introduce the session concept, which is common to web applications (does not matter which technology you use for develop them).


2.3. Sessions

What is a session? If you are familiar with web applications, you are familiar with sessions. A brief history: HTTP is a stateless protocol. This means that a web server is not able to discriminate which requests came from one client browser, and which from another. It just knows from which IP a request came from, but there may be (and is usual) thousands of users behind the same IP, so it is not possible for a web server to know where a request came from.

This is ok for viewing web pages with static information, because does not matter who you are: each page is the same for everybody. But what happens if you want to keep a shopping cart? A shopping cart can be accessed through the same URL, but will be different for each user: one may have the cart empty, while another may have added several items to it.

With stateless HTTP, it is not possible to maintain the state of a shopping cart for every user. So, how is this solved? This is solved introducing the concept of sessions, so HTTP become stateful, instead of stateless.

Maybe you think: why don't use a javascript + cookie shopping cart? It would work, but it would be executed on client machine, and then you would not be able to know when the shopping cart you are receiving was manipulated, and what about think clients that does not execute javascript or cookies, or they are not activated?

A session is an entity, stored in the server, that keeps the state of the web application for a user through requests. Each time a new user arrives to a stateful web application, a new session is generated in the server and a session identifier is sent to the client.

In further requests to the server, the client sends the session identifier to the server, so the server can get the state of the session, and know, for instance, if there is any item in the shopping cart. It does not matter if two clients come from the same IP, because each one will have their own sessin identifier, and the server will be able to keep different states for each client.

There are two ways of maintaining sessions between requests. First of them is using cookies that are stored in the client browser. Each time the browser makes a request, the cookie with the session identifier is sent to the server.

The second one is called URL rewriting, and involves rewriting every link to the application to include the session identifier. 2step allows to do it easily. It has the advantage that URL rewriting works even when cookies are not allowed, so that the web application has more quality than one that only works with cookies. Think, for instance, on the many mobile devices with Internet access that does not support cookies.


2.4. Understanding how the main menu works

As it has been said before, for generate the main menu page, 3 files are involved: tutorial.2s which is the main (and only) script of tutorial application, shell.html which contains header and footer for all the tutorial pages and main.html which contains the body of the main menu.

Let's take a detailed look to the URL that invokes the main menu:

 
http://localhost:8080/tutorial/webapp
 

The scheme (http) defines the protocol of the request. localhost just says that the server is our local machine but could be something as www.mydomain.com. 8080 is the default port where the 2step server is listening, waiting for requests. tutorial is the URI of the context that contains tutorial application and webapp the URI where the tutorial applicaiton is mapped to.

As you can see, there is not any clue of what action or template will be invoked. In these cases, the main action is invoked. Now let's take a look to the main action. It is pretty simple:

 
entry action main {            
title= "Main menu";
render main;
}
 

Notice the entry keyword at the beginning of the declaration: it means that this action can be used to initiate a new session. If a new session is started through a non entry point, a error page is generated instead of the requested template. This is done this way for avoiding accessing directly internal parts of an application that need for some previous initializations.

Inside the action the value "Main menu" is assigned to the request variable title. Variables will be presented in further chapters. And after this, there is the keyword render, that as its name indicates, is used for rendering a template, in this case, the template main. Notice that an action and a template can have the same name (they don't share the same namespace). These are the contents of the template main:

 
template main render "tplts/shell.html" {
tag body render mainBody;
}
 

This is the declaration of the template main. Notice that this template does not show the entry keyword, so that a session cannot be started invoking directly this template. As this template is invoked from main action, it not needs to be declared as an entry point. Notice also, that between parenthesis, as a String, there is the name of the template file, which in this case is shell.html in tplts subdirectory. Its path is relative to the application directory. If we take a look to this template file, we will see three diferent tags:

The first one is <$=request.param$>. All the tags are enclosed between <$ and $>. Tags as this one, that begin with the equals sign, are expression that are evaluated, and the result is put in the place of the tag. For instance, in this case, the value of the request parameter named title is what will be placed in the place of this tag. If you look some lines above, when we were describing the main action, the you will realize that this value will be "Main menu".

The next tag is <$body$>. This invokes the tag that is called body. When this template will be rendered, at the place of this tag will be the output of the statements of tag body. In this case, the tag body renders another template, mainBody, so that the entire template mainBody will appear in the place of this tag.

Finally there is one last tag: <$=constant.version$>. This simply puts the value of the constant version, that always contains the version of 2step being executed.

Ant the last template involved in the main menu is main.html. This template contains the body of the main page: a menu with links to another chapters in this tutorial. You will notice that in this template there is a tag, <$=2step$>,that is repeated several times (one for each link). As you already know, this tag puts the value of the expression 2step.

2step is a reserved keyword of the 2step language, and is a global variable that takes the value of the servlet URI. For instance, in the case of this tutorial application, 2step value is /tutorial/webapp. In the case that cookies are not allowed, 2step also encodes de session identifier so that session can be kept between requests. That means that if you use the value of 2step for creating the links of an application, it will work with or without cookies because this way your application would implement a technique that is called URL rewriting. If the links are statically hardcoded into the templates, then the application will only work with cookies, and without them session will be new each request.

Until now, you have seen how to invoke the main action from your browser. Now you will see how invoke any action or template: this is done using the action (for actions) or the tplt (for templates) request parameters in the URL. Notice the question mark that separates the end of the URI from the beginning of request parameters (this is common to all web applications, and part of HTTP protocol).


2.5. Sample 1: Hello world

Why are needed two templates for rendering just one page? You could do a page in just one template, but for this tutorial (and for most of the applications) all the pages will have the same header, and maybe the same footer. This way, the header and the footer is kept in one unique template (in this case the template main), and then the body of each page.

Now go to the helloworld sample template. You see that is like the main menu (the same header and footer), but without body, and the caption says "Hello world" instead of main menu. Let's look at the template declaration:

 
entry template helloworld() extends main {
onbegin request.title= "Hello world";
tag body;
}
 

The first line says that this template is an entry point, is called helloworld and it extends template main. That means, that helloworld is a subtemplate of template main, the same as in Java a class may be a subclass of another one. Template inheritance has some similitudes to Java inheritance, but as a class is pretty different from a template, there are also some differences.

When a template extends another one, the subtemplate (the one is extending another one), inherits all tags and blocks from the first one, and them can be overriden in the subtemplate, the same as parent methods can be overriden in subclasses, for doing another things. The template file may be inherited if the subtemplate does not specify its own template file (as the case presented above), but is also possible for a subtemplate to have a different template file, with the same tag and blocks definitions.

For instance, the tag body has been overriden in helloworld, so that, instead of rendering a template, it does not do nothing at all. This way we can reuse a common header and footer in as many templates as we want.

The last thing to be commented about this template, is the onbegin block. If you remember, there was an onbegin when describing application structure, which was executed when a new session was started. In an analog manner, the onbegin statement in a template is executed before the template file is rendered. The onbegin statement is usuallu used for performing template optimizations. In this case, the value "Hello world" is assigned to the request parameter title, so that, when rendered, the caption of the page is precissely "Hello world";



   3. Sample 2: Variables

  Variables are at the heart of any programming language. They are used to store values, and are an abstraction of raw computer memory in assembly language. In 2step there are several kinds of variables, each one with its own scope of action, and lifespan.

2step variables can store any Java object. They are declared in the object sections. If the variable is declared in the global object section, then the variable is global, or local otherwise.

Go to the next URL for seeing the variables sample. You should see a page with the common header and footer, and a body composed of 4 lines. The code that generates these lines is in the body tag:

 
put(var1 +"<br>");        
put(session.var1+ "<br>");
put(var2 + "<br>");
put(request.var);
 



3.1. Local variables

Local variables have the narrowest scope. They are only accessible from within the element where they were declared. They are created when the action or template execution starts, and are destroyed when the execution has finished (so, usually its life is as short as few milliseconds.)

In this sample you can see a local variable named var1 declated in the object section of the template that is initialized with the value "var1 local", and then is printed in the first line of the body tag.


3.2. Session variables

The variables with the broadest scope are session ones (usually referred as global variables). They are created at the beginning of the session, in the onbegin section that is located at the top of the main script fail. Session variables maintain their value between requests, and can be accesed from any action or template and their existence is bound to the session: when the session expires, session variables disappear with it.

Now if we look at the object section, at the beginning of the main script file, we can see two session variables, var1 and var2 declared and initialized to "var1 session" and "var2 session" values respectively.

These values match with the second and third line of the sample. Notice that in this sample already there is a local variable named var1, for showing what happens when a local and a global variable have the same name: then, the local variable is accessed, as you can see in the sample. However, the global variable var1 can also be accessed from this template using the keyword session, as you can see in the second line of this sample.


3.3. Request variables

Finally, we can use request variables which are something between global and local variables. From one side, request variables lifespan is the same as local variables one: a request, from the beginning to the end of it. However, its scope is the same as global variables: any template or action in the applictation.

Request variables are very easily recognized because they are always prefixed with the keyword request and they only can store Strings.

Request variables can be assigned a value from code or directly from a URL (in this sample the value "FOO" is assigned to request variable var) or from a form through GET or POST methods. A request variable that has not been assigned any value always return an empty string.



   4. Sample 3: Expressions and types

  This chapter will talk about 2step expressions and types.

Underlying 2step, there is the Java language. The Java language is a strongly typed language (each variable must have a declared type), while 2step is a loosely typed language (variables must be declared, but without known type). This was done by simplicity: all variables are treated as Java Object and can contain a instance of any class.

2step keeps track, at compilation time, of what is being assigned to each variable, and deduces the right type of the variable. So sometimes type cannot be automatically deduced, and then the variable is treated as a Java Object.

Anyway, you can embed Java expressions into a 2step script if you enclose them between double brackets ({{ and }}), so if you find any problem with types, just put it directly in Java.

2step literals are typed. There are String literals (enclosed in "), Integer literals (a simple number), Double literals (with . or scientific notation), and Boolean literals (true or false).

Now, take a look to the expressions template in the source file. The first two expressions (2+2 and 2+3.1) works as expected, so no more comments about them. The third one ("2"+2) works exactly as in Java, doing a String concatenation.

The fourth one ("2"*1+2) is a bit different. Here, the String literal "2" is multiplied by 1, so that is converted into an Integer, and then 2 is added, resulting in 4. So that, in 2step, you can multiply a String by a number.

Fifth, sixth and seventh ones (3.1+2.23, var1+1.2 and var1+var2) works as expected.

Eighth expression (var1+(var3*1.0)) goes one step further. var3 contains a String that represents a Double. We can convert var3 to a Double multiplying it with 1.0, and the result can be added to var1. If we would add var1 and var3, the result would be the string 12.51.2.

Nineth one (Math.random(10)+1) invokes the method random on the object Math (a session variable that is a instance of com.zentense.step2.util.Math). This method returns a Integer with a random number from 0 to 10-1.

Tenth and eleventh expressions are examples of Boolean expressions.

And finally, the last expressions are examples of operations with request variables. Request variables are always of type String so that, if we add them a number, it is concatenated to the request variable. We can multiply them by a number for getting the numeric value.



   5. Sample 4: Guess the secret number!

  In this chapter we will see a more ellaborated example that uses conditional blocks. Is the guess the secret number game! Go to this URL. You should see a page with the game 'guess the secret number'. The secret number goes from 1 to 100. You can put a number in the field and the computer will tell you if the secret number is greater or lesser.

In this sample we use two global variables for storing state of the game during the session: secretNumber and tries, being their names descriptive enough of their purposes.

The action guessNumberGame initializes the game: sets tries to 0 and assigns a random number to secretNumber. Then the template with the same name (guessNumberGame) is rendered.

This template is formed by a very simple form where the number guess can be introduced, and a button sends the request to the server. Notice that the action of the form is the value of 2step, so that, the servlet URI. Then a hidden field is used to assign the value "guessnumbergame" to the request parameter tplt, so that, when the form is submitted, the same template is invoked again. In further samples forms will be described with more detail.


5.1. Conditional blocks

Let's take a look at guessNumberGameBody template. It increments tries variable on their onbegin. And then there are the definition of several conditional blocks.

A block, as its name points, represents a block of HTML code, that may contain tags and other blocks inside if needed (and the inner blocks can enclose more tags and blocks, and so on). The blocks are always enclosed between two tags (the block bounds: begin and end), being always the first one the name of the block.

There are several kind of blocks in 2step, but now we just will see conditional blocks, that are blocks that are rendered depending on a condition. If the condition is true, block is rendered, and if it is false, block is bypassed and nothing is done.

The first block that we found is called wrong, which ends in the tag named end, and which is rendered only when the request variable number has some value and it is not a valid natural number. If you look at the guessnumber.html template you will see that this block displays an error message.

The second block is isgreater, which ends in the tag end, and which is rendered only when request variable is a natural and it is minor to the value of the secret number. This block displays the message that secret number is greater than value you tried.

The third and fourth blocks, are minor and gotit, are analogous previous ones, but with different conditions. Notice that the last one, gotit also contains conditional blocks for evaluating your performance in the game.



   6. Sample 5: Accessing database

  In this chapter we will see how to access a database from 2step. For that, we will introduce two new important concepts: models and views.

if you look this sample you will look some Jack Torrance's information that is stored in a database. The tutorial is set up for getting data from an HSQL database, whis is a pure Java relational database that can be run embedded in the Java VM. This is useful for, as in this sample, ship applications with an embedded database, without worrying about installing and configuring a database server, setting up the data, and so on...


6.1. Configuring other databases

If you are trying to develop serious web applications, you will be need to use other relational databases: MySQL, Oracle, SQL Server, Postgress... any of them can be used with 2step... in fact, any database that has a JDBC driver available (and there are lots of them).

Of course, you will need to have the database server you have choosen up and running, with the data you want to serve. You also will need the JDBC driver for your database, and you will have to put it in the classpath (there are several forms of doing it that will not be described here), so that 2step will be able to find the driver classes for accessing the database.

The last step is going to the general setup in the administration area and putting the driver class, the JDBC URL for the database (it depends on which database are you trying to access. The JDBC driver documentation will show you how to build the right JDBC URL), and the username and password (if needed) for accessing the database. You can also specify the number of connections to be stablished with the database, which at the same time will be the limit to the maximum number of concurrent requests being served at any given time.

Then restart 2step application and if all is right (database permissions, JDBC URL, etc) you will be accessing the database. If there is any problem connecting to the database the errors will be written in the standard error.


6.2. Accessing data: using views and models

Views and models are two key 2step concepts. Each model represent a table, that is, a collection of rows, where each row is a collection of columns, where one or more of the columns are used to identify each row from the other ones. A view is a subset of the data contained in a model. Usually models represent database tables, and views represent the results of executing SQL queries but models and views that obtain data from other sources different than databases can be done.

A model that gets data from a database is a com.zentense.step2.model.ResultSetModel. All the views are subclasses of com.zentense.step2.view.View but ResultSetModel generate views of type com.zentense.step2.view.ResultSetView.

Not all the views must be created by models. Some of them, as com.zentense.step2.view.RowView and com.zentense.step2.view.VectorRowView can be instantiated and populated with data that is get from any imaginable way. RowViews are views of only one row, while VectorRowViews are Vectors of RowViews, so that they can contain any number of rows.

In this sample we see how to use a model for getting a customer from the database:

 
entry stackable template customerView(cusid) extends main {
onbegin title="Customer #"+cusid+" view";
body render "tplts/customerView.html" {
object cusview= Customer.findById(cusid);
}
}
 


Customer is a session variable (so it is created when session starts), and instantiates the tutorial.Customer class, which is a subclass of ResultSetModel. ReulstSetModel constructor takes 2 arguments: the table name, and the primary key of that table. It can get data through SQL statments from any table of the database, but several methods construct SQL statements and only works if there is a given table. A subset of that methods only work with a single primary key (they will not work with a multiple primary key). Fortunately if one of these methods does not work, just build the query using SQL.

In this sample, once the model has been created, we use findById for getting a view with one row wich is stored in the variable cusview. Now, if you take a look at the file customerView.html you will see how easily the data contained in cusview can be accessed.



   7. Sample 6: Looping views through multiple rows.

  In this sample we will see how to loop a view through multiple rows, and present several rows in screen. For this a new type of block, view blocks, will be presented.

In this case we call to model getAll method, whicn returns a view with all the rows in this model. See t this sample in action. This is the code used for generate this page:

 
entry template customerList extends main { 
onbegin title="Customers list";
body render "tplts/customerList.html" {
object cusview= Customer.getAll();
block customers view(cusview)
}
}
 


Notice that in this case we use Customer as the model, which is a global variable declared at the beginning of the file. Customer is an instance of tutorial.Customer which is a subclass of ResultSetModel. This way we can extend model features in Java, although in this example any of the extended features will be used and a plain ResultSetModel as in previous example would have worked.

Another thing to be highlighted here is the declaration of customers block. This is a view block. The HTML code between tags customers and end will be repeated as many times as rows are contained in view cusview, and each time view cursor is advanced one position (method next is invoked automatically each time a view block is executed). You can get cursor position invoking getOrdinal (first row is number 1). Views are also positioned at the first row, but calling open method positions row before first row.


7.1. Sample 7: Table views and filters

If you look at this sample, you will see almost the same than in the previous sample. The difference lies in the implementation: In this one, a com.zentense.step2.view.TableView is used. A TableView is constructed from another view, and has the ability to render its content as an HTML table.

If you look at the code of this template, you will see that tabview is a TableView constructed from another view containing all the customers, as in previous example. Then, the method renderAll of TableView renders the table (with headers). It is possible to change the look of a table rendered this way: you need to subclass TableView and override all the render methods.

But the other interesting thing in this sample is the use of filters. com.zentense.step2.model.ResultSetFilterModel are ResultSetModel that add column filters, so that only the rows that match filters criteria are shown, and results can also be ordered by a column. In this example filters are created in the file Customer.java:

 
private void addFilters() {                              
getFilters().addLikeFilter("name", "LCASE(cusname)");
getFilters().addStringFilter("dept", "cusdept");
getFilters().addLikeFilter("email", "cusemail");
}
 


Three filters are added to model: name on column cusname, dept on column cusdept and email on column cusemail. There are several filters: first and last are like filters, while second one is a string filter. Like filters are as adding <column> like '%<value>%' to SQL while string filters are as adding <column>='<value>' and you can change filters value with setFilterValue and get filter value with getFilterValue.



   8. Sample 8: Paging the results

  We have seen how to see all the rows in a ResultSetView. But many times there are too many results in a query for displaying all of them at once, and you will want to page the results in several pages.

This is done with setPageBounds method which takes these arguments: first row (being 0 the first one), page size (number of rows that will be shown in one page), and the pager (an instance of com.zentense.step2.util.Pager which is a class that renders link to other pages in the view. Notice that only the rows that will be displayed are retrieved from database, so performance improves a lot when working with very large views.

Look at this sample and you will see the well-known table of customers, but now only the first five are shown, and a pager that allows navigating through the pages is at the bottom of the page. There are also links for inserting and deleting users but these will be used in further samples. The code needed for generating the paged view is:

 
        object cusview= Customer.getAll(runtime);
cusview.setPageBounds(beg*1, 10, pager);
 

First we create a view with all customers, as seen in previous examples, and then we set page bounds. Then we draw the pager with draw method, that takes one argument: the target URL. The pager will add the request parameter beg to the URL with the values pointing to each page.

For understanding better how the links of pages are generated, put the mouse pointer over a pager link. The URL is something like:

 
http://localhost:8080/tutorial/webapp/pagedCustomerList;jsessionid=674791273511?&beg=5
 

The red and orange parts is the part that points to the 2step servlet. The red part is the servlet URI that points to the 2step servlet. The blue part is the name of the 2step action or template being referred by this URL. The orange part is the session information, only present when cookies are not used for storing session identifier and 2step is rewriting URLs, and the green part is the part that defines which row is the firt row to be viewed in the page where this link points to.

Last but not least, the pager look-and-feel can be completely redefined, and is very, very flexible.



   9. Sample 9: Getting data from users.

  One of the most important features of web application is that the information flows bidirectionaly: from server to user (the HTML pages that it returns) and from user to server (the data that user enters into web forms), so that a true dialogue can be stablished.

In this chapter we will see the basics of forms in 2step, how to check the values provided by users and how to insert those values into database. You can see the sample if you go to the previous sample and click on 'New customer' link.

You may be seeing a form with 3 fields. Now take a look to the template file


9.1. Form

It is not required to be familiar with HTML tags for creating forms to understand this chapter, but without any kind of doubt, it will be quite useful. You can use HTML tags for creating input forms in 2step, but there is a better way: a Form object (more precisely, com.zentense.step2.util.Form) which makes things easier, and have some nice features, as filling forms with values (very useful when you submit a form, and some field is wrong and the same page is rendered with the values you put in, so you just have to correct the one is wrong instead of fullfiling the entire form again).

The first thing to do for using the object Form, is to create the fields, as shown in action createCustomerForm:

 
private createCustomerForm {                              
object form= new Form("customer", href:newCustomer());
form.addTextField("cusname", 40, 50);
form.addTextField("cusdept", 16, 16);
form.addTextField("cusemail", 40, 50);
form.addHiddenField("cusid", "");
form.populate(request);
return form;
}
 

The constructor of the object Form takes two arguments: name of the form and where data is submitted when tht submit button is pressed (form action).

The next lines add text fields to the form. addTextField takes three arguments, name of field, visible size of field and max length of data. addHiddenField adds a hidden field to the form and takes two argumetns: field name and field value. Finally, populate method populates fields with values got from request parameters, but populate accepts other objects as arguments depending on where to get values from.

Now that we have a form with values, it has to be rendered in HTML. open method opens the form and takes one argument: the method used for submitting data, it can be GET or POST. Then the method render, that takes as argument the field to be rendered, renders the fields as HTML. And finally close closes the form.


9.2. Check

The data, as have been said, is submitted, and is received by the newcustomer action. There, some checks on the data are performed. This is done using a instance of the class Check.

The first thing to do before using a Check object, is to initialize the Check object, and this is done with the startCheck method, that puts the object to a fresh state. The,n each time that doCheck is invoked, one aspect of input data can be checked. The first argument of doCheck is a boolean expression. If the boolean expression is true, then the check is right, otherwise the check is wrong and the second argument is the error message. Notice that even when a check is not passed, execution of the action continues normally.

In the example, it first checks if the cusname field is empty, in the second checks if cusphone is empty and finally cusemail is checked to be a valid email address.

Now if we return to the template file we can see that the error tag prints the error message from the Check object. The getError method returns the first error detected during the checkings. Is also possible to print all the checking errors at the same time using getErrorsView, which return a VectorRowView that can be looped using a loop block to print all the errors found.


9.3. Inserting values into database

Notice also that when all the checks are passed, a new customer is inserted into the database. There are several ways to insert rows into database.

The first one, is the most obvious: constructing the insert SQL statement that insert the requiered data into database. This is performed by insertClassicMethod and updateClassicMethod on object Customer.

The second one uses prepared statements. Prepared statements are SQL statements that are precompiled and can be executed multiple times. Prepared statements are theorically faster than normal SQL statements but depending on which database and JDBC driver you use, this may be false so do not rely on theory for this matter. Insert and update with prepared statements is performed by insertPreparedMethod and updatePreparedMethod.

Third method uses updatable views. This is most sophisticated method of inserting and updating rows into database so not all the JDBC drivers and databases will support it. By instance, HSQL database shipped with 2step does not support updatable views so you will need other database server, as MySQL, for using them. Insert and update with updatable views is performed by insertUpdatableMethod and updateUpdatableMethod. In these methods fillRowWithParams method is used, which takes the request parameters and puts them into the row (in this case cusname, cusphone and cusemail). .



   10. Sample 10: Alerting the user and navigation stack.

  If your web application can do some delicate operations as delete rows from a database, you may want to alert the user that is going to do some unrevocable operation. But making an alert page, for each time you want to ask something to the user can be very repetitive. One good option is to use Javascript alerts, but we will do it in a different way for showing some 2step features.

In this chapter we will see how to program a general alert page for easily asking anything to the user. For this, go to the sample 8, and try to delete a customer. You may get a message alerting you that you are going to delete a message.

Look the source of delcustomer action. It begins with a condition. If answer request variable is different than yes, an alert page is presented, and otherwise it means that answer to alert page is affirmative, and the customer whose id is in request parameter cusid, is erased from the database.

The line url.save(request, "answer=yes") saves current URL in the object url. url is a global object that contains a instance of com.zentense.step2.util.Location and does some operations on URLs and navigation. The second argument passed to the save method is added to the URL saved, so that what it is saved is the current URL plus the request argument answer with the value yes, which is precissely the condition that is checked in the action before deciding to present the alert screen or delete the row.

So, when you invoke the delcustomer action from a link, the request parameter answer is undefined, and the alert template is presented. Take a look at that template. It is pretty simple. If just presents a message that is in the request variable question. Then two links: one for answering yes to the question (accepting the deletion) and another one for cancelling it.

If you accept the deletion, then the action restore is executed. The action restore does an url.restore(). The restore method is the antagonist of the save: it restores the URL previously saved. The saved URL invokes the delcustomer action, but this time, with the answer parameter request set to yes, so that, this time, instead of rendering the alert page, the customer will be deleted and pagedcustomer template will be rendered.

The other link invokes action refresh. Refresh does a forwarding to the current URL in the navigation stack. The navigation stack is stored in the object Location and it stores some of the URLs that have been visited through this session. Which URLs are stored in the navigation stack? All the 2step actions and templates with the modifier stackable, or when Location.save method is called. , and sets forward request parameter to true. The back action executes url.back(), which makes the previous page to be rendered (as if back button would have been pressed in the client browser). In the url object the navigation of a session is tracked automatically, so that each time a request is done, the URL is stored. This is done for each request, except if a request assigns any value to the parameter forward.

Let's see this in detail. This is the sequence of invoked URLs for a session (from older to newer).

 
http://localhost:8080/tutorial/webapp/                       
http://localhost:8080/tutorial/webapp/pagedCustomerList?beg=0
http://localhost:8080/tutorial/webapp/delcustomer?cusid=4
http://localhost:8080/tutorial/webapp/url.refresh?seq=2
 

URLs in bold face (the first two) are the ones that are marked as stackable in the code, so they are stored in the navigation stack. The other two are not stored there. The first one goes to the main menu of tutorial, then Paging results link is pressed, then the deletion link on fourth row, and finally the No link in the alert window. Location.refresh method does a forward to the most recent URL in the navigation stack, that is the one that goes to the pagedCustomerList template, so the list of customers is refreshed and shown again.

The seq parameter in the last URL is automatically generated by 2step and it is used for going to the right URL even when back and forwards buttons are used in the client browser. Those buttons run completely on the client, and the server is unaware of their use, so this way 2step can know which URL on the navigation stack is being referred.

This may seem tricky at first, and it is. It is quite important to tag as stackable the right templates and actions and use refresh and back methods of class Location in the right way for keeping a coherent and comprehensive navigation through the application. As a rule of thumb, actions that do persistent changes, as delcustomer may not be stored in navigation window, because recalling them should lead to undesired effects. Templates that present information but don't change anything are perfect candidates to be stored in the navigation store.



   11. Sample 11: Parametrized blocks.

  Up to now, we have seen two types of blocks: conditional and view. In this sample we will see the third type: the parametrized blocks, that are blocks that accept parameters.

Parametrized blocks, as their name suggests, are blocks that accept parameters. The parameters are like local variables that can be used only inside the block.

This example defines a parametrized block (box), with two parameters (color, which is the background color), and number of the box, which is displayed in the title of the box, and is the number of asteriscs printed inside the box. The tag ticks (inside the block box) is responsible of printing the asterisks.

Now look at the template file. You will notice that there are three tags that issue a render statement. Statements can also be embedded in tags. Tags that contain statements are recognized because they finish with semicolon (;). Of course, you can put a identifier for this tag, and put the render statement in the source file.

Maybe is not the most useful of the samples in this tutorial...



   12. Sample 12: Authentication

  Until now, we have seen public web applications that can be accessed by anybody. But sometimes we would like to restrict access to parts of the web application, or the entire web application at all. 2step offers a complete access control solution that allows to define who have access to any action or template.

Go to this sample and you will see a login page. You can access with login guest and password guest. Now look at the sample code, and notice that the requested template, authenticated shows the entry modifier, but another new one: auth, which makes a template (or an action) authenticated. When a resource is authenticated, it is not shown until user has been authenticated, and user has access to resources.

Global variable user stores the user authenticated in a session. By default it contains the value nobody which means that session has not been authenticated. When an authenticated resource is accessed, and session is not authenticated, then the login template is shown instead of the requested resource, for authenticate user, and the original request URL is saved in the url object (you have seen this in the 10th sample).

The login template is responsible for requesting authentication to user. It usually requests a login name and a password, but other authentication methods may be used in 2step. Then this template must send information entered by the user to an action able to check if authentication information given by the user is right, and act in consequence.

In this sample, login template submits information to login action. The login action uses checkLogin method of access, for knowing if given user and login are right. You can create users and change passwords in access control of administration area. In the access control, you can also define rules for allowing or denying access to resources (templates and actions) to users or groups.

As has been said, the login action checks user login and password, and if they are right, the given login is assigned to user global variable, so that session is now authenticated and login page will no longer shown when requesting an authenticated resource. And then, restore action is called, which goes to the authenticated resource originally requested. If login and / or password are wrong, an error is assigned to error request parameter, and login page is rendered again for getting a new login / password pair.



   13. Sample 13: Model file (shop demo)

  The demo shop is the most complete sample. It presents a very simple e-commerce shop, with its cart,categories, and products with different skus. Is a good way to see a real application in action, although quite simple.

The most interesting part of this tutorial, from the learning point of view, is the declaration of models in 2step language: models.2s. model keyword is used to describe the tables in the database. In this sample there are 3 different tables: Category, Article and Sku that refer to database tables category, article and sku respectively.

Models also allows to enumerate the columns in table with its type. This serves several purposes: allows to know the type of a column at compile time, allows to show warnings when is accessed a column that is not listed in the model (for example if it is mistyped), and allows to identify primary key columns (marked with an asterisk) as foreign key relations.

Each model generates a com.zentense.step2.model.AutoModel, which in turn is a subclass of com.zentense.step2.model.ResultSetModel, that can be accessed in the application as a session variable with the same name as the model (for instance, Article). The code that initializes model is generated automatically at runtime, and model is automatically instantiates when session begins.

The other key concept with model files is finders. A finder is a query to a model that usually has some parameters.

Generated on Tue 14 16:01:10 CEST
  
2step 7.1.46
© 2008 Zentense S.L.