Misplaced Pages

PREST: Difference between revisions

Article snapshot taken from[REDACTED] with creative commons attribution-sharealike license. Give it a read and then ask your questions in the chat. We can research this topic together.
Browse history interactively← Previous editContent deleted Content addedVisualWikitext
Revision as of 01:31, 25 August 2012 editBearcat (talk | contribs)Autopatrolled, Administrators1,570,915 edits categorization/tagging using AWB← Previous edit Latest revision as of 23:23, 3 May 2017 edit undoAnomieBOT (talk | contribs)Bots6,589,808 editsm Substing templates: {{redr}}. See User:AnomieBOT/docs/TemplateSubster for info. 
(40 intermediate revisions by 9 users not shown)
Line 1: Line 1:
#REDIRECT ] {{redirect category shell|{{R from abbreviation}}{{R from subtopic}}{{R with history}}}}
==pREST server==

'''pREST''' server is a java servlet container REST MVC web framework for simple development of RIA and REST web applications and services.

Older project sources are hosted on http://prest.sourceforge.net/ . Project is under active development and new enterprise features are implemented and not published as open source.

'''For new project releases contact project mantainer''' ''Peter Rybar <pr.rybar@gmail.com>''.

===Features===

* Modularity - modular framework architecture
* Extensibility - easy to extend and integrate by any other technologies
* Departed development of application logic, presentation and data layer
* Horizontal application development - developer does not need to be skilled in all technologies through the application
* Minimal time to learn and shallow learning curve
* Development of RIA web applications and REST services the same way
* Minimal other technology dependencies and their loose coupling - easy to maintain for a long time
* Platform independency - Java 7 or higher

===Authors===

# ''Peter Rybar'' - project leader, architect, developer
# ''Daniel Buchta'' - main developer for server side project components
# ''Jozef Sivek'' - main developer for client side project components and design

==pREST Quick Start==

pREST - Java Web Framework for servlet container which is dedicated for effective development of Web applications and Web services.

;Controller: - basic unit of pREST Web application which controls the flow of input/output data. It is represented by a Java class
;Action: - basic functional unit of pREST Web controller, represented by its public method
;Request parameters: - standard POST/GET HTTP input parameters
;URL parameters: - sequential input parameters gained from the URI part after the controller action part (the parameter separator is "/")

===Controller creation===

<syntaxhighlight lang="java">
/**
* pREST controller extends class prest.core.Controller.
* The controller is mapped to the part of URL in the initialize()
* method of prest.core.Application class:
*
* package quickstart;
*
* public class QuickStartApplication extends prest.core.Application {
*
* @Override
* public void initialize() throws ApplicationException {
* mount("/controller-path", new QuickStartController());
* }
* }
*
* pREST application represented by an application class QuickStartApplication
* is specified in web.xml
*
* <filter>
* <filter-name>ServletFilter</filter-name>
* <filter-class>prest.core.ServletFilter</filter-class>
* <init-param>
* <param-name>Application</param-name>
* <param-value>quickstart.QuickStartApplication</param-value>
* </init-param>
* </filter>
* <filter-mapping>
* <filter-name>ServletFilter</filter-name>
* <url-pattern>/*</url-pattern>
* </filter-mapping>
*
* The controller's methods can be call by URL:
*
* http://<host>:<port>/<app_name>/<controller-path>/<action_name>
*
* <app_name> - the application context
* <controller_path> - the path a pREST controller is mapped, in our case "/controller-path"
* <action_name> - the name of a requested action - action is mapped into name of public method
* of the controller or into the name specified in @Action annotation
*/
public class QuickStartController extends prest.core.Controller {

/**
* pREST engine try to call the public controller method annotated by @Action
* annotation with the same name as the URL action name <action_name> is.
* <action_name> is the first URL segment after <controller_path>.
* The helloWorld() method therefore can be invoke by URL with action
* named "hello-world":
* http://<host>:<port>/<app_name>/<controller_path>/hello-world
*
* The @Doc annotation is used for documenting a controller, a method (action)
* and a method parameter - the documentation can be displayed by calling
* _docs_ and _doc_ actions like this:
* http://<host>:<port>/<app_name>/<controller_path>/_doc_
*
* The example method below sets HTTP response parameters:
* content type, charset encoding and a content of a HTTP body.
*
* @return string written to output
*/
@Action(name="hello-world")
@Doc("Documentation of a method")
public String helloWorld() {
setContentType(ContentType.TEXT_PLAIN_UTF8);
String httpBody = "Hello World!";
return httpBody;
}

/**
* POST/GET parameters with p1 and p2 keys are mapped on param1 and param2
* parameters by using @Key annotation (prest.core.annotations.Key)
*
* For example, in case of URL:
* http://localhost:8080/quickstart/controller-path/post-get-parameters?p1=foo&p2=77
* the param1 will get "foo" value and param2 will get number 77 as a value.
*
* In case of missing POST/GET parameter or unsuccessful conversion of prameter
* string representation to corresponding object or primnitive Java type,
* parameter has null value.
*
*
* @param param1
* the value of POST/GET parameter of p1 is mapped to this parameter
* @param param2
* the value of POST/GET parameter of p2 is mapped to this parameter
*/
@Action(name="post-get-parameters")
public void postGetParameters(
@Key("p1") String param1,
@Key("p2") Integer param2)
{
// do_something();
}

/**
* The URL parameters are mapped into arguments (not annotated by @Key annotation)
* of the called method seriately.
*
* For example, in case of URL:
* http://localhost:8080/quickstart/controller-path/url-parameters/foo/77
* the stringParam will get "foo" value and integerParam will get number 77
* as a value.
*
* In case of missing URL parameter or unsuccessful conversion of URL prameter
* string representation to corresponding object (or primnitive Java type),
* parameter has a null value.
*
* @param stringParam
* the value of the first URL parameter is mapped into this argument
* @param integerParam
* the value of the second URL parameter is mapped into this argument.
*/
@Action(name="url-parameters")
public void urlParameters(
@Doc("Documentation of string parameter") String stringParam,
@Doc("Documentation of integer parameter") Integer integerParam)
{
// do_something();
}

/**
* The special types:
* - prest.core.types.UrlParameters
* - prest.core.types.RequestParameters
* encapsulates all URL or POST/GET parameters.
*
* @param urlParameters
* all URL parameters.
* @param requestParameters
* all POST/GET parameters.
*/
@Action(name="special-parameters")
public void specialParameters(
UrlParameters urlParameters,
RequestParameters requestParameters)
{
// do_something_with_all_url_parameters(urlParameters);
// do_something_with_all_request_parameters(requestParameters);
}

/**
* @Action annotation (prest.core.annotations.Action) is used for mapping
* the action name into an appropriate annotated controller's public
* method which can be called by defined HTTP methods.
* This Java controller method will be called only by GET HTTP method on URL
* with action name "product":
* http://host.net/app/controller-path/product
* If we do not specify httpMethod @Action parameter, pREST will call
* this method in case any HTTP method.
*
* @param id
* URL parameter
* @return object, which will be serialized into string by calling a toString() method.
*/
@Action(name = "product", httpMethod = {"GET"})
public Car getProduct(Long id) {
Car car = Car.get(id);
return car;
}

/**
* @View annotation (prest.web.annotations.View) is used for assigne the
* input/output filter with called controller method.
* The goal of the @View annotation filter is to represent testView()
* returned data (model) by template specified as @View annotation
* template attribute.
*
* @return data, model that has to be visualized by @View annotation template.
*/
@Action(name="test-view")
@Doc("The example of @View annotation usage")
@View(template = "/templates/view.jsp")
public Object testView() {
String data = {"data", "for", "view"};
return data;
}

/**
* @Json annotation (prest.json.annotations.Json) is used for assigne the
* input/output filter with called controller method.
* The task of the @Json annotation filter is to serialize output data
* into JSON format.
*
* @param id
* URL parameter
* @return data object, model (has to be serialized into JSON format)
*/
@Action(name="test-json")
@Json
public Car testJson(Long id) {
Car car = Car.get(id);
return car;
}
}
</syntaxhighlight>

==pREST Form Tutorial==

pREST framework allows creating forms with a validation support and pREST provides an easy way how to work with them. Let's see, the way how to work with HTML forms.

Let's demonstrate the work with HTML forms on an example of page which is used for user's profile attributes modification.

===Server side form implementation and validation===

Let's have a project with the following structure:

<syntaxhighlight lang="bash">
demo
|-- docs
|-- lib
| `-- prest.jar
|-- src
| `-- demo
| |-- DemoApplication.java
| `-- controllers
| `-- ProfileController.java
|-- web
| |-- META-INF
| |-- WEB-INF
| | `-- web.xml
| `-- index.html
|-- build.properties
`-- build.xml
</syntaxhighlight>

Our goal is to create a simple page, which is created by one form with some input components and with a submit button. After submitting the data our goal is to handle the submitted data, validate whether the values were in a correct format, and then save this data.

pREST framework is based on MVC (Model - View - Controller) Design Pattern principles. In our case the Model will be a class which will be created with appropriate attributes and it will bear demo.model.Profile name. The next thing is to create a Controller class demo.controllers.ProfileController, which will manage a page logic. This controller will be mapped in an application class demo.DemoApplication in initialize() method to an URI (let's say): /user

<syntaxhighlight lang="java">
import prest.core.Application;
import prest.core.ApplicationException;
import demo.controllers.ProfileController;

public class DemoApplication extends Application {

@Override
public void initialize() throws ApplicationException {
mount("/user", new ProfileController());
}

}
</syntaxhighlight>

We will add a public ProfileForm profile(ProfileForm form) method into the controller class. The action of the same as method's name will be mapped into this method and will be exposed on a URL: http://localhost:8080/demo/user/profile. The input parameter of this method is a class which extends prest.validators.form.Form type and this type is also a return value of the method. This class is also a form model we are working with.

<syntaxhighlight lang="java">
package demo.controllers;

import demo.model.Profile;
import prest.core.Controller;
import prest.core.annotations.Action;
import prest.core.annotations.Doc;
import prest.core.annotations.Key;
import prest.validators.form.Form;
import prest.web.annotations.View;

public class ProfileController extends Controller {

@Action(name = "profile", httpMethod = {"GET", "POST"})
@Doc("Profile form page")
@View(template = "/templates/profile.jsp")
public ProfileForm profile(@Doc("Profile Form") ProfileForm form) {
return form;
}
}
</syntaxhighlight>

We want to make a validation of received data. The validation is a process of checking the syntactic and semantic correctness of the received data, which has been submitted from a web browser throughout HTML form. Let's show how easy and elegant is the process of data validation and how easy is to affect the application flow according to validation result.

In our case, the form and his validation will be represented by a demo.forms.ProfileForm class extending prest.validators.form.Form. The first thing to do is to define form entries and corresponding validators inplementing public void addEntries() method. By calling public void addEntry(String key, Validator validator, String description) the concrete type of a validator will be bound to HTTP form entry which is identified by key attribute. Calling public void addEntrySubmit(String key) defines which form entry is mandatory and then must be send to consider form as submitted. During the form definition it is possible to use some of the build-in validators, or it is also possible to create own implementation, which extends prest.validators.common.Validator type.

<syntaxhighlight lang="java">
package demo.forms;

import prest.validators.EmailValidator;
import prest.validators.LongValidator;
import prest.validators.RegexpValidator;
import prest.validators.form.Form;
import demo.model.Profile;

public class ProfileForm extends Form {

@Override
protected void addEntries() {
addEntry("name", new ProfileNameValidator(), "Full name");
addEntry("email", new EmailValidator("@"), "E-mail address");
addEntry("age", new LongValidator(), "Age");
addEntrySubmit("submit");
}

}

class ProfileNameValidator extends RegexpValidator {
public ProfileNameValidator() {
super("\\w{4,}");
setRegexpErrorMessage("at least 4 alphanumeric characters");
}
}
</syntaxhighlight>

Presentation view or View (For simplicity let's have a JSP template) web/templates/profile.jsp may looks like this:

<syntaxhighlight lang="html5">
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<%@ page
language="java"
contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
isELIgnored="false"
import="prest.validators.form.Form"
%>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="sk" lang="sk">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="content-language" content="sk" />
<title>Profil</title>
</head>
<body>
<h1>Profile</h1>

<form id="profile_form" method="post" action="">

<% ProfileForm form = (Form) request.getAttribute("ProfileForm"); %>

<div>
<label for="name">Name:</label>
<input type="text" id="name" name="name" value="<%= form.getEntryValue("name") %>"/>
<span id="name_message" class="error"><%= form.getEntryErrorMessage("name") %></span>
</div>
<div>
<label for="email">E-mail:</label>
<input id="email" type="text" name="email" value="<%= form.getEntryValue("email") %>" />
<span id="email_message" class="error"><%= form.getEntryErrorMessage("email") %></span>
</div>
<div>
<label for="age">Age:</label>
<input id="age" type="text" name="age" value="<%= form.getEntryValue("age") %>" />
<span id="age_message" class="error"><%= form.getEntryErrorMessage("age") %></span>
</div>
<div>
<input id="submit" type="submit_button" name="submit" value="save" />
</div>
</form>

</body>
</html>
</syntaxhighlight>

After the first call of URL http://localhost:8080/demo/user/profile the controller method profile() is called. Output of action method profile() is used as data for HTML template listed in @View annotation. After the first call there were no form entries sent, so the form is empty. After filling out the form, it is a good practice to submit it to the same URL (to the self), where the data will be processed. In case of defective input data the form is showed again with error messages. This process repeats cyclically until valid data are obtained, which are processed afterwards. The action method profile() input parameter ProfileForm form is automatically initialized by HTTP request form parameters. During the process of initialization the validation is performed. From point of view pREST engine the action method's profile() input parameter ProfileForm form is an aggregated type.

Often we face a situation in which we need to fill the form at the begining with some sort of data - either with data acquired from a database or with other dynamic data. In this case it is appropriate to add a logic, which covers the initial filling of the form and form data extraction - setProfile() and getProfile() respective. This methods enable the transformation of valid form (page model) directly into business logic model.

<syntaxhighlight lang="java">
package demo.forms;

import prest.validators.EmailValidator;
import prest.validators.LongValidator;
import prest.validators.RegexpValidator;
import prest.validators.form.Form;
import demo.model.Profile;

public class ProfileForm extends Form {

public ProfileForm() {
}

@Override
protected void addEntries() {
addEntry("name", new ProfileNameValidator(), "Full name");
addEntry("email", new EmailValidator("@"), "E-mail address");
addEntry("age", new LongValidator(), "Age");
addEntrySubmit("submit");
}

public void setProfile(Profile profile) {
setEntryValue("name", profile.getName());
setEntryValue("email", profile.getEmail());
setEntryValue("age", String.valueOf(profile.getAge()));
}

public Profile getProfile() {
String username = (String) getValidator("name").getValue(0);
long age = (Long) getValidator("age").getValue(0);
String email = (String) getValidator("email").getValue(0);

Profile result = new Profile(username, email, age);
return result;
}
}

class ProfileNameValidator extends RegexpValidator {
public ProfileNameValidator() {
super("\\w{4,}");
setRegexpErrorMessage("at least 4 alphanumeric characters");
}
}
</syntaxhighlight>

The logic for initialization and processing of the form are added into a controller method, for the case where there was no data submited by the client.

<syntaxhighlight lang="java">
package demo.controllers;

import prest.core.Controller;
import prest.core.RedirectException;
import prest.core.annotations.Action;
import prest.core.annotations.Doc;
import prest.json.annotations.Json;
import prest.web.annotations.View;
import demo.applogic.Profiles;
import demo.forms.ProfileForm;
import demo.model.Profile;

@Doc("Profile controller")
public class ProfileController extends Controller {

@Action(name = "profile", httpMethod = {"GET", "POST"})
@View(template = "/templates/profile.jsp")
@Doc("Profile form page")
public ProfileForm profile(@Doc("Profile form") ProfileForm form) {

if (form.isSubmitted()) {
if (form.isValid()) {
// extract profile (business model)
// from HTTP form (page model)
Profile profile = form.getProfile();
// call business logic to handle data
Profiles.setProfile(profile);
} else {
// form is invalid, try next iteration
}
} else {
// fill the form if it is possible
Profile profile = Profiles.getProfile();
if (profile != null) {
form = new ProfileForm();
form.setProfile(profile);
}
}

return form;
}

@Action(httpMethod = "GET")
@Doc("Redirects to the profile")
public void index() throws RedirectException {
redirect(getApplicationPath() + getControllerPath() + "/profile");
}

}
</syntaxhighlight>

Finaly we are done with server side HTTP form and his validation.

===Client side form validation===

While '''pREST server''' framework is intended for creation of Rich Internet Applications (RIA), '''pREST client''' offers a set of tools including a form validation. pREST client enables validation of values of single arrays on the browser side.

Input HTML form entries, are abstracted by JavaScript objects. During object creation it is necessary give to form entries identifier as an object constructor parameter under which is the element represented in the DOM structure. So the object can be created following way:

<syntaxhighlight lang="javascript">
var name_input = new prest.widgets.forms.TextInput("name");
var submitButton = new prest.widgets.forms.Button("submit_button");
</syntaxhighlight>

pREST client javaScript library implements Signal-Slot Design Pattern. Validators are objects they extends prest.validators.Validator type. Hence all validators emits two kinds of signal:

* signal_valid
* signal_invalid

We have to implement corresponding slots, one for valid and one for invalid signal emited as a result of validation process. Let us implement slots for RegexValidator.

<syntaxhighlight lang="javascript">
var name_validator = new prest.validators.RegexValidator(/^+$/, "Bad name format");

name_validator.signal_valid.connect(
function(value) {
document.getElementById("name_message").innerHTML = "";
}
);

name_validator.signal_invalid.connect(
function(error_message, value) {
document.getElementById("name_message").innerHTML = error_message;
}
);
</syntaxhighlight>

It is possible, similarly as in the case of server component, to associate validators to the HTTP form entries by using prest.validators.FormValidator. Individual validators are bound into FormValidator by calling an add_form_object() method. The meaning of the parameters are:

'''add_form_object(form_object, validator, connect)'''

* form_object - object representing input HTTP form entry
* validator - validator which we want to use for HTTP form entry validation
* connect - a flag of automatic connection of a validator's slot validate() to HTTP form entry object's signal signal_change, so that the validation is realized in a real time, not only on submit

During the creation of FormValidator, it is possible to specify whether the HTTP form entry value changes should be immediately signaled to the validator by using a third optional parameter of the add_form_object() method. In that case the validation will be running in a real time, after every change of the value of the HTTP form entry. On the other hand the validation will take place after the explicit call of FormValidator method validate().

Client side HTTP form validator can be implementid like this:

<syntaxhighlight lang="javascript">
var form_validator = new prest.validators.FormValidator();

//realtime validation
form_validator.add_form_object(name_input, name_validator, true);
//validate only on submit
form_validator.add_form_object(email_input, email_validator);
form_validator.add_form_object(age_input, age_validator);
</syntaxhighlight>

The complete client side validation for our demo profile form will be:

<syntaxhighlight lang="html5">
<head>
<script type="text/javascript" src="${pageContext.request.contextPath}/javascript/prest.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/javascript/prest/dev.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/javascript/prest/calendar.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/javascript/prest/validators.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/javascript/prest/widgets/forms.js"></script>

<script type="text/javascript">

prest.queue_load_event(init);

function init() {
// name
var name_input = new prest.widgets.forms.TextInput("name");

var name_validator = new prest.validators.RegexValidator(/^+$/, "Bad name format");
name_validator.signal_valid.connect(
function(value) {
document.getElementById("name_message").innerHTML = "";
}
);
name_validator.signal_invalid.connect(
function(error_message, value) {
document.getElementById("name_message").innerHTML = error_message;
}
);

// email
var email_input = new prest.widgets.forms.TextInput("email");

var email_validator = new prest.validators.EmailRegexValidator("Bad email format");
email_validator.signal_valid.connect(
function(value) {
document.getElementById("email_message").innerHTML = "";
}
);
email_validator.signal_invalid.connect(
function(error_message, value) {
document.getElementById("email_message").innerHTML =
"Invalid value " + value + " ( " + error_message + " )";
}
);

// age
var age_input = new prest.widgets.forms.TextInput("age");

var age_validator = new prest.validators.RegexValidator(/^\d{1,3}$/, "Bad age format");
age_validator.signal_valid.connect(
function(value) {
document.getElementById("age_message").innerHTML = "";
}
);
age_validator.signal_invalid.connect(
function(error_message, value) {
document.getElementById("age_message").innerHTML =
"Invalid value " + value + " ( " + error_message + " )";
}
);

// form
var form_validator = new prest.validators.FormValidator();
//realtime validation
form_validator.add_form_object(name_input, name_validator, true);
//validate only on submit
form_validator.add_form_object(email_input, email_validator);
form_validator.add_form_object(age_input, age_validator);

var submitButton = new prest.widgets.forms.Button("submit_button");
submitButton.signal_click.connect(function()
{
//if (form_validator.validate()) {
// document.getElementById("profile_form").submit();
//}
}
);

}
</script>
</head>
</syntaxhighlight>

Definitly we are done with client side HTTP form validation now.

{{Uncategorized|date=August 2012}}

Latest revision as of 23:23, 3 May 2017

Redirect to:

This page is a redirect. The following categories are used to track and monitor this redirect:
  • From an abbreviation: This is a redirect from an abbreviation to a related topic, such as the expansion of the abbreviation. Use this template for any length reduction other than the following.
    • Instead of this template:
      • use {{R from acronym}} for abbreviations that are pronounced as words, such as NATO and RADAR;
      • use {{R from initialism}} for those abbreviations that are pronounced as letters, such as CIA and HIV;
      • use {{R from short name}} for the initials of a person's name or for any other length reduction that is not typically classed as an abbreviation.
  • With history: This is a redirect from a page containing substantive page history. This page is kept as a redirect to preserve its former content and attributions. Please do not remove the tag that generates this text (unless the need to recreate content on this page has been demonstrated), nor delete this page.
    • This template should not be used for redirects having some edit history but no meaningful content in their previous versions, nor for redirects created as a result of a page merge (use {{R from merge}} instead), nor for redirects from a title that forms a historic part of Misplaced Pages (use {{R with old history}} instead).
When appropriate, protection levels are automatically sensed, described and categorized.
PREST: Difference between revisions Add topic