Anda di halaman 1dari 22

Develop and deploy: Cloud-optimized 4GL

Which platform - 3GL, 4GL, or both - is optimal for your
data-driven cloud apps

Skill Level: Intermediate

Sean Devlin (

Senior Technical Engineer

Jan Aleman (

CEO and Co-founder

24 Jan 2011

Explore which programming language type, 3GL or 4GL, is best suited for your cloud
application needs and development environment. The authors — who have built a
successful, real-world application programming environment of their own — take you
on a journey of cloud application programming from 3GL to 4GL and back. They
demonstrate their own invention: The anatomy of an environment that combines the
best of both worlds.
Developing and deploying applications for the cloud need not be any more difficult
than for traditional environments, especially when developers select tools which
provide cloud-enabling functionality right out of the box.

For data-driven applications, developers usually choose between two types of


• Fourth-generation programming languages (4GL) or

• Third-generation programming languages (3GL).

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 1 of 22

Each has its place in the technology landscape and each has enjoyed periods of
relative popularity throughout the history of software development.

See how and why the pendulum has shifted from 3GL to 4GL and, more recently,
back to 3GL. Moreover, explore the challenges and choices developers face today
with respect to developing applications for the cloud and get our own unique take on
the challenge — a platform which combines the best aspects of 4GL tools and 3GL

The pendulum swings

The 1990s saw the emergence of 4GL tools (such as FoxPro, Delphi, Progress,
Oracle Forms, Informix 4GL) which gained tremendous popularity because they
greatly reduced the overall complexity of application development and maintenance
while increasing developer productivity.

4GL tools typically automate the foundation layers common to most applications,
such as data access, data binding, the assembly of native GUI components, etc.
Relieved of these concerns, developers are free to focus on the user experience and
specific business logic, resulting in a faster development cycle and leaner, simpler
code base. A bonus is that this also requires a smaller, less specialized team to
maintain it, making it a clear win over 3GL platforms.

However, toward the end of the 1990s, 4GLs began to show their limitations. Amid
the rise of the Internet, there was a paradigm shift which marked the departure of
"fat clients," client-side processing, and native windowing interfaces in favor of
server-side computing and a general decoupling of business logic from the
presentation layer.

Interestingly, none of the classic 4GLs presented an answer to the new paradigm of
Internet-based computing, so many developers turned once again to 3GL platforms,
primarily Java™ and C#, to meet a growing demand for web applications.

Today, developers are faced with many new challenges as new technologies have
facilitated another paradigm shift — the compelling demand for software as a service
(SaaS) and cloud-based applications. A modern SaaS application requires
web-based delivery that is secure, elastic, and fault-tolerant, combined with the
robust user experience of a native application. Meeting this demand requires a new
bag of tricks. A short list includes:

• Full Ajax support for interactivity

• Cross-browser support
• Security

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 2 of 22 developerWorks®

• Multi-tenancy
• Clustering and load-balancing
• Automated deployment
• Production life cycle (versioning)
• Strong Linux® support
Given these added requirements, developers face tough choices and the argument
of 3GL vs. 4GL takes on new dimensions. So let's take a closer look at each choice.

The 3GL approach to the cloud

More on 3GL
The second generation of programming languages brought logical
structure to software; the third-generation programming language
(3eGL or high-level language) refined that by making the languages
more programmer-friendly. This included such features as improved
support for aggregate data types and expressing concepts in a way
that focused on the programmer and not the machine (like
eliminating the need to state the length of multi-character string
literals in Fortran). In a 3GL, the computer handles a lot of the
non-essential details for the programmer. Most 3GLs support
structured programming

Fortran, ALGOL, and COBOL are early (1950s) examples of 3GLs;

today, the popular 3GLs are C, C++, C#, Java, Delphi, and Python.

The basic problem of 3GLs is one of productivity and maintenance. In 3GL

environments, developers are responsible for the building and maintenance of the
foundation layers of an application. While this approach provides the granularity to
deal with web-based environments, developers pay a price in lost productivity and
increased complexity.

The productivity problem is greatly compounded when building for the cloud. The
added requirements translate into additional foundation layers to build and maintain.
The code base can become so large that the majority of it deals only with the
plumbing and very little deals with actual application-specific functionality.

Consider just some of the aspects of a cloud-based application from back to front:

• Developers must write code to implement the data access layer, including
optimizing SQL queries, caching results to optimize performance, without
compromising an application's footprint.
• They must implement the presentation layer. For browser-based systems

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 3 of 22

this involves generating markup, CSS, and client-side JavaScript™.

• Additionally, developers must build and maintain the Ajax layer as well,
binding browser DOM events to client-side scripts which invoke
server-side business logic, all of which must be tested and debugged for
every build iteration, browser, and mobile device.
• Developers must also seriously consider cloud topics such as elasticity,
adding/removing capacity, clustering technologies, load-balancing, etc.
And so far we haven't even mentioned the real task at hand — developing
application-specific functionality and articulating effective user experiences.

The reality is that 3GL teams must be so specialized and focused on these
foundation layers of the application that their ability to deliver core functionality can
be compromised.

A common solution to this problem is the use of third-party frameworks — essentially

reusable code and classes which provide higher-level abstractions of specific
functionalities. While this approach reduces the burden of development, it is not
without its own drawbacks.

Software teams must evaluate, integrate, maintain compatible versions, train, etc.
For example, to cloud-enable a modern ERP application would require some 10-30
frameworks to address the foundation layers.

So given the increased burden of development and maintenance, it could be

beneficial to consider 4GLs again. Let's take a closer look at the reasons why classic
4GL tools have not been well suited for cloud-based applications and what can be
done about it.

The classic 4GL approach to the cloud

More on 4GL
A fourth-generation programming language (4GL) is one
designed with a specific purpose in mind, like development of
compute-intensive scientific or commercial business software.
4GLs, roughly introduced in the 1970s, attempted to refine 3GL by
offering higher abstractions and statement power. In an attempt to
eliminate some of the more error-prone, slower 3GL development
methods and provide more rapid software development, 4GLs
added higher-level methodology that could generate the equivalent
of extremely complicated 3GL instructions with fewer errors. One
complaint of 4GLs is that they can often generate code that is
inelegant in its structure and not easy to maintain.

From an architectural point of view, a 4GL is a set of pre-selected, well-orchestrated

frameworks that address the foundation layers of an application. The 4GL vendor

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 4 of 22 developerWorks®

assumes the burden of maintenance for these frameworks and the developer is free
again to focus on the user interface and core business logic. To facilitate this, 4GLs
typically provide graphical editors to visually design the GUI and proprietary scripting
languages and APIs to tie user-driven events to specific business logic.

The upside is simple — increased productivity and reduced complexity. The price
paid for this is flexibility. Unless 4GL vendors provide extensibility options and even
source code, developers must operate within the limits of the tool. This can be
frustrating because classic 4GLs often use proprietary languages, protocols, and
even databases, making integration and extension even more difficult. However, in
the context of cloud applications, the biggest limitation is scalability.

The biggest limitation: Does it scale?

The limited scalability in classic 4GL tools is rooted in their underlying architecture.
Traditional 4GLs use a two-tier design. This essentially means that native clients
connect directly to a database. This setup is optimized for a smaller install base
which runs on a local area network (LAN). 4GL tools cannot support a larger
concurrency because each additional client requires its own database connection;
therefore, system-wide performance will degrade as more users access an
application and burden the database with too many connections.

4GL tools cannot be run over a wide area network (WAN) for reasons of security and

• Organizations cannot expose a database through a corporate firewall.

• And two-tier systems perform poorly when a fat client must connect to a
database over the WAN.
The fake cloud approach

A common way of overcoming this WAN-incompatibility involves a "fake cloud"

approach. This typically involves video streaming technologies, such as Terminal
Services, Citrix, or VDI. A native client is run inside the LAN and its video image is
projected over the WAN to a remote client.

This approach overcomes the security problems of deploying two-tier systems over
the WAN by replicating a native client to a remote user. Moreover it is a simple
solution because there is no need to modify the existing application.

Still, the fake cloud approach does not address the scalability issues inherent in a
two-tier system. It doesn't reduce the burden of database concurrency and the
required technologies are often cost prohibitive. In short, it is a quick-and-dirty

At the end of the day, two-tier systems cannot accommodate the requirements of

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 5 of 22

cloud applications.

This is not the triumph of browser over native, though

This is not to say that native clients are a thing of the past and that all new
applications must be browser-based. While most developers consider "cloud" to
automatically imply browser-based applications, native clients can still be deployed
over the WAN using n-tier technology.

In fact, native clients are required by certain tasks for which browsers simply don't
have access, such as interacting with client-side file systems, hardware, and
software. But classic 4GLs do not have the proper architecture to deliver this "smart
client" technology.

So is there the possibility of having, in a single platform, both the productivity and
simplicity of a classic 4GL and the flexibility and scalability of a cloud-optimized set
of 3GL frameworks?

The modern 4GL approach to the cloud

Developers have a lot to consider when selecting a new platform; let's run through
some requirements that a modern, cloud-optimized 4GL should meet.

• A next-generation 4GL platform should address the demand for

server-side computing by providing n-tier architecture. This design
pattern introduces an application server in the middle tier which brokers
connections between clients and the data tier. This approach dramatically
increases the capacity for high concurrency because database
connections are pooled and shared amongst clients. Moreover, n-tier
architecture allows for applications to be securely deployed over the WAN
while the database can remain securely behind the corporate firewall. The
application server layer should also be optimized for cloud deployments to
include out-of-the-box support for security, multi-tenancy, clustering and
load-balancing, automated deployment, production life cycle (versioning).
• A modern 4GL platform should also provide robust options for both
native client and browser-based deployments. If the platform can
provide these options from a single code base without the need to rewrite
anything, then development time and maintenance are again greatly
A true smart client runs over the Internet using standard protocols —
HTTP and SSL to avoid proxy and firewall issues. It is as responsive as a
locally run application or a browser-based application. It deploys without
installation to any operating system and automatically updates itself and
provides integration with local file systems, hardware, and software.

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 6 of 22 developerWorks®

• For robust browser-based deployments, a 4GL should support all

modern browsers without plug-ins or proprietary technologies:
• The platform should dynamically generate the markup, CSS, and
client-side JavaScript.
• The Ajax layer should be fully automated in a way that developers
can easily capture GUI events and bind them to business logic which
is executed without refreshing the page.
• There should be a clean separation of business logic and
presentation. Application-specific code should not be exposed in the
browser, thus improving security and performance and perhaps most
importantly, providing the ability to debug a browser-based application
as easily as a native application.
• Cloud-optimized platforms should support industry standards and
remain agnostic in terms of the database vendor and operating
system. For example, applications may be developed and tested on
Windows™ machines using a MSSQL database while running in
production in the cloud on a Linux instance with a Postgresql database.
Therefore, a modern 4GL platform should support any relational database
and operating system.
• The next-generation 4GL platform should provide a robust,
extensible integrated development environment (IDE). Contrary to
classic 4GLs, new platforms should not introduce any proprietary
languages or protocols. They should provide support for standard version
control systems such as Subversion (SVN) and the ability to
auto-complete, document, debug, refactor, profile, and unit test all code.
We've covered a number of requirements and it has probably become clear that a
modern, cloud-optimized 4GL looks quite a bit different from the classic setup. But
the idea is the same: Provide tools to greatly enhance developer productivity while
reducing the complexity of deployment and maintenance.

Now let's take a look at a real-world platform which has reinvented 4GL to address
these requirements.

A novel combination approach to the cloud

The approach we use that mixes 3GL and 4GL features addresses some of the
limitations of classic 4GLs, allowing you to:

• Develop complex business applications in less time.

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 7 of 22

• Deploy both locally and over the Internet.

• Provide scalable smart client technology.
• Provide a platform which is based on standards.
The platform we've developed, a development, deployment, and integration suite,
offers developers a single code base from which to rapidly build and deploy for
native client and browser applications, both for the public and on-premise cloud and
internal systems (Figure 1).

Figure 1. The architecture

The remainder of this article describes this architecture and provides a simple
example of a development environment that uses features of both 3- and 4GLs for
deploying applications into the cloud.

The n-tier deployment architecture

More on n-tier architectures

N-tier architectures (also known as multi-tier architectures) are
client-server architectures in which the presentation, the application
processing, and the data management are logically separate
processes. For example, an application that uses middleware to
service data requests between a user and a database employs
multi-tier architecture. N-tier application architecture delivers a
model for developers to use to create flexible, reusable applications;
by breaking applications into tiers, developers only have to modify
or add a specific layer rather than rewrite the entire application.

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 8 of 22 developerWorks®

The most used n-tier architecture structure is the three-tier one.

The Servoy environment employs an n-tier architecture which includes

cross-database support, and advanced application server, and a variety of
deployment options in the presentation layer. Let's look at these layers in greater

Data access layer

We provide a data access layer (bottom of Figure1) which is database agnostic. It
uses a combination of JDBC, Hibernate, and Servoy extensions to make this
database independent and as such, it will run on IBM DB2®, Microsoft® SQL
Server, Oracle®, Sybase, MySQL, Postgresql, and others. Developers don't need to
write specific code in order to do this.

Application server layer

The Servoy Application Server (second layer from bottom) is an advanced
application container which manages data transactions on behalf of connected
clients. Some of its many duties include:

• Deployment and life cycle management

• Enforcing security
• Generating HTML, CSS and client-side JavaScript
• Two-way communication with the connected clients
• State management
• Clustering
• Load balancing
• Database connection pooling
• Data broadcasting
Business rules layer
This layer (third from bottom) is responsible for executing all logic. This can be both
client- or server-side.

With the web client it is always server-side: Deploying business rules to a browser
exposes them to the end user, there is less control over the execution and
performance, and debugging is very complex ... this is all avoided by keeping them
on the server side.

The smart client can run code both locally and server-side. By running code locally
the server is offloaded and often interactivity is increased. Expensive operations or

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 9 of 22

data-intensive operations can still be run server-side using the headless client. A
batch processor is available to schedule tasks to run server-side either on an
ongoing basis, a scheduled time interval, or at a certain time.

Presentation layer
This layer (shown on top) is used to visualize applications to end users. For native
clients, they will render in the look and feel of the native operating system. For
browser applications, our platform generates the markup, CSS, and JavaScript, as
well as Ajax, enabling the user interfaces for a dynamic experience. The Apache
Wicket framework is used for this and we've contributed heavily to this open source

Our platform also has the capability to publish web services acting as a middleware
server for HTTP, SOAP and REST connections.

The development environment

Servoy: Rapid deployment with a single code

The Servoy platform consists of:

• Servoy Developer, a powerful IDE used to design,

develop, test, and debug applications.

• The Servoy Application Server lets you deploy apps as a

zero-deployment, native smart client, or a browser-based
web client.

• The Servoy Smart Client is a small 2MB Java application

that uses Java WebStart technology for a
zero-deployment model.

• The Servoy Web Client is a secure, scalable, pure

HTML/CSS/JavaScript client that requires no plug-ins to
run and supports Ajax.

• The Servoy Headless Client allows you to execute your

app's data and business logic via JSP pages, Servlets
and Java applications.

All work in the Servoy platform can be done from a single code
base. This video will let you understand Servoy in less than a

Our development environment includes a what-you-see-is-what-you-get (WYSIWYG

) graphical form builder. This is typical of most 4GL platforms and offers a productive
edge over 3GL environments which require a lot of code to generate GUI

The environment also provides sophisticated data modeling tools, a

scripting/debugging engine, and robust APIs which like classic 4GLs are simple in

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 10 of 22 developerWorks®

nature, but provide the sophistication needed to build modern applications. An entire
application is stored as many simple metadata text files which are easily
synchronized with a developer's preferred version control system.

The development tool ships as a plug-in for the open-source, industry standard
Eclipse integrated IDE.

The basic unit of development in our platform is called a Solution. Solutions

encompass all forms, scripts, and data-modeling elements for a single application.
However Solutions can contain other Solutions as Modules. By using a modular
approach, developers can reuse components and code across multiple applications.

Connecting to data
Our platform connects to any relational database using Java Database Connectivity
(JDBC) technology. To establish a connection, one needs only the JDBC driver and
the connection parameters.

Developers can design their database from within our platform's IDE or through the
DB admin tool of their choice. At runtime, the platform will manage all database
transactions, issuing SQL queries, caching and binding results to forms and API
objects. No prior experience with SQL is required.

Figure 2. Database connections are visually configured in a designer

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 11 of 22

Figure 3. Once a data connection is configured, the platform introspects the

table structure

Building forms
Developers also don't need to write any code to generate GUI elements; the platform
provides a comprehensive palette of components which can be placed on a form.

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 12 of 22 developerWorks®

This includes all standard controls (buttons, text-fields, combo boxes, radios) and
special controls (type-ahead fields, tree-view controls, contextual pop-up menus).

Developers are also free to use image media and icons in their solutions. All
components can be styled using CSS, making it easy to re-skin an entire application
by making adjustments to the style sheet. The platform also supports object-oriented
design through form inheritance, allowing components and form-level code to be
inherited and overridden.

A form is bound to a specified database table and all data controls within can be
used to display and edit the table's columns without writing code or SQL. But our
form is not limited to working within a single table; a single form can display and edit
data for many tables through relation objects.

Figure 4. User can specify data source, CSS, etc., in the forms wizard

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 13 of 22

Figure 5. Forms are modified through the form editor

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 14 of 22 developerWorks®

Modeling data relations

A relation is defined between two tables by specifying key columns that link the
tables, most commonly regular relations such as customer-to-orders. However, our
platform is designed to support relations which use many keys and many operators
(other than =). Relations can also use scripting variables as part of their key
definitions, thus making a relation dynamic, such as customers-to-orders that
occurred within seven days of a specified date.

Figure 6. Relations are configured in the Relation Designer

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 15 of 22

A relation is defined graphically without writing code and it can serve many purposes
in an application:

• First of all, it can be used to display/edit data on a form.

• Additionally, entire forms can be shown within other forms through a
In this case, the record set of the contained form will be constrained by the relation.
So following our example, you can show a list of customer's recent orders within the
customer form by adding a second, orders, form through a relation. By continuing
this process, you can build a sophisticated, fully data-driven user interface without
writing any code or SQL.

Figure 7. You can place an entire form within a form using a relation

Figure 8. Customer orders displayed at runtime on a browser form

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 16 of 22 developerWorks®

Finding and filtering data

It is possible to perform sophisticated data searches without writing SQL. To
accomplish this, our platform provides a feature called find mode. When in find
mode, all of a form's data-bound fields can be used as search criteria. When the
search is run, then it will generate the SQL needed to return the records. The criteria
may be entered manually by the user or programmatically. Find mode on our
platform can accommodate logical a wide array of operators, logical operators,
wildcard string searches, etc.

Figure 9. Three lines of code and no SQL for sophisticated data search

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 17 of 22

Implementing business logic

Our environment uses JavaScript as its primary programming language; it exposes a
robust, self-documented JavaScript API with which to accomplish most programming

Traditionally scripting languages have always provided a great amount of flexibility

and productivity over their compiled counterparts, but due to their interpreted nature,
they also have had some downsides:

• difficult to debug
• performance issues
• weak and strong typing
• code refactoring
We've addressed these issues by implementing and extending Mozilla's Rhino
project. Rhino is a Java-based JavaScript engine. Under the hood, every JavaScript
function in our platform maps to an underlying Java class. This approach provides
the performance of pure Java code and the productivity and reduced complexity of
JavaScript. Moreover, developers are free to code in Java if they are comfortable
with it. This is typically useful when integrating with a third-party Java library.

One of the bigger issues encountered was debugging. To solve this, we co-founded
an Eclipse project named DLTK (dynamic language toolkit) which provides tools for
the debugging of dynamic languages. Recently this has been extended to also
support strong typing which with some other extensions allows for advanced code
refactoring. More information can be found in Resources.

Figure 10. This JavaSCript method is bound to the selected text field's
design-time, on-data-change event

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 18 of 22 developerWorks®

Figure 11. The JavaScript code that handles the on-data-change event

Figure 12. At runtime, the JavaScript method is executed when the field's data

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 19 of 22

The deployment environment

Once developed, an application can be easily deployed to the application server we

use. The process takes only a few seconds. A project can be exported from the
development environment and imported into the application server in a few clicks.

When the import is complete, the application is available to the world as a

cross-platform, self-installing native client and as a cross-browser web application.
Subsequent build iterations follow the same steps and the application server will
manage numerous production versions of the same application with the ability to roll
back and roll forward with a single click.

Moreover, the import process will also optionally update the data model, so that
schema changes made against a development database will be pushed into
production automatically.

With application life cycle management greatly simplified, developers are free to
pursue a more aggressive build schedule and keep their users happy.

In conclusion

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 20 of 22 developerWorks®

Developing applications for the cloud shouldn't be too different or more difficult than
when you develop them for a more traditional environment.

In this article, we've provided you with a glimpse at how cloud application
development can work on a 3GL platform, on the classic and modern versions of a
4GL platform, and then we've detailed the inner workings of our own platform, one
designed to leverage the cloud-oriented best practices of both 3- and 4GL.

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 21 of 22

• Learn more about the Servoy development environment and application server.
• Servoy is a co-founder of the DLTK (dynamic language toolkit) project at
Eclipse, tools for the debugging of dynamic languages.
• In the developerWorks cloud developer resources, discover and share
knowledge and experience of application and services developers building their
projects for cloud deployment.
• The next steps: Find out how to access IBM Smart Business Development and
Test on the IBM Cloud.
Get products and technologies
• See the product images available on the IBM Smart Business Development and
Test on the IBM Cloud.
• Join a cloud computing group on developerWorks.
• Read all the great cloud blogs on developerWorks.
• Join the developerWorks community, a professional network and unified set of
community tools for connecting, sharing, and collaborating.

About the authors

Sean Devlin
Sean manages Cloud Services for Servoy and is a frequent speaker at
technology conferences in the United States.

Jan Aleman
Jan is a frequent speaker at SaaS and cloud computing conferences
internationally. He is secretary of Eurocloud, a European cloud
consortium, and member of the SaaS~Cloud steering committee for the
ICT~Office, a trade association of IT, telecom, and Internet companies
in the Netherlands.

Develop and deploy: Cloud-optimized 4GL applications Trademarks

© Copyright IBM Corporation 2011. All rights reserved. Page 22 of 22