SlideShare a Scribd company logo
1 of 42
Download to read offline
Presentation Layer

Sérgio Silva

October 2013
Fenix Architecture
Application Container (Tomcat, Jetty)

JSPs
renderers

Faces
Jersey
(REST API)
Domain
Model

Struts

Fenix Framework
(STM)

MYSQL
Today
Application Container (Tomcat, Jetty)

JSPs
renderers

Faces
Jersey
(REST API)
Domain
Model

Struts

Fenix Framework
(STM)

MYSQL
Struts
Control Layer

● open-source web application framework
● uses and extends the Java Servlet API
● model–view–controller (MVC) architecture
● version 1.2.7
● http://struts.apache.org/release/1.2.x/
Struts
Control Layer
Struts
Control Layer
Struts Dispatching
fenix.ist.utl.pt/publico/executionCourse.do?method=marks&executionCourseID=1610612925705
Struts Dispatching
Module
fenix.ist.utl.pt/publico/executionCourse.do?method=marks&executionCourseID=1610612925705

Semantic division in modules
● struts-publico.xml
● .do maps to Struts Servlet (JavaServlet API)
○ src/main/resources/web.xml
Struts Dispatching
Mapping
fenix.ist.utl.pt/publico/executionCourse.do?method=marks&executionCourseID=1610612925705

Mapping between path and action
●

The path is a string
○

●

“/executionCourse”

Where is the mapping ?
○

Mapping with annotations (what you should use)
@Mapping(module
="publico" path="/executionCourse"
)

○

struts-publico.xml (read-only, don’t create things here)
<action type="n.s.f.p.A.p.ExecutionCourseDA" parameter="method" path="/executionCourse">
Struts Dispatching
Mapping
fenix.ist.utl.pt/publico/executionCourse.do?method=marks&executionCourseID=1610612925705

Mapping between path and action
● Action Class ( ExecutionCourseDA.java)
●

each action is module aware

●

usually extends FenixDispatchAction (check outline on eclipse)
○

helper methods
■
■

redirect

■

getLoggedPerson

■

●

getFromRequest

getDomainObject(request,parameter)

method execute runs always
DEMO
Struts @ Fenix
Action
fenix.ist.utl.pt/publico/executionCourse.do?method=marks&executionCourseID=1610612925705

ExecutionCourseDA.java
public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) {
final String executionCourseOID = request.getParameter("executionCourseID");
final ExecutionCourse executionCourse = FenixFramework.getDomainObject(executionCourseOID);
Map<> attendsMap = getAttendsMap(executionCourse);
request.setAttribute("executionCourse", executionCourse);
request.setAttribute("attendsMap", attendsMap);
request.setAttribute("dont-cache-pages-in-search-engines", Boolean.TRUE);
return mapping.findForward("execution-course-marks");
}

● Parameters
○ get values on query string

● Attributes
○ get or set state on request
Struts
Wrapping Up
JSPs
Presentation Layer
Struts
Presentation Layer
● Tiles
○ templating system
○ create a common look and feel for a web application
○ create reusable view components
○ bridge to JSPs
○ module aware
● tiles-<module>-definitions.xml
○ template definitions
● tiles-<module>-pages-definitions.xml
○ fill in the template
Struts @ Fenix
Forwards
ExecutionCourseDA.java
public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) {
final String executionCourseOID = request.getParameter("executionCourseID"
);
final ExecutionCourse executionCourse = FenixFramework.
getDomainObject(executionCourseOID);
Map<> attendsMap = getAttendsMap(executionCourse);
request
.setAttribute("executionCourse" executionCourse);
,
request
.setAttribute("attendsMap", attendsMap);
request
.setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE);
,
return mapping.findForward("execution-course-marks"
);
}
Struts @ Fenix
Forwards
ExecutionCourseDA.java
public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) {
final String executionCourseOID = request.getParameter("executionCourseID
");
final ExecutionCourse executionCourse = FenixFramework.
getDomainObject(executionCourseOID);
Map<> attendsMap = getAttendsMap(executionCourse);
request
.setAttribute("executionCourse" executionCourse);
,
request
.setAttribute("attendsMap", attendsMap);
request
.setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE);
,
return mapping.findForward("execution-course-marks");
}

● What is mapping.findForward(..) ?
Struts @ Fenix
Forwards
ExecutionCourseDA.java
public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) {
final String executionCourseOID = request.getParameter("executionCourseID
");
final ExecutionCourse executionCourse = FenixFramework.
getDomainObject(executionCourseOID);
Map<> attendsMap = getAttendsMap(executionCourse);
request
.setAttribute("executionCourse" executionCourse);
,
request
.setAttribute("attendsMap", attendsMap);
request
.setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE);
,
return mapping.findForward("execution-course-marks");
}

● Forwards annotation (what you should use)
@Forwards({ @Forward(name = "execution-course-marks", path = "/publico/executionCourse/marks.jsp") })
public class ExecutionCourseDA extends FenixDispatchAction { … }

●
●

name - forward name
path - logic name for tiles
Struts @ Fenix
Forwards (Deprecated)
ExecutionCourseDA.java
public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) {
final String executionCourseOID = request.getParameter("executionCourseID
");
final ExecutionCourse executionCourse = FenixFramework.
getDomainObject(executionCourseOID);
Map<> attendsMap = getAttendsMap(executionCourse);
request
.setAttribute("executionCourse" executionCourse);
,
request
.setAttribute("attendsMap", attendsMap);
request
.setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE);
,
return mapping.findForward("execution-course-marks");
}

● struts-publico.xml
<action type="n.s.f.p.A.p.ExecutionCourseDA" parameter="method" path="/executionCourse"
>
<forward path=
"execution-course-marks" name="execution-course-marks"></forward>
…………………………………………………
</action>

●
●

name - forward name
path - logic name for tiles
Struts @ Fenix
Forwards (Deprecated)
● tiles-public-definitions.xml
<definition name=
"execution-course-marks" extends="definition.public.executionCourse">
<put name="body" value="/publico/executionCourse/marks.jsp" >
/
</definition>

● tiles-public-pages-definitions.xml
<definition name=
"definition.public.executionCourse" page="/layout/istLayout.jsp">
<put name="title" value="/commons/functionalities/courseTitle.jsp"/>
<put name="hideLanguage" value="true"/>
<put name="symbols_row" value="/publico/degreeSite/symbolsRow.jsp" />
<put name="profile_navigation" value="/publico/degreeSite/profileNavigation.jsp"/>
<put name="main_navigation" value="/publico/executionCourse/mainNavigation.jsp"/>
<put name="body_header" value="/publico/executionCourse/executionCourseHeader.jsp"/>
<put name="body" value="/commons/blank.jsp" />
<put name="footer" value="/publico/degreeSite/footer.jsp" />
<put name="rss" value="/messaging/announcements/rssHeader.jsp">
/
<put name="keywords" value="/messaging/announcements/keywordsHeader.jsp">
/
</definition>

● istLayout.jsp
<tiles:insert attribute=
"body" ignore="true"/>
<tiles:insert attribute=
"footer" ignore="true"/>
JSPs
● JavaServer Pages (JSP)
● create dynamically generated web pages
based on HTML
● HTML with behaviour
DEMO
publico/executionCourse/marks.jsp
JSPs
without renderers
● publico/executionCourse/marks.jsp
<logic:iterate id="evaluation" name="executionCourse property="orderedAssociatedEvaluations
"
">

● ExecutionCourseDA.java
public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) {
final String executionCourseOID = request.getParameter("executionCourseID"
);
final ExecutionCourse executionCourse = FenixFramework.
getDomainObject(executionCourseOID);
Map<Attends, Map<Evaluation, Mark>> attendsMap = getAttendsMap(executionCourse);
request
.setAttribute("executionCourse" executionCourse);
,
request
.setAttribute("attendsMap", attendsMap);
request
.setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE);
,
return mapping.findForward("execution-course-marks"
);
}
JSPs
without renderers
● publico/executionCourse/marks.jsp
<logic:iterate id="evaluation" name="executionCourse" property="orderedAssociatedEvaluations">

● ExecutionCourseDA.java
public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) {
final String executionCourseOID = request.getParameter("executionCourseID"
);
final ExecutionCourse executionCourse = FenixFramework.
getDomainObject(executionCourseOID);
Map<Attends, Map<Evaluation, Mark>> attendsMap = getAttendsMap(executionCourse);
request
.setAttribute("executionCourse", executionCourse);
request
.setAttribute("attendsMap", attendsMap);
request
.setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE);
,
return mapping.findForward("execution-course-marks"
);
}

●
●

name - get from request attribute or parameter with that name
property - get property from object
○

●
●

executionCourse .getOrderedAssociatedEvaluations ()

uses Java Bean conventions
id - defines bean in jsp scope
JSPs
without renderers
● simple bean example
<h3><bean:write name=
"executionCourse" property="name"></h3>

●

executionCourse.getName()

● Tag libs
○
○
○
○

<bean:*>
<logic:*>
<html:*>
http://struts.apache.org/release/1.2.x/userGuide/
JSPs
Renderers
● integration with domain model and fenixframework
● our taglib <fr:*>
○ fr:view - display domain objects
○ fr:create - create domain objects
○ fr:edit - edit domain objects

● renderers-config.xml
○ All renderers definitions
JSPs
Renderers
● What is a renderer ?
○ java class used to produce HTML
○ Properties
■ layout
●

logical name for renderer definition

■ mode
●
●

input
output

■ class
●

rendered type
○ String
○ ExecutionCourse
○ int

■ properties
●

render specific properties
JSPs
renderers-config.xml

● output renderer
<renderer type=
"java.util.Collection" layout="contact-list" class="n.s.f.p.r.ContactListRenderer"
>
<property name=
"bundle" value="APPLICATION_RESOURCES"
/>
<property name=
"defaultLabel" value="label.partyContacts.defaultContact" >
/
</renderer>

● input renderer
<renderer mode=
"input" type="j.u.Collection" layout="option-select" class="p.i.fr.InputCheckBoxListRenderer"
>
<property name=
"eachClasses" value="dinline" />
</renderer>
JSPs
Renderers
● manageApplications.jsp
<fr:view name="appsOwned" schema="oauthapps.view.apps"
>
<fr:layout name="tabular">
<fr:property name="classes" value="tstyle4 thcenter thcenter"
/>
<fr:property name="columnClasses" value="tdcenter, tdcenter, ..."
/>
</fr:layout>
</fr:view>

● renderers-config.xml
<renderer type="java.util.Collection" layout="tabular" class="p.i.f.r.CollectionRenderer"
>
<property name="groupLinks" value="true"/>
<property name="linkGroupSeparator" value=", "/>
</renderer>
JSPs
Renderers
● manageApplications.jsp
<fr:view name="appsOwned" schema="oauthapps.view.apps"
>
<fr:layout name="tabular">
<fr:property name="classes" value="tstyle4 thcenter thcenter"
/>
<fr:property name="columnClasses" value="tdcenter, tdcenter, ..."
/>
</fr:layout>
</fr:view>

● renderers-config.xml
<renderer type="java.util.Collection" layout="tabular" class="p.i.f.r.CollectionRenderer"
>
<property name="groupLinks" value="true"/>
<property name="linkGroupSeparator" value=", "/>
</renderer>
JSPs
Renderers
JSPs
Renderers
Renderers
(reusable) Schemas
● manageApplications.jsp
<fr:view name="appsOwned" schema="oauthapps.view.apps"
>
<fr:layout name="tabular">
<fr:property name="classes" value="tstyle4 thcenter thcenter"
/>
<fr:property name="columnClasses" value="tdcenter, tdcenter, ..."
/>
</fr:layout>
</fr:view>
Renderers
(reusable) Schemas
● manageApplications.jsp
<fr:view name="appsOwned" schema="oauthapps.view.apps">
<fr:layout name="tabular">
<fr:property name="classes" value="tstyle4 thcenter thcenter"
/>
<fr:property name="columnClasses" value="tdcenter, tdcenter, ..."
/>
</fr:layout>
</fr:view>
Renderers
(reusable) Schemas
● Schemas
<fr:view name="appsOwned" schema="oauthapps.view.apps">
<fr:layout name="tabular">
<fr:property name="classes" value="tstyle4 thcenter thcenter"
/>
<fr:property name="columnClasses" value="tdcenter, tdcenter, ..."
/>
</fr:layout>
</fr:view>

●

specify how object’s slots are rendered

●

schemas-config.xml

●

*-schemas.xml
○

logical separation
Renderers
(reusable) Schemas
●

personnelSection-schemas.xml
<schema name="oauthapps.view.apps" type="net.sourceforge.fenixedu.domain.ExternalApplication"
bundle="APPLICATION_RESOURCES"
>
<slot name="name" key="oauthapps.label.app.name" />
<slot name="description" layout="longText" key="oauthapps.label.app.description"
/>
<slot name="scopes" layout="flowLayout">
<property name=
"eachLayout" value="values"></property>
<property name=
"eachSchema" value="oauthapps.view.scope.name"
></property>
<property name=
"htmlSeparator" value=", "></property>
</slot>
<slot name="siteUrl" key="oauthapps.label.app.site.url" />
</schema>

●
●
●

name
○ unique identifier
type
○ schema target type
slot
○ object slot to render
Renderers
(inline) Schemas
<fr:view name="appsOwned" schema="oauthapps.view.apps"
>
<fr:layout name="tabular">
<fr:property name="classes" value="tstyle4 thcenter thcenter"
/>
<fr:property name="columnClasses" value="tdcenter, tdcenter, ..."
/>
</fr:layout>
</fr:view>
Renderers
(inline) Schemas
<fr:view name="appsOwned">
<fr:schema type="net.sourceforge.fenixedu.domain.ExternalApplication"bundle="APPLICATION_RESOURCES"
>
<fr:slot name="name" key="oauthapps.label.app.name" />
<fr:slot name="description" layout="longText" key="oauthapps.label.app.description"
/>
<fr:slot name="scopes" layout="flowLayout">
<fr:property name="eachLayout" value="values"></property>
<fr:property name="eachSchema" value="oauthapps.view.scope.name"
></property>
<fr:property name="htmlSeparator" value=", "></property>
</fr:slot>
<fr:slot name="siteUrl" key="oauthapps.label.app.site.url" />
</fr:schema>
<fr:layout name="tabular">
<fr:property name="classes" value="tstyle4 thcenter thcenter"
/>
<fr:property name="columnClasses" value="tdcenter, tdcenter, ..."
/>
</fr:layout>
</fr:view>
Resource Bundles
<fr:view name="appsOwned">
<fr:schema type="net.sourceforge.fenixedu.domain.ExternalApplication"bundle="APPLICATION_RESOURCES">
<fr:slot name="name" key="oauthapps.label.app.name" />
<fr:slot name="description" layout="longText" key="oauthapps.label.app.description"
/>
<fr:slot name="scopes" layout="flowLayout">
<fr:property name="eachLayout" value="values"></property>
<fr:property name="eachSchema" value="oauthapps.view.scope.name"
></property>
<fr:property name="htmlSeparator" value=", "></property>
</fr:slot>
<fr:slot name="siteUrl" key="oauthapps.label.app.site.url" />
</fr:schema>
<fr:layout name="tabular">
<fr:property name="classes" value="tstyle4 thcenter thcenter"
/>
<fr:property name="columnClasses" value="tdcenter, tdcenter, ..."
/>
</fr:layout>
</fr:view>

●

src/main/resources/resources/ApplicationResources_pt.properties
○ oauthapps.label.app.name
="Nome Aplicação"

●

src/main/resources/resources/ApplicationResources_en.properties
○ oauthapps.label.app.name
="Application Name"
JavaServer Faces
● JavaServer Faces
○ version 1.1

● component-based user interfaces for web
apps
● servlet mapping *.faces
● faces-config.xml
JavaServer Faces
https://fenix.ist.utl.pt/publico/degreeSite/viewCurricularCourse.faces?degreeID=2761663971474
JavaServer Faces
https://fenix.ist.utl.pt/publico/degreeSite/viewCurricularCourse.faces?degreeID=2761663971474

●

publico/degreeSite/viewCurricularCourse.jsp

<h:outputFormat value="<h1>#{CurricularCourseManagement.degreePresentationName}</h1>" escape="false"/>

●

Backing Bean
○

●

name - CurricularCourseManagement

faces-config.xml

<managed-bean>
<description>ManagerCurricularCourseManagementBackingBean</
description>
<managed-bean-name>ManagerCurricularCourseManagement</ anaged-bean-name>
m
<managed-bean-class>n.s.f.p.b.m.c.ManagerCurricularCourseManagementBackingBean</
managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>

DEMO
Golden Rules
● don’t create stuff in struts-*.xml
○ use annotations
■ @Mapping
■ @Forwards
●

@Forward

● renderers-config.xml
○

read-only (unless you are going to create a new renderer)

● read renderers docs
○

Administrador > Frameworks > Renderers > Exemplos Renderers

● install resource bundle editor
○

https://fenix-ashes.ist.utl.pt/fenixWiki/I18NConventions
Q&A?

More Related Content

What's hot

Spring 3.x - Spring MVC
Spring 3.x - Spring MVCSpring 3.x - Spring MVC
Spring 3.x - Spring MVC
Guy Nir
 
Java Spring MVC Framework with AngularJS by Google and HTML5
Java Spring MVC Framework with AngularJS by Google and HTML5Java Spring MVC Framework with AngularJS by Google and HTML5
Java Spring MVC Framework with AngularJS by Google and HTML5
Tuna Tore
 

What's hot (17)

Paging Like A Pro
Paging Like A ProPaging Like A Pro
Paging Like A Pro
 
Spring MVC Annotations
Spring MVC AnnotationsSpring MVC Annotations
Spring MVC Annotations
 
Drupal8Day: Demystifying Drupal 8 Ajax Callback commands
Drupal8Day: Demystifying Drupal 8 Ajax Callback commandsDrupal8Day: Demystifying Drupal 8 Ajax Callback commands
Drupal8Day: Demystifying Drupal 8 Ajax Callback commands
 
Reactive state management with Jetpack Components
Reactive state management with Jetpack ComponentsReactive state management with Jetpack Components
Reactive state management with Jetpack Components
 
Spring 3.x - Spring MVC
Spring 3.x - Spring MVCSpring 3.x - Spring MVC
Spring 3.x - Spring MVC
 
Hastening React SSR - Web Performance San Diego
Hastening React SSR - Web Performance San DiegoHastening React SSR - Web Performance San Diego
Hastening React SSR - Web Performance San Diego
 
Demystifying AJAX Callback Commands in Drupal 8
Demystifying AJAX Callback Commands in Drupal 8Demystifying AJAX Callback Commands in Drupal 8
Demystifying AJAX Callback Commands in Drupal 8
 
State management in android applications
State management in android applicationsState management in android applications
State management in android applications
 
Backbone Basics with Examples
Backbone Basics with ExamplesBackbone Basics with Examples
Backbone Basics with Examples
 
Heroku pop-behind-the-sense
Heroku pop-behind-the-senseHeroku pop-behind-the-sense
Heroku pop-behind-the-sense
 
Simplified Android Development with Simple-Stack
Simplified Android Development with Simple-StackSimplified Android Development with Simple-Stack
Simplified Android Development with Simple-Stack
 
Architecting Single Activity Applications (With or Without Fragments)
Architecting Single Activity Applications (With or Without Fragments)Architecting Single Activity Applications (With or Without Fragments)
Architecting Single Activity Applications (With or Without Fragments)
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern
 
"Angular.js Concepts in Depth" by Aleksandar Simović
"Angular.js Concepts in Depth" by Aleksandar Simović"Angular.js Concepts in Depth" by Aleksandar Simović
"Angular.js Concepts in Depth" by Aleksandar Simović
 
Demystifying Drupal AJAX Callback Commands
Demystifying Drupal AJAX Callback CommandsDemystifying Drupal AJAX Callback Commands
Demystifying Drupal AJAX Callback Commands
 
Workshop 26: React Native - The Native Side
Workshop 26: React Native - The Native SideWorkshop 26: React Native - The Native Side
Workshop 26: React Native - The Native Side
 
Java Spring MVC Framework with AngularJS by Google and HTML5
Java Spring MVC Framework with AngularJS by Google and HTML5Java Spring MVC Framework with AngularJS by Google and HTML5
Java Spring MVC Framework with AngularJS by Google and HTML5
 

Similar to Training: Day Four - Struts, Tiles, Renders and Faces

Primefaces Nextgen Lju
Primefaces Nextgen LjuPrimefaces Nextgen Lju
Primefaces Nextgen Lju
Skills Matter
 
Introduction to Struts
Introduction to StrutsIntroduction to Struts
Introduction to Struts
elliando dias
 
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Provectus
 
Stepbystepguideforbuidlingsimplestrutsapp 090702025438-phpapp02
Stepbystepguideforbuidlingsimplestrutsapp 090702025438-phpapp02Stepbystepguideforbuidlingsimplestrutsapp 090702025438-phpapp02
Stepbystepguideforbuidlingsimplestrutsapp 090702025438-phpapp02
Rati Manandhar
 

Similar to Training: Day Four - Struts, Tiles, Renders and Faces (20)

Rich Portlet Development in uPortal
Rich Portlet Development in uPortalRich Portlet Development in uPortal
Rich Portlet Development in uPortal
 
Unit 07: Design Patterns and Frameworks (3/3)
Unit 07: Design Patterns and Frameworks (3/3)Unit 07: Design Patterns and Frameworks (3/3)
Unit 07: Design Patterns and Frameworks (3/3)
 
Building Web Apps with Express
Building Web Apps with ExpressBuilding Web Apps with Express
Building Web Apps with Express
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVC
 
PUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBootPUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBoot
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
How to React Native
How to React NativeHow to React Native
How to React Native
 
Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング
Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディングXitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング
Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング
 
Xitrum @ Scala Matsuri Tokyo 2014
Xitrum @ Scala Matsuri Tokyo 2014Xitrum @ Scala Matsuri Tokyo 2014
Xitrum @ Scala Matsuri Tokyo 2014
 
Android best practices
Android best practicesAndroid best practices
Android best practices
 
Struts Intro
Struts IntroStruts Intro
Struts Intro
 
Primefaces Nextgen Lju
Primefaces Nextgen LjuPrimefaces Nextgen Lju
Primefaces Nextgen Lju
 
Primefaces Nextgen Lju
Primefaces Nextgen LjuPrimefaces Nextgen Lju
Primefaces Nextgen Lju
 
Introduction to Struts
Introduction to StrutsIntroduction to Struts
Introduction to Struts
 
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
 
Writing HTML5 Web Apps using Backbone.js and GAE
Writing HTML5 Web Apps using Backbone.js and GAEWriting HTML5 Web Apps using Backbone.js and GAE
Writing HTML5 Web Apps using Backbone.js and GAE
 
Stepbystepguideforbuidlingsimplestrutsapp 090702025438-phpapp02
Stepbystepguideforbuidlingsimplestrutsapp 090702025438-phpapp02Stepbystepguideforbuidlingsimplestrutsapp 090702025438-phpapp02
Stepbystepguideforbuidlingsimplestrutsapp 090702025438-phpapp02
 
We sport architecture_implementation
We sport architecture_implementationWe sport architecture_implementation
We sport architecture_implementation
 
Spine.js
Spine.jsSpine.js
Spine.js
 

Recently uploaded

Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider  Progress from Awareness to Implementation.pptxTales from a Passkey Provider  Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
FIDO Alliance
 
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc
 
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptxHarnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
FIDO Alliance
 

Recently uploaded (20)

JavaScript Usage Statistics 2024 - The Ultimate Guide
JavaScript Usage Statistics 2024 - The Ultimate GuideJavaScript Usage Statistics 2024 - The Ultimate Guide
JavaScript Usage Statistics 2024 - The Ultimate Guide
 
Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider  Progress from Awareness to Implementation.pptxTales from a Passkey Provider  Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
 
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdfLinux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
 
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
 
Generative AI Use Cases and Applications.pdf
Generative AI Use Cases and Applications.pdfGenerative AI Use Cases and Applications.pdf
Generative AI Use Cases and Applications.pdf
 
Design and Development of a Provenance Capture Platform for Data Science
Design and Development of a Provenance Capture Platform for Data ScienceDesign and Development of a Provenance Capture Platform for Data Science
Design and Development of a Provenance Capture Platform for Data Science
 
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptxHarnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
 
2024 May Patch Tuesday
2024 May Patch Tuesday2024 May Patch Tuesday
2024 May Patch Tuesday
 
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...
 
Vector Search @ sw2con for slideshare.pptx
Vector Search @ sw2con for slideshare.pptxVector Search @ sw2con for slideshare.pptx
Vector Search @ sw2con for slideshare.pptx
 
Intro to Passkeys and the State of Passwordless.pptx
Intro to Passkeys and the State of Passwordless.pptxIntro to Passkeys and the State of Passwordless.pptx
Intro to Passkeys and the State of Passwordless.pptx
 
ERP Contender Series: Acumatica vs. Sage Intacct
ERP Contender Series: Acumatica vs. Sage IntacctERP Contender Series: Acumatica vs. Sage Intacct
ERP Contender Series: Acumatica vs. Sage Intacct
 
How we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdfHow we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdf
 
Event-Driven Architecture Masterclass: Engineering a Robust, High-performance...
Event-Driven Architecture Masterclass: Engineering a Robust, High-performance...Event-Driven Architecture Masterclass: Engineering a Robust, High-performance...
Event-Driven Architecture Masterclass: Engineering a Robust, High-performance...
 
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdfSimplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
 
Portal Kombat : extension du réseau de propagande russe
Portal Kombat : extension du réseau de propagande russePortal Kombat : extension du réseau de propagande russe
Portal Kombat : extension du réseau de propagande russe
 
Collecting & Temporal Analysis of Behavioral Web Data - Tales From The Inside
Collecting & Temporal Analysis of Behavioral Web Data - Tales From The InsideCollecting & Temporal Analysis of Behavioral Web Data - Tales From The Inside
Collecting & Temporal Analysis of Behavioral Web Data - Tales From The Inside
 
Design Guidelines for Passkeys 2024.pptx
Design Guidelines for Passkeys 2024.pptxDesign Guidelines for Passkeys 2024.pptx
Design Guidelines for Passkeys 2024.pptx
 
Extensible Python: Robustness through Addition - PyCon 2024
Extensible Python: Robustness through Addition - PyCon 2024Extensible Python: Robustness through Addition - PyCon 2024
Extensible Python: Robustness through Addition - PyCon 2024
 
Intro in Product Management - Коротко про професію продакт менеджера
Intro in Product Management - Коротко про професію продакт менеджераIntro in Product Management - Коротко про професію продакт менеджера
Intro in Product Management - Коротко про професію продакт менеджера
 

Training: Day Four - Struts, Tiles, Renders and Faces

  • 2. Fenix Architecture Application Container (Tomcat, Jetty) JSPs renderers Faces Jersey (REST API) Domain Model Struts Fenix Framework (STM) MYSQL
  • 3. Today Application Container (Tomcat, Jetty) JSPs renderers Faces Jersey (REST API) Domain Model Struts Fenix Framework (STM) MYSQL
  • 4. Struts Control Layer ● open-source web application framework ● uses and extends the Java Servlet API ● model–view–controller (MVC) architecture ● version 1.2.7 ● http://struts.apache.org/release/1.2.x/
  • 8. Struts Dispatching Module fenix.ist.utl.pt/publico/executionCourse.do?method=marks&executionCourseID=1610612925705 Semantic division in modules ● struts-publico.xml ● .do maps to Struts Servlet (JavaServlet API) ○ src/main/resources/web.xml
  • 9. Struts Dispatching Mapping fenix.ist.utl.pt/publico/executionCourse.do?method=marks&executionCourseID=1610612925705 Mapping between path and action ● The path is a string ○ ● “/executionCourse” Where is the mapping ? ○ Mapping with annotations (what you should use) @Mapping(module ="publico" path="/executionCourse" ) ○ struts-publico.xml (read-only, don’t create things here) <action type="n.s.f.p.A.p.ExecutionCourseDA" parameter="method" path="/executionCourse">
  • 10. Struts Dispatching Mapping fenix.ist.utl.pt/publico/executionCourse.do?method=marks&executionCourseID=1610612925705 Mapping between path and action ● Action Class ( ExecutionCourseDA.java) ● each action is module aware ● usually extends FenixDispatchAction (check outline on eclipse) ○ helper methods ■ ■ redirect ■ getLoggedPerson ■ ● getFromRequest getDomainObject(request,parameter) method execute runs always DEMO
  • 11. Struts @ Fenix Action fenix.ist.utl.pt/publico/executionCourse.do?method=marks&executionCourseID=1610612925705 ExecutionCourseDA.java public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { final String executionCourseOID = request.getParameter("executionCourseID"); final ExecutionCourse executionCourse = FenixFramework.getDomainObject(executionCourseOID); Map<> attendsMap = getAttendsMap(executionCourse); request.setAttribute("executionCourse", executionCourse); request.setAttribute("attendsMap", attendsMap); request.setAttribute("dont-cache-pages-in-search-engines", Boolean.TRUE); return mapping.findForward("execution-course-marks"); } ● Parameters ○ get values on query string ● Attributes ○ get or set state on request
  • 14. Struts Presentation Layer ● Tiles ○ templating system ○ create a common look and feel for a web application ○ create reusable view components ○ bridge to JSPs ○ module aware ● tiles-<module>-definitions.xml ○ template definitions ● tiles-<module>-pages-definitions.xml ○ fill in the template
  • 15. Struts @ Fenix Forwards ExecutionCourseDA.java public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { final String executionCourseOID = request.getParameter("executionCourseID" ); final ExecutionCourse executionCourse = FenixFramework. getDomainObject(executionCourseOID); Map<> attendsMap = getAttendsMap(executionCourse); request .setAttribute("executionCourse" executionCourse); , request .setAttribute("attendsMap", attendsMap); request .setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE); , return mapping.findForward("execution-course-marks" ); }
  • 16. Struts @ Fenix Forwards ExecutionCourseDA.java public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { final String executionCourseOID = request.getParameter("executionCourseID "); final ExecutionCourse executionCourse = FenixFramework. getDomainObject(executionCourseOID); Map<> attendsMap = getAttendsMap(executionCourse); request .setAttribute("executionCourse" executionCourse); , request .setAttribute("attendsMap", attendsMap); request .setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE); , return mapping.findForward("execution-course-marks"); } ● What is mapping.findForward(..) ?
  • 17. Struts @ Fenix Forwards ExecutionCourseDA.java public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { final String executionCourseOID = request.getParameter("executionCourseID "); final ExecutionCourse executionCourse = FenixFramework. getDomainObject(executionCourseOID); Map<> attendsMap = getAttendsMap(executionCourse); request .setAttribute("executionCourse" executionCourse); , request .setAttribute("attendsMap", attendsMap); request .setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE); , return mapping.findForward("execution-course-marks"); } ● Forwards annotation (what you should use) @Forwards({ @Forward(name = "execution-course-marks", path = "/publico/executionCourse/marks.jsp") }) public class ExecutionCourseDA extends FenixDispatchAction { … } ● ● name - forward name path - logic name for tiles
  • 18. Struts @ Fenix Forwards (Deprecated) ExecutionCourseDA.java public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { final String executionCourseOID = request.getParameter("executionCourseID "); final ExecutionCourse executionCourse = FenixFramework. getDomainObject(executionCourseOID); Map<> attendsMap = getAttendsMap(executionCourse); request .setAttribute("executionCourse" executionCourse); , request .setAttribute("attendsMap", attendsMap); request .setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE); , return mapping.findForward("execution-course-marks"); } ● struts-publico.xml <action type="n.s.f.p.A.p.ExecutionCourseDA" parameter="method" path="/executionCourse" > <forward path= "execution-course-marks" name="execution-course-marks"></forward> ………………………………………………… </action> ● ● name - forward name path - logic name for tiles
  • 19. Struts @ Fenix Forwards (Deprecated) ● tiles-public-definitions.xml <definition name= "execution-course-marks" extends="definition.public.executionCourse"> <put name="body" value="/publico/executionCourse/marks.jsp" > / </definition> ● tiles-public-pages-definitions.xml <definition name= "definition.public.executionCourse" page="/layout/istLayout.jsp"> <put name="title" value="/commons/functionalities/courseTitle.jsp"/> <put name="hideLanguage" value="true"/> <put name="symbols_row" value="/publico/degreeSite/symbolsRow.jsp" /> <put name="profile_navigation" value="/publico/degreeSite/profileNavigation.jsp"/> <put name="main_navigation" value="/publico/executionCourse/mainNavigation.jsp"/> <put name="body_header" value="/publico/executionCourse/executionCourseHeader.jsp"/> <put name="body" value="/commons/blank.jsp" /> <put name="footer" value="/publico/degreeSite/footer.jsp" /> <put name="rss" value="/messaging/announcements/rssHeader.jsp"> / <put name="keywords" value="/messaging/announcements/keywordsHeader.jsp"> / </definition> ● istLayout.jsp <tiles:insert attribute= "body" ignore="true"/> <tiles:insert attribute= "footer" ignore="true"/>
  • 20. JSPs ● JavaServer Pages (JSP) ● create dynamically generated web pages based on HTML ● HTML with behaviour DEMO publico/executionCourse/marks.jsp
  • 21. JSPs without renderers ● publico/executionCourse/marks.jsp <logic:iterate id="evaluation" name="executionCourse property="orderedAssociatedEvaluations " "> ● ExecutionCourseDA.java public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { final String executionCourseOID = request.getParameter("executionCourseID" ); final ExecutionCourse executionCourse = FenixFramework. getDomainObject(executionCourseOID); Map<Attends, Map<Evaluation, Mark>> attendsMap = getAttendsMap(executionCourse); request .setAttribute("executionCourse" executionCourse); , request .setAttribute("attendsMap", attendsMap); request .setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE); , return mapping.findForward("execution-course-marks" ); }
  • 22. JSPs without renderers ● publico/executionCourse/marks.jsp <logic:iterate id="evaluation" name="executionCourse" property="orderedAssociatedEvaluations"> ● ExecutionCourseDA.java public ActionForward marks(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { final String executionCourseOID = request.getParameter("executionCourseID" ); final ExecutionCourse executionCourse = FenixFramework. getDomainObject(executionCourseOID); Map<Attends, Map<Evaluation, Mark>> attendsMap = getAttendsMap(executionCourse); request .setAttribute("executionCourse", executionCourse); request .setAttribute("attendsMap", attendsMap); request .setAttribute("dont-cache-pages-in-search-engines" Boolean.TRUE); , return mapping.findForward("execution-course-marks" ); } ● ● name - get from request attribute or parameter with that name property - get property from object ○ ● ● executionCourse .getOrderedAssociatedEvaluations () uses Java Bean conventions id - defines bean in jsp scope
  • 23. JSPs without renderers ● simple bean example <h3><bean:write name= "executionCourse" property="name"></h3> ● executionCourse.getName() ● Tag libs ○ ○ ○ ○ <bean:*> <logic:*> <html:*> http://struts.apache.org/release/1.2.x/userGuide/
  • 24. JSPs Renderers ● integration with domain model and fenixframework ● our taglib <fr:*> ○ fr:view - display domain objects ○ fr:create - create domain objects ○ fr:edit - edit domain objects ● renderers-config.xml ○ All renderers definitions
  • 25. JSPs Renderers ● What is a renderer ? ○ java class used to produce HTML ○ Properties ■ layout ● logical name for renderer definition ■ mode ● ● input output ■ class ● rendered type ○ String ○ ExecutionCourse ○ int ■ properties ● render specific properties
  • 26. JSPs renderers-config.xml ● output renderer <renderer type= "java.util.Collection" layout="contact-list" class="n.s.f.p.r.ContactListRenderer" > <property name= "bundle" value="APPLICATION_RESOURCES" /> <property name= "defaultLabel" value="label.partyContacts.defaultContact" > / </renderer> ● input renderer <renderer mode= "input" type="j.u.Collection" layout="option-select" class="p.i.fr.InputCheckBoxListRenderer" > <property name= "eachClasses" value="dinline" /> </renderer>
  • 27. JSPs Renderers ● manageApplications.jsp <fr:view name="appsOwned" schema="oauthapps.view.apps" > <fr:layout name="tabular"> <fr:property name="classes" value="tstyle4 thcenter thcenter" /> <fr:property name="columnClasses" value="tdcenter, tdcenter, ..." /> </fr:layout> </fr:view> ● renderers-config.xml <renderer type="java.util.Collection" layout="tabular" class="p.i.f.r.CollectionRenderer" > <property name="groupLinks" value="true"/> <property name="linkGroupSeparator" value=", "/> </renderer>
  • 28. JSPs Renderers ● manageApplications.jsp <fr:view name="appsOwned" schema="oauthapps.view.apps" > <fr:layout name="tabular"> <fr:property name="classes" value="tstyle4 thcenter thcenter" /> <fr:property name="columnClasses" value="tdcenter, tdcenter, ..." /> </fr:layout> </fr:view> ● renderers-config.xml <renderer type="java.util.Collection" layout="tabular" class="p.i.f.r.CollectionRenderer" > <property name="groupLinks" value="true"/> <property name="linkGroupSeparator" value=", "/> </renderer>
  • 31. Renderers (reusable) Schemas ● manageApplications.jsp <fr:view name="appsOwned" schema="oauthapps.view.apps" > <fr:layout name="tabular"> <fr:property name="classes" value="tstyle4 thcenter thcenter" /> <fr:property name="columnClasses" value="tdcenter, tdcenter, ..." /> </fr:layout> </fr:view>
  • 32. Renderers (reusable) Schemas ● manageApplications.jsp <fr:view name="appsOwned" schema="oauthapps.view.apps"> <fr:layout name="tabular"> <fr:property name="classes" value="tstyle4 thcenter thcenter" /> <fr:property name="columnClasses" value="tdcenter, tdcenter, ..." /> </fr:layout> </fr:view>
  • 33. Renderers (reusable) Schemas ● Schemas <fr:view name="appsOwned" schema="oauthapps.view.apps"> <fr:layout name="tabular"> <fr:property name="classes" value="tstyle4 thcenter thcenter" /> <fr:property name="columnClasses" value="tdcenter, tdcenter, ..." /> </fr:layout> </fr:view> ● specify how object’s slots are rendered ● schemas-config.xml ● *-schemas.xml ○ logical separation
  • 34. Renderers (reusable) Schemas ● personnelSection-schemas.xml <schema name="oauthapps.view.apps" type="net.sourceforge.fenixedu.domain.ExternalApplication" bundle="APPLICATION_RESOURCES" > <slot name="name" key="oauthapps.label.app.name" /> <slot name="description" layout="longText" key="oauthapps.label.app.description" /> <slot name="scopes" layout="flowLayout"> <property name= "eachLayout" value="values"></property> <property name= "eachSchema" value="oauthapps.view.scope.name" ></property> <property name= "htmlSeparator" value=", "></property> </slot> <slot name="siteUrl" key="oauthapps.label.app.site.url" /> </schema> ● ● ● name ○ unique identifier type ○ schema target type slot ○ object slot to render
  • 35. Renderers (inline) Schemas <fr:view name="appsOwned" schema="oauthapps.view.apps" > <fr:layout name="tabular"> <fr:property name="classes" value="tstyle4 thcenter thcenter" /> <fr:property name="columnClasses" value="tdcenter, tdcenter, ..." /> </fr:layout> </fr:view>
  • 36. Renderers (inline) Schemas <fr:view name="appsOwned"> <fr:schema type="net.sourceforge.fenixedu.domain.ExternalApplication"bundle="APPLICATION_RESOURCES" > <fr:slot name="name" key="oauthapps.label.app.name" /> <fr:slot name="description" layout="longText" key="oauthapps.label.app.description" /> <fr:slot name="scopes" layout="flowLayout"> <fr:property name="eachLayout" value="values"></property> <fr:property name="eachSchema" value="oauthapps.view.scope.name" ></property> <fr:property name="htmlSeparator" value=", "></property> </fr:slot> <fr:slot name="siteUrl" key="oauthapps.label.app.site.url" /> </fr:schema> <fr:layout name="tabular"> <fr:property name="classes" value="tstyle4 thcenter thcenter" /> <fr:property name="columnClasses" value="tdcenter, tdcenter, ..." /> </fr:layout> </fr:view>
  • 37. Resource Bundles <fr:view name="appsOwned"> <fr:schema type="net.sourceforge.fenixedu.domain.ExternalApplication"bundle="APPLICATION_RESOURCES"> <fr:slot name="name" key="oauthapps.label.app.name" /> <fr:slot name="description" layout="longText" key="oauthapps.label.app.description" /> <fr:slot name="scopes" layout="flowLayout"> <fr:property name="eachLayout" value="values"></property> <fr:property name="eachSchema" value="oauthapps.view.scope.name" ></property> <fr:property name="htmlSeparator" value=", "></property> </fr:slot> <fr:slot name="siteUrl" key="oauthapps.label.app.site.url" /> </fr:schema> <fr:layout name="tabular"> <fr:property name="classes" value="tstyle4 thcenter thcenter" /> <fr:property name="columnClasses" value="tdcenter, tdcenter, ..." /> </fr:layout> </fr:view> ● src/main/resources/resources/ApplicationResources_pt.properties ○ oauthapps.label.app.name ="Nome Aplicação" ● src/main/resources/resources/ApplicationResources_en.properties ○ oauthapps.label.app.name ="Application Name"
  • 38. JavaServer Faces ● JavaServer Faces ○ version 1.1 ● component-based user interfaces for web apps ● servlet mapping *.faces ● faces-config.xml
  • 40. JavaServer Faces https://fenix.ist.utl.pt/publico/degreeSite/viewCurricularCourse.faces?degreeID=2761663971474 ● publico/degreeSite/viewCurricularCourse.jsp <h:outputFormat value="<h1>#{CurricularCourseManagement.degreePresentationName}</h1>" escape="false"/> ● Backing Bean ○ ● name - CurricularCourseManagement faces-config.xml <managed-bean> <description>ManagerCurricularCourseManagementBackingBean</ description> <managed-bean-name>ManagerCurricularCourseManagement</ anaged-bean-name> m <managed-bean-class>n.s.f.p.b.m.c.ManagerCurricularCourseManagementBackingBean</ managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> DEMO
  • 41. Golden Rules ● don’t create stuff in struts-*.xml ○ use annotations ■ @Mapping ■ @Forwards ● @Forward ● renderers-config.xml ○ read-only (unless you are going to create a new renderer) ● read renderers docs ○ Administrador > Frameworks > Renderers > Exemplos Renderers ● install resource bundle editor ○ https://fenix-ashes.ist.utl.pt/fenixWiki/I18NConventions
  • 42. Q&A?