Chapter 1. Development Best Practices

Oracle Application Express (APEX) makes it extremely easy to quickly prototype and develop a web application. However, as a software developer, you're probably aware that speed of development is only one of a number of criteria that will contribute to the perceived success (or failure) of your project.

Of course, perceptions about a project's success can vary. For example, the people who encounter a typical project might include developers, testers, managers, production support, and end users. The developers may feel that the project was a success because development went quickly, Production support might feel like the project was a failure because no one has a clear strategy for performing application upgrades. The end users may dread using the application because it runs incredibly slowly. Clearly, for the project to be considered a success, you need to satisfy the expectations of all these people (or as many as you reasonably can). Ideally, you should strive for an application that has the following characteristics:

  • Easy to develop

  • Easy to deploy and upgrade

  • Easy to maintain and debug

  • Enjoyable for end users to use

  • Fast enough for the users' requirements

  • Stable from the end users' perspective

  • Secure enough to protect your data from unauthorized access

You should never end up feeling like developing, deploying, maintaining, or (even worse) using the application is seen as a chore. Each of these areas can often benefit from the adoption of some best practices to ensure that everyone involved sees your application as a success.

Chapter 1 is the best place to introduce best-practice techniques, since they should form the foundation of every significant development project you undertake. You can certainly create applications without using any of the techniques we'll discuss, but adopting techniques like these will make your job as a developer easier, and your applications will be considerably more successful.

APEX Installation Decisions

This book will not cover the details of installing APEX version 4.0, since that information is already bundled with the product, as well as discussed in detail in several online resources. It is worth mentioning, however, that APEX 4.0 supports another connection type via the Oracle Application Express Listener. The APEX Listener is a Java-based web server that is certified to run under Web Logic, Tomcat, OC4J with Oracle WebLogic Server, OC4J, and Oracle Glassfish.

Indeed, many people enjoy using APEX without bothering with installation, either because someone else has installed it for them or they are using a hosted environment (such as the public Oracle apex.oracle.com site or one of the commercial providers such as Shellprompt). Others use Oracle Database Express Edition (XE), a free edition of the database that includes a preinstalled version of APEX.

However, if you are installing APEX, one important decision is which tablespace to use for the product. The installer usually defaults to installing APEX into the SYSAUX tablespace, and if you're using the 11g version of the database, the SYSAUX tablespace will be selected by default. I highly recommend that instead of using SYSAUX you create a dedicated tablespace that you'll use specifically for the APEX database objects and metadata. By using a dedicated tablespace, you can gain a far greater degree of control and flexibility over the administration of the APEX environment. For example, should it become necessary to recover the tablespace using point-in-time recovery from an Oracle Recovery Manager (RMAN) backup, you'll be confident that you haven't affected any other database components, like AWR (which may not be the case if you install into SYSAUX).

Installing into a separate, dedicated tablespace also allows the database administrator (DBA) to make decisions about where that dedicated tablespace should be stored on disk (to reduce contention), control the storage growth of the tablespace, and perhaps also take advantage of advanced Oracle features, such as transportable tablespaces to quickly move the tablespace to another database instance.

Application Development Considerations

The decisions related to how to create and organize your application within the APEX and database environment will greatly affect how easily you'll be able to deploy and migrate your application later on. By structuring your development environment in a logical and organized way, you'll encounter far fewer problems when your application needs to be deployed or updated to your live environment.

Users and Administrators

When APEX is installed, an Application Express instance administrator is created. You can connect to APEX as this instance administrator in two ways:

  • Connect to one of the following URL's:

    • PL/SQL Plugin: http://server:port/pls/apex/apex_admin

    • APEX Listener or EPG: http://server:port/apex/apex_admin

      Use the username ADMIN and the password you used when you installed the product.

  • Connect to the same URL you'd use to log into any workspace, such as

    http://server:port/pls/apex/apex_login, and use INTERNAL as the workspace and ADMIN as the username with the password you used when you installed the product.

Including the instance administrator, four different types of users exist for APEX:

Application Express instance administrator:

This is the user you'll use to administer the APEX installation. The instance administrator can connect only to the INTERNAL workspace to perform administration tasks, such as creating workspaces and users, monitoring activity, and managing the APEX service. Instance administrators can't create any applications themselves; they must create workspaces and other users for applications to be created. The instance administrator can create workspace administrators, developers, and built-in users for any of the workspaces.

Workspace administrator:

A workspace administrator is responsible for the administration of a particular workspace. As a workspace administrator, you are able to create developers and users for that workspace, and create applications. Workspace administrators can also log into any application within the same workspace that uses APEX account credentials.

Application developer:

Application developers are created within a particular workspace by workspace administrators. They can create and maintain an application within that workspace. They can't log in to other workspaces, but they are able to log in to any application within the same workspace that uses APEX account credentials.

Application user:

Application users can take two forms. They can be created and managed within the APEX environment and are then known as built-in users (or cookie users). Alternatively, they can be created and managed outside the APEX environment; for example, they could be stored within a database table or as part of a Lightweight Directory Access Protocol (LDAP) directory. Built-in users are able to log in to any application within the same workspace that uses APEX account credentials.

For small projects with a single developer, it is quite possible to perform all application development as the workspace administrator. However, for any development that uses two or more developers, it's best to create a specific developer account for each physical developer, since this lets you use features such as page-locking, as well as track changes to the application at the developer level.

Although the workspace administrator could be one of the physical developers, a better idea is to create a developer account to use for development. Use the workspace administrator account only when necessary to perform administration duties.

Workspaces and Schemas

When you create an application in APEX, you must select a schema as the default parsing schema. In other words, if you built a report that issued a query like this:

select empno, ename from emp;

the query would use the emp table in the schema you selected as the parsing schema when you created your application. If you wanted to access an object in a different schema, you could prefix the object name with the schema name, like this:

select empno, ename from payroll.emp;

Because the application will be executed using the parsing schema (and its privileges), accessing objects in other schemas requires that the appropriate privileges are granted to the parsing schema. Objects in other schemas can also be accessed via synonyms or a view, which effectively hides the schema and enables you to reference the object without needing to specify the schema name yourself.

Choosing a Parsing Schema

The schemas assigned to the workspace you are currently logged into define the choice of schemas that can be used as the parsing schema. When you create a workspace (as an APEX administrator), you must specify whether to use an existing schema or create a new one, as shown in Figure 1-1. If no other schemas are assigned to the workspace, you will be able to select only this schema as the parsing schema when you create your application.

Creating a new workspace

Figure 1.1. Creating a new workspace

This means that if you already have an existing schema with a lot of objects you'd like to access, you can select that schema. Then, any applications that are created within the schema will be able to access those schema objects directly. This way, you can create an application in APEX that provides a front end to existing data very quickly.

Although you can select only a single schema during the provisioning of the workspace, extra schemas can be assigned to the schema later on. After these additional schemas have been assigned to the workspace, they are available to workspace developers to use as the default parsing schema when they create an application within that workspace.

If you choose to create a new schema during the provisioning of the workspace, a new tablespace and corresponding datafile will be created for that schema automatically. The disadvantage is that the tablespace and datafile will have nondescriptive names, such as APEX_1400423609989676 and APEX_1400423609989676.dbf. Moreover, if you later decide to remove the workspace, that tablespace and datafile will not be deleted. If you regularly provision and delete a lot of workspaces, you can end up with many tablespaces and datafiles cluttering up your disk (and perhaps being unnecessarily included in your backups).

For small developments or evaluation, it may be fine to create a new schema through the APEX wizard. However, from a maintenance point of view, this approach often increases the difficulty in correlating schemas, tablespaces, datafiles, and workspaces because of the nondescriptive names. While this may not be a primary concern to you as a developer, it can be critical to how quickly the DBA is able to restore your schema from a backup if necessary.

Generally, for larger developments, if you are not using an existing schema, you may find it beneficial to manually create the tablespace and schema yourself, using a tool such as Enterprise Manager. For example, you can create a tablespace called APEXDEMO, which has a single datafile named APEXDEMO01.dbf that's allowed to grow to 2GB. You can then create a user APEXDEMO that has the APEXDEMO tablespace as its default tablespace. Figure 1-2 shows how the schema would look after being created in Enterprise Manager.

Creating a schema in Enterprise Manager

Figure 1.2. Creating a schema in Enterprise Manager

You could now create a workspace named APEXDEMO and select the APEXDEMO schema you just created in Enterprise Manager, as shown in Figure 1-3. This naming scheme ties together your workspace with the underlying schema and related tablespace and datafiles. If you should accidentally drop some tables (forgetting for the moment about the recycle bin in Oracle Database 10g / 11g), you can use RMAN to recover them easily, since their schema and tablespace will be obvious.

Creating a workspace using an existing schema

Figure 1.3. Creating a workspace using an existing schema

Although the APEX administrator can view reports that show which schemas and tablespaces particular workspaces are using, adopting a sensible naming convention makes it easier to get this information. For example, the DBA could look at a tablespace called APEXDEMO and be able to understand the purpose of that tablespace, which would not be clear from a generic tablespace name like APEX_1400423609989676.

Note

Naming and coding standards can be extremely subjective. For example, some people may prefer to name the tablespace as APEXDEMO_TS while others prefer TS_APEXDEMO. If you already have an existing policy that details how you should name database objects, it makes sense to adopt that same policy for your development with APEX. If you do not currently have a policy in place, you should consider adopting one. The standards policy you use should be detailed enough to aid you in your work, but not so draconian that it actually hinders you.

Once the workspace is provisioned, additional schemas can be assigned to it. For example, you can create an APEXDEMO_TEST schema in Enterprise Manager, log in as the Application Express instance administrator, choose Manage Workspaces

Creating a workspace using an existing schema
Adding a schema to a workspace

Figure 1.4. Adding a schema to a workspace

Controlling Access to New Schemas

Now the workspace administrator can specify which application developers can use the new schema (or, indeed, any of the assigned schemas). Figure 1-5 shows an example of a new developer account being created. By default, the developer will be able to access both schemas (APEXDEMO and APEXDEMO_TEST), since both schemas have been assigned to this workspace.

Creating a new developer with access to all assigned schemas

Figure 1.5. Creating a new developer with access to all assigned schemas

When this developer logs into the workspace, he or she can see the list of schemas that are available by clicking on the Administration

Creating a new developer with access to all assigned schemas
Schemas available to the developer

Figure 1.6. Schemas available to the developer

Selecting a default parsing schema for an application

Figure 1.7. Selecting a default parsing schema for an application

Creating Workspaces

Generally, workspaces should be used to group together applications that are related. In other words, if you have a number of developers collaborating on applications that are related to each other and operate on the same data and schemas, ideally you'd create a workspace specifically for those applications.

It is possible, using multiple schemas assigned to a workspace, to establish a development/testing/live environment all within the same workspace. For example, you could create a single workspace that has a schema for development, a schema for testing, and a schema for live objects and data. You could then have three copies of your application: one pointing to the development schema, one to the testing schema, and one to the live schema. However, with this approach, it is all too easy to become confused about which schema you are operating in, with potentially disastrous effects.

It's best to create separate workspaces for development, testing, and live environments. This forces you to specifically log in to an environment, and helps to minimize the risk of making application or schema changes in the wrong environment. The APEX environment helpfully displays which workspace you are logged in to (and which user you are logged in as), so it definitely pays to double-check this information before you perform any drastic operations.

You also need to consider how the development, testing, and production environments will be staged. You may have each environment running on a different database instance (for example, a development instance, a testing instance, and a production instance). If you have only one database instance, it is possible to have the three (or two if you choose to forgo the testing environment!) environments all installed on the same database instance.

Application Deployment

An APEX application consists of three main components:

  • The APEX application itself

  • Any external images and files (such as CSS files), stored in the web server file system

  • Database objects and data contained in the schema

The definition of the application itself—that is, the metadata that represents the pages, branches, processing logic, and so on—is stored in the schema that was created when the product was installed. This is quite distinct from the database objects on which your application performs operations, which are the database objects that reside in the parsing schema of your application and any other schemas that your application accesses.

In order to deploy your application from one database to another (where APEX has already been installed), you need to deploy the application definition as well as any database objects on which it is depends. You also need to deploy any static files (CSS files, images, and so on).

Deploying Workspaces

When moving an application from one APEX instance to another (instance here refers to a database with the APEX software installed), you need to have created the workspace on the destination instance. The instance administrator can choose to either create the workspace manually or export the existing workspace from the source instance. Exporting the workspace creates a file containing the SQL needed to re-create that workspace, and the instance administrator can then import that SQL file to the destination instance. Figure 1-8 shows the APEXDEMO workspace being exported. Note that you can specify a file format to use (either UNIX or DOS), which will determine the newline sequence used in the file.

Exporting a workspace

Figure 1.8. Exporting a workspace

The advantage of using a workspace export file is that this also re-creates all the users and developers created within that workspace, so it's a quick way of migrating all the existing users (and their permissions) to a new server. It also demonstrates an advantage of using the built-in users. If you use another method to authenticate your users, you'll need to handle their migration manually. Listing 1-1 shows the contents of the file that is created by exporting the APEXDEMO workspace.

Example 1-1. Contents of a Workspace Export File

set serveroutput on size 1000000

set feedback off
-- Company, user group and user export
-- Generated 2006.07.17 12:58:25 by ADMIN
-- This script can be run in sqlplus as the owner of the Oracle flows engine.
begin
   wwv_flow_security.g_security_group_id := 1635127392255802;
end;
/
----------------
-- W O R K S P A C E
-- Creating a workspace will not create database schemas or objects.
-- This API will cause only meta data inserts.
prompt Creating workspace APEXDEMO...
begin
wwv_flow_fnd_user_api.create_company (
  p_id                      => 1635220613255830,
  p_provisioning_company_id => 1635127392255802,
  p_short_name              => 'APEXDEMO',
  p_first_schema_provisioned=> 'APEXDEMO',
  p_company_schemas         => 'APEXDEMO:APEXDEMO_TEST'),
end;
/
----------------
----------------
-- G R O U P S
--
prompt  Creating Groups...
----------------
-- U S E R S
-- User repository for use with apex cookie based authenticaion.
--
prompt  Creating Users...
begin
wwv_flow_fnd_user_api.create_fnd_user (
  p_user_id      => '1428202658421752',
  p_user_name    => 'ADMIN',
  p_first_name   => '',
  p_last_name    => '',
  p_description  => '',
  p_email_address=> '[email protected]',
  p_web_password => '1E6287A65491647783288E3C9E1A87D1',
  p_web_password_format => 'HEX_ENCODED_DIGEST_V2',
  p_group_ids    => '',
  p_developer_privs=> 'ADMIN:CREATE:DATA_LOADER:EDIT:HELP:MONITOR:SQL',
  p_default_schema=> 'APEXDEMO',
  p_account_locked=> 'N',
  p_account_expiry=> to_date('201011011826','YYYYMMDDHH24MI'),
  p_failed_access_attempts=> 0,
  p_change_password_on_first_use=> 'Y',
  p_first_password_use_occurred=> 'N',
  p_allow_app_building_yn=> 'Y',
  p_allow_sql_workshop_yn=> 'Y',
  p_allow_websheet_dev_yn=> 'Y',
  p_allow_team_development_yn=> 'Y',
  p_allow_access_to_schemas => ''),
end;
/
begin
wwv_flow_fnd_user_api.create_fnd_user (
  p_user_id      => '1437223003555234',
  p_user_name    => 'TGF',
  p_first_name   => '',
  p_last_name    => '',
  p_description  => '',
  p_email_address=> '[email protected]',
  p_web_password => 'D72DA6B4C47475A502A29CA604ACCCBC',
  p_web_password_format => 'HEX_ENCODED_DIGEST_V2',
  p_group_ids    => '',
  p_developer_privs=> 'CREATE:EDIT:HELP:MONITOR:SQL:MONITOR:DATA_LOADER',
  p_default_schema=> 'APEXDEMO',
  p_account_locked=> 'N',
  p_account_expiry=> to_date('201011011848','YYYYMMDDHH24MI'),
  p_failed_access_attempts=> 0,
p_change_password_on_first_use=> 'N',
  p_first_password_use_occurred=> 'N',
  p_allow_app_building_yn=> 'Y',
  p_allow_sql_workshop_yn=> 'Y',
  p_allow_websheet_dev_yn=> 'N',
  p_allow_team_development_yn=> 'Y',
  p_allow_access_to_schemas => ''),
end;

When possible, you should use workspace exports. They enable you to rapidly create a copy of an existing workspace and reduce the chance that you'll forget to create a particular user or developer.

Note

Since the export file is a series of Data Definition Language (DDL) statements, it can be edited, such as to change the name of the workspace. However, you really shouldn't do this unless you have been specifically advised to do so by Oracle.

Deploying Applications

An application developer (or workspace administrator) can export an application from the Application Builder interface. This creates a file similar to the workspace export file, in that the file will contain all the statements necessary to create the metadata required for the application. However, the application export file does not contain the DDL to create any of the database objects your application uses, nor does the application export file contain any data from the database objects that your application uses. Such data would normally be handled with specialized data-loading tools, such as the import and export utilities, or copied over a database link.

You can use the APEX export options, shown in Figure 1-9, to affect the file that is created. For the most part, you won't need to change application export settings. However, you should understand what each of these options do. Here are descriptions of some of the more useful options:

File Format:

This allows you to specify whether you want to use a DOS or UNIX format. Your choice will affect which newline sequences will be used in the resulting export file.

Owner Override:

This lets the owner of an application specify that a different schema should be used rather than the one that is currently specified. It allows you to import the schema into a workspace that's completely different from the workspace from which the application was exported.

Build Status Override:

This lets you specify whether the resulting export file will contain an application that developers can access (Run and Build Application) or one that only end users can access (Run Application Only).

As of __ minutes ago:

This allows you to export an application as it existed some time ago. For example, you could export an application the way it was before you deleted a page.

Export Preferences:

This set of options is new in APEX 4.0 and allows for some additional control during the export process.

Exporting an application

Figure 1.9. Exporting an application

An important consideration is whether you want developers to be able to access your application when it is imported to the target server. There is a trade-off between the added security you gain by exporting the application as Run Application Only versus not allowing your developers to access and debug the application via the Application Builder.

When you set an application to Run Application Only, you lose the ability to debug (by changing the NO in the URL to YES) and trace that application (by adding p_trace=YES to the URL). Generally, you want to set your production applications to Run Application Only, so that end users can't gain direct access to debug information that they shouldn't be able to see.

Deploying Static Files

APEX applications can include references to many different types of static files, including Cascading Style Sheets (CSS), images (JPG, GIF, and so on), JavaScript libraries, and Flash movies. You can reference these static files in two ways:

  • They can be stored on a file system that's accessible by the Oracle HTTP Server (OHS).

  • They can be uploaded to the database via the Application Builder interface.

Both methods have advantages and disadvantages, and you should be aware of how each method will affect your application. Whether you choose to store files in the file system or in the database ultimately comes down to how you intend to manage those files and whether they need to be used by other external systems.

Uploading Static Files to the Database

You can upload static files to the database simply by using a browser. This means that you can upload a new image to the database and use it in your application immediately, without needing to involve the person in charge of administering the web server. When you reference these static files in your application, they are actually downloaded to the end user's browser via a database procedure. For example, if you uploaded a static file called logo.jpg, you could reference that file in an HTML region in your application with the following line of code:

<img src="#WORKSPACE_IMAGES#logo.jpg"></img>

When that HTML region is processed, the #WORKSPACE_IMAGES# directive will be translated into something like this:

<img src="wwv_flow_file_mgr.get_file?p_security_group_id=16&p_fname=logo.jpg"></img>

Note

In the example, the value used for p_security_group_id is modified so that it fits onto a single line on this book page. In reality, you would have a number such as 1635127392255802.

Any static files you reference in this way will be downloaded to the end user's browser as a result of making a call to the get_file routine. This approach has two downsides:

  • Each file you reference in this way requires a separate call to the get_file routine.

  • Quite often, browsers fail to store the image in their cache. In these cases, the static file will be rerequested every time the page is displayed, resulting in slower-loading pages and images that appear to flicker as they reload.

You can write your own get_file replacement procedure to enable the browser to cache the static files, as you'll learn in Chapter 9.

Storing Static Files on the Server File System

Storing files directly in the web server file system usually makes caching the static files happen transparently, since the OHS can add some default expiry headers to the file as it is requested by the user's browser. When the user views the same page again, his browser determines whether the image can be reloaded from the browser cache or if it needs to be requested again from the web server. By taking advantage of caching for static files, your application will appear far more responsive to your end users, as well as put less load on your database.

The downside of storing static files directly in the file system is that they aren't as integrated with your application. For example, you'll need to ensure that they are backed up as a separate process, since they won't be backed up as part of the database. You also need to ensure that the file names on the file system correspond to the names you used to reference the files in your application.

Application Portability and Code Reuse

APEX offers several features to make your application more portable between environments. We'll look at a few of these features, as well as how to separate data and application logic from style and presentation.

Using Substitution Strings to Avoid Hard-Coding References

Every application in APEX has a unique identifier—an application ID—assigned to it. This ID is used to identify the application and the associated metadata within the APEX repository.

When you move applications between different APEX environments, such as between your development and test environments, you might encounter problems if you've hard-coded any references to values that may be different in these environments. Unless you can be absolutely certain that the application ID and any other references are exactly the same in the different environments, you shouldn't hard-code any of these references.

You can avoid hard-coding the application ID by using the APP_ID substitution string. Here's an example of a typical URL where the application ID (112) has been hard-coded:

f?p=112:1:&APP_SESSION.:

If you import your application into an APEX environment that already has an application that uses the same application ID, forcing you to use a different ID, you may forget to update your hard-coded references. In that case, your links will point to the wrong application.

To avoid this, you should use the APP_ID substitution string, which will be replaced with the actual ID of your application at runtime. Using this substitution string, the URL now looks like this:

f?p=&APP_ID.:1:&APP_SESSION.:

This makes your application more portable between APEX environments.

Although it is not common for pages to be renumbered, you can also avoid having to hard-code a page ID by using the APP_PAGE_ID substitution string. This can be useful where you have a branch on a page that branches back to the same page.

A page within an application can also have an alias defined for it. For example, you may give page 1 the alias of HOME, as shown in Figure 1-10. Now, rather than using a URL such as this:

f?p=&APP_ID.:1:&APP_SESSION.:

you can use this URL:

f?p=&APP_ID.:HOME:&APP_SESSION.:
Defining an alias for the page

Figure 1.10. Defining an alias for the page

By using the alias in the reference, you'll be able to renumber the pages and the URL will still be pointing to the correct page.

You should use substitution strings to avoid hard-coding any references to resources (such as style sheets) in templates and HTML regions. This will give you great flexibility to change the location of those resources, whether they are uploaded into the database or are stored in the file system. For example, if you stored all your images in the database by uploading them through the Application Builder interface, you might refer to a particular image file like this:

<img src="#APP_IMAGES#logo.gif" type="image/jpeg" />

At runtime, this will be expanded to reference the download procedure that is used for accessing static files that have been uploaded into the database. The text that is substituted at runtime will be something like this:

wwv_flow_file_mgr.get_file?p_security_group_id=986113558690831&p_flow_id=112&p_fname
=logo.gif

In this section, I described #WORKSPACE_IMAGES# and #APP_IMAGES#. The simple difference between the two is that workspace images are available to any application in a workspace while application images are available only in the application to which they're assigned. If you want to assign an image to an application, you specify the application name during the upload process. If you don't specify an app name, the image will be available to all applications in the workspace.

However, if you now wanted to store the images in the file system rather than in the database, you'd need to change every reference like this one to use a different path to the file.

To simplify and minimize the number of changes you'll need to make, you can define a substitution string that will be used in place of the #APP_IMAGES# substitution string. You can find the Substitution Strings section on the Application Definition page in the Shared Components part of the Application Builder, as shown in Figure 1-11. When you define the substitution string IMAGE_PATH for the value #APP_IMAGES#, the URL then looks like this:

<link rel="stylesheet" href="&IMAGE_PATH.logo.gif" type="text/css" />
Defining the alias to reference the #APP_IMAGES# substitution string

Figure 1.11. Defining the alias to reference the #APP_IMAGES# substitution string

Effectively, this creates a substitution string that references a substitution string. At runtime, this will be expanded to reference the same download procedure that was used before. The advantage of using this technique is that if you later decide to store the static files in the file system, rather than storing them within the database, you can simply change the value of your substitution string to reflect the new location, Figure 1-12 shows the substitution string modified to use a reference to the file system instead.

Changing the substitution string value to use the file system

Figure 1.12. Changing the substitution string value to use the file system

This greatly reduces the number of references you'll need to change when moving your application between different APEX environments where the static files are stored in different locations, Figure 1-13 shows how the substitution string would be set in the development environment.

Substitution string set in the development environment

Figure 1.13. Substitution string set in the development environment

Using the Publish/Subscribe Feature

The publish/subscribe feature allows you to reuse certain common components among applications. You can define your component in an application, and then other applications can reference that common component by subscribing to it. Any changes that are made to the master component can then easily be incorporated into the applications that subscribe to the component.

Changes can be propagated from the master component to the subscribing component in two ways:

From the master component:

The master component publishes the changes to all subscribing applications. If you want the changes to the master component to be reflected in all subscribing components, you publish the changes from the master component.

From within applications:

The subscribing applications refresh the components that subscribe to a master component. If you want only certain applications to have their subscribing components updated, you refresh the components from within those applications.

The following components can make use of the publish/subscribe feature:

  • Authentication schemes

  • Authorization schemes

  • Lists of values

  • Navigation bar entries

  • Shortcuts

  • Templates

When you create any of these component types, you have the option of subscribing to an already existing component of the same type. For example, suppose you create a master application that has a named List of Values (LOV) component that uses the following query:

select ename d, empno r
from   emp
order by 1

Figure 1-14 shows the results of using this LOV in a select list.

Using the master LOV in a select list

Figure 1.14. Using the master LOV in a select list

In another application within the same workspace, you can create a new LOV and choose to create it based on an existing LOV, as shown in Figure 1-15. The LOV creation wizard will then allow you to select which application (in the same workspace) should be used to list the available LOVs to subscribe to, as shown in Figure 1-16.

Creating an LOV that will subscribe to the master LOV

Figure 1.15. Creating an LOV that will subscribe to the master LOV

Copying the LOV from the master application

Figure 1.16. Copying the LOV from the master application

Once you've selected an application, the LOVs available in that application will be displayed, as shown in Figure 1-17, allowing you to copy and subscribe to them.

Subscribing to the master LOV

Figure 1.17. Subscribing to the master LOV

If you now create a select list in the application that uses the LOV you've subscribed to, you will see the same list of employees displayed in the master application, as shown in Figure 1-18.

A select list based on the subscribing LOV

Figure 1.18. A select list based on the subscribing LOV

If you decide you want to change the definition of the LOV so that the employees are listed in descending alphabetical order, you can go back to the master application and change the query as follows:

select ename d, empno r

from   emp

order by 1 desc

At this point, the master application would display the employees in descending alphabetical order, while the application that subscribes to the LOV would still display the employees in ascending order. To update the subscribing LOV, you need to either publish the changes from the LOV in the master application, as shown in Figure 1-19, or refresh the LOV in the subscribing application, as shown in Figure 1-20.

Publishing the changes from the master LOV

Figure 1.19. Publishing the changes from the master LOV

Refreshing the subscribing LOV

Figure 1.20. Refreshing the subscribing LOV

After you either publish the changes from the master application or refresh the subscribing LOV, the select list in the application will use exactly the same query that was defined in the master application. Figure 1-21 shows the select list now displaying the employees in descending alphabetical order.

Subscribing LOV refreshed with the master LOV

Figure 1.21. Subscribing LOV refreshed with the master LOV

The publish/subscribe functionality makes it far easier to maintain consistent appearance and behavior across common applications. You could create a single application that's used as the master for all of your common applications, and then make changes to the common components in the master application and synchronize the changes to the other applications by publishing from the master application or refreshing from individual applications.

Separating Data and Application Logic from Style and Presentation

To ensure that your applications and code are as portable and reusable as possible, you should strive to keep the data itself distinct from its presentation. This means that in general you shouldn't embed HTML markup into the data-retrieval process. For example, suppose you want to display employee names in bold in a report. You could use a query like this:

select '<b>' || ename || '</b>' as ename, deptno, sal from emp

However, it's better to keep the HTML markup out of the query. You could use a custom report template or use the column formatting section within the report attributes to apply some CSS formatting to the individual column. Then the query to bold the names would look like this:

select ename, deptno, sal from emp

Even in this simple example, it's far easier to see at a glance exactly what this query is doing. You can also change the way the data is presented without needing to modify the query definition.

By keeping the HTML markup out of the query, you may find that your application performs better if you use the query in multiple places. Because the query is already stored in the shared pool, Oracle can take advantage of this by soft-parsing the query. If you embed the HTML markup in the query, Oracle will treat two queries as being different, even if only the embedded markup is different. For example, this query:

select '<b>' || ename || '</b>' as ename, deptno, sal from emp

would not be considered the same query as this one:

select '<i>' || ename || '</i>' as ename, deptno, sal from emp

If you run the first query and then the second query (for the first time), Oracle won't find the second query in the shared pool. It would need to hard-parse the query, which would be more CPU-intensive than if it had found the query in the shared pool (and therefore could skip the hard-parse).

Whenever you find yourself including HTML in a query, rethink your approach. Consider how you can use SQL features (such as DECODE, NVL, and so on) to add conditional logic to a report template to distinguish how different columns or rows should be displayed.

Using Page Zero

If you want to display the same page element on all or multiple pages of your application, consider making use of page zero. When you place a page element on page zero, that element can be seen on all other pages of your application. By including some conditional display logic, you can restrict the element to appear only on certain pages.

A common use for page zero is to include a navigational menu in an application. You can create a region on page zero that contains the menu, which then appears on every page. You can also include a region that contains a navigational menu for administrators. You would include a conditional display that shows this menu only if the application user is an administrator.

By using page zero, you can centralize common components and functionality within your application. This also leads to a more consistent look and feel, since you can control the layout of the elements in a single place.

Performance Considerations

You need to consider performance during the design and implementation of a system, rather than investigate it only after you've rolled out your application to the live environment (at which point it might be too late to correct any fundamental flaws in the design).

Since the APEX environment runs entirely within the database, many of the recommendations for writing code and good schema design that perform and scale well are the same as for any PL/SQL programs or SQL queries. A sound understanding of SQL and PL/SQL is a definite benefit when developing with APEX. Take the time to learn about the latest features available in the database that might enable you to achieve your aims in a different way. For example learning how to use analytics might simplify many of your existing queries and make them perform better.

Bind Variables

The importance of using bind variables in your code can't be overstated. Bind variables not only help your code perform better, they also help protect you from SQL-injection attacks.

For example, say we use the following piece of code to return the SQL that should be used in a report. The code checks whether the user has entered a value for the page item P1_SEARCH. If the page item is not null, the value is appended to the text that is returned.

declare

  v_sql VARCHAR2(2000);
begin
  v_sql := 'select name, salary from payroll where deptno = 10 ';
  if p_search is not null then
    v_sql := v_sql || 'and name=''' || p_search || '''';
  end if;
  return v_sql;
end;

So, if the user entered SMITH into the search box, the following SQL would be returned:

select deptno, name, salary from payroll
  where deptno = 10 and name = 'SMITH';

However, suppose a malicious user knows you have a function called delete_user with the following signature:

function delete_user(p_id IN INTEGER) return integer

That user could enter this into the search box:

or delete_user(id) = 1

The query now looks like this:

select id, deptno, name, salary from payroll
  where deptno = 10 and name = ' or delete_user(id) = 1'

The seemingly innocent search becomes a mass employee-deletion routine. The proper way to handle this would be to not concatenate the user input directly to the SQL text:

declare

  v_sql VARCHAR2(2000);
begin
  if :P1_SEARCH is not null then
    v_sql := 'select name, salary from payroll where deptno = 10';
  else
    v_sql := 'select name, salary from payroll '
             || ' where deptno = 10 and name = :P1_SEARCH';
end if;

  return v_sql;
end;

Another potential danger in writing code that allows SQL injection is that a malicious user can add code like this:

'or'1'='1

As you can see, this code snippet can cause injected SQL to evaluate to true, potentially allowing users to access data they were not intended to see.

Report Pagination Style

Different report pagination styles affect performance when displaying a report. For example, some pagination styles will display something like "Row Ranges X to Y of Z," If you don't need to display how many total rows are returned from the query, choose a pagination style that displaysonly "Row Ranges X to Y." It is worth noting that if you decide to display the total number records the report will return, the database will fetch all the rows, even though you may not display them all. If you don't need to display the total number of rows, the report will be rendered after the rows for the first page are fetched.

Error and Exception Handling

Your APEX application can use many different anonymous PL/SQL blocks, functions, procedures, and packages as it executes. If an error or exception occurs during the execution of some code, you need to be able to handle that error or exception gracefully, in such a fashion that flow of execution by the APEX engine is not broken. For example, the following code catches an exception and sets the value of an application item:

declare

  v_salary INTEGER;
begin
  select
    salary
  into
    v_salary
  from
    emp
  where
    empno = :P1_EMPNO;
  return v_salary;
  :APP_ERROR_MSG := null;
exception
  when no_data_found then
:APP_ERROR_MSG := 'Could not find the employee record.';
end;

You can then display the application item on the page in an HTML region using this syntax:

&APP_ERROR_MSG.

You would then create a branch on the page to branch back to itself if the value of the application item is not null, thereby enabling the user to see the error and correct it.

Packaged Code

APEX allows you to write SQL and PL/SQL codedirectly in a number of places via the Application Builder interface. For example, suppose you create the following after-submit process in your login page to audit that a user logged in to the application.

begin

  insert into tbl_audit
    (id, user_name, action)
  values
    (seq_audit.nextval, :APP_USER, 'Logged On'),
end;

Now, while this would work, it means that if you ever want to modify the logic of the auditing, you need to change the application. For example, notice that you aren't currently storing a timestamp of when the audit action was performed. To add that functionality, you'd need to modify the tbl_audit table and add an extra column to store the timestamp information (if that column didn't already exist), and then edit the application to change the PL/SQL page process to include the timestamp information, like this:

begin

  insert into tbl_audit
    (id, ts, user_name, action)
  values
    (seq_audit.nextval, sysdate, :APP_USER, 'Logged On'),
end;

So, for a very simple change, you might need to modify the application in development, export a new version of the application, import that version into a test environment, and so on through to production.

A much more efficient approach is to try to isolate the number of places you directly code logic into your application by placing that code into a package and then calling the packaged procedure or function from your application. For example, you could change the PL/SQL page process to simply do this:

begin
  pkg_audit.audit_action('Logged On'),
end;

This allows you to encapsulate all of the logic in the packaged code. Assuming you aren't making fundamental changes to the package signature, you can modify the internal logic without needing to change the application. This design allows you to change the table that the audit information is stored in or to add new columns and reference session state items without needing to change anything in the application itself. All you'd need to do is recompile the new package body in the development, test, or production environment. This would result in much less downtime for the application, since you no longer need to remove the old version of the application and import the new version. This method really does allow downtimes of just a few seconds (the time it takes to recompile the package body), as opposed to minutes, or potentially hours, while new versions of the applications are migrated. If you're using the 11gR2 database, you can take advantage of edition-based redefinition, which allows for the creation of multiple versions of PL/SQL objects. Obviously, you won't always be able to completely encapsulate your logic in packages. And sometimes, even if you do encapsulate the logic, you may need to change something in the application (for example, to pass a new parameter to the packaged code). However, it is good practice to use packaged logic where you can. Using packaged code can save you a lot of time later on, as well as encourage you to reuse code rather than potentially duplicating code throughout your application.

Another benefit of using packaged code is security. The parsing schema can be granted execution privileges on the packaged code only, without having direct grants on the underlying objects. This obviates the need for at least two schemas, one to own the objects and another to act as the parsing schema. If the parsing schema is compromised, the underlying objects are still protected as only the packaged code is accessible via this schema.

Team Development

The Team Development toolset is new in APEX 4.0. These tools enable management of the development process and tracking of new feature requests, bugs, and milestones. Before I delve into how these tools are used, it should be noted that Team Development does not perform source code control. That activity, sadly, is still handled as it was in previous versions of APEX and HTMLDB. What Team Development does provide is a centralized mechanism for recording requests and tracking the progress of development activities. The interface is straightforward but highly configurable, so some planning is required before putting these tools to full use. For businesses that use tools like Microsoft Project or Mercury Quality Center to track projects, Team Development may not be necessary. But for those without enterprise tools, these new features can be very beneficial—especially because they're free.

Using Team Development

To access the Team Development tools, simply log in to APEX as a developer. On the home page, you'll see the Team Development icon as shown in as shown in Figure 1-22.

Team Development icon

Figure 1.22. Team Development icon

Once inside the Team Development system, you will see the types of project information that can be tracked. Figure 1-23 shows the Team Development dashboard. Before attempting to use these tools, a little planning is recommended.

Team Development dashboard

Figure 1.23. Team Development dashboard

Preparing for Team Development

To get the maximum benefit from the Team Development tools, it is important to understand the interrelationships among its five components. Features and milestones are the anchor components in a development project. Milestones are just what you'd think—a date that has some significance to the project. Once a milestone is defined, it can stand alone or be assigned to features, to dos, and bugs. Features are a little more complex since milestones, bugs, and to dos can be associated with a feature. In addition, one feature can be the parent of another. This allows for an endless hierarchy of features, which is why planning is important. You don't want project management to be more complicated than the development process.

Figure 1-24 shows how you could build a hierarchy of features, bugs, to dos, and milestones with several parent-child relationships. Figure 1-25 shows a simplified method of managing a project. If you equate features to forms or reports, you can see that it's easier to track features, bugs, to dos, and milestones using the simplified method.

In any case, the Team Development tool offers managers and developers a centralized, web-based repository for storing bug reports, user requests, requirement changes, and so forth. This is a significant improvement over the traditional methods for recording this type of information: verbal conversations, handwritten notes, e-mail, etc. Since the Team Development interface is web-based, it can be used by anyone, whether APEX is being used for development or not.

Possible feature hierarchy

Figure 1.24. Possible feature hierarchy

Recommended feature hierarchy

Figure 1.25. Recommended feature hierarchy

To enable Team Development for a project, the team lead should prepare by identifying the main functions of the application and recording them as features. Specific forms and reports are good candidates to record as features since users routinely report problems and request changes at these levels. In addition, if any milestones are known, they too can be recorded. From that point on, to dos and bugs should be recorded and associated to a feature or a milestone. As new features are added, they should be recorded as well.

A simple workflow for setting up Team Development would include the following:

  1. Create features for all known forms, reports, etc.

  2. Assign any known to dos to their related features

  3. Assign dates to any known milestones

As the project progresses, new bugs and to dos can be assigned and the status of completed tasks can be updated.

Creating Features

To create a feature, click on the Features icon on the Team Development dashboard and then click the Create button. Figure 1-26 shows the Feature entry screen. It is important to note that this form allows you to not only create a feature, but also to populate LOVs, which are used throughout the Team Development system. Fields used to build LOVs are:

  • New Owner

  • New Contributor

  • New Focus Area

  • New Release

Because the data entered into these fields is accepted without validation, it can easily become polluted. Please note that the "Release" LOV is populated using this form. If Team Development is to be used effectively, data like Release numbers must be tightly controlled. For this reason, it is recommended that access to the Create Feature page be restricted to team leads.

Create Feature form

Figure 1.26. Create Feature form

The following LOVs are prepopulated at installation and can't be changed via the Team Development interface:

  • Feature Status

  • Desirability

  • Priority

While many of the fields on this form accept free-form text, Feature Status does not. It is used, as you will see later, to aid in project status reporting. The remaining attributes of this form are self-explanatory and are common to most project management methodologies.

Recording Bugs and To Dos

Once the testing process begins, your users will inevitably begin reporting bugs. The Bug form in the Team Development interface is used to record and manage this information. The Create Bug form, shown in Figure 1-27, is similar in functionality to the Create Feature form. As noted, bugs should be associated with features that equate to a form, report, etc.

Create Bug page

Figure 1.27. Create Bug page

To dos are similar to bugs in that they are a record of some activity that needs to be done by some date. You could consider a to do as anything except a bug. Figure 1-28 shows the Create To Do page. Determining whether to use the Bug page or the To Do page is up to the team lead. As all developers know, testers often report bugs that may actually be requests for new or changed functionality. In such cases, the team lead may want to create a To Do so that the development team can address the issues appropriately. The main difference between these two objects is that the Bug page can record much more detailed information about the context of the issue.

Like the Create Feature page, the Create Bug and To Do pages contain free-form text fields, LOVs, and fields used to populate LOVs. Using the recommended project management process, a bug or to do would be created and minimally assigned to a feature and to a developer.

Create To Do page

Figure 1.28. Create To Do page

It is worth noting that to dos, like features, can be created in a hierarchical fashion. Although this is a powerful feature, it does allow complex hierarchies, which are difficult to track and manage. At least in the beginning, it is recommended that the depth of to do (or feature) hierarchies be kept to one or two levels.

Recording a Milestone

A milestone is a significant date within a project plan. It can signify the required completion date of some unit of work, a business critical date like the end of a fiscal year, or something unrelated to the project like a public holiday. The Create Milestone page, shown in Figure 1-29, allows you to create a milestone, which can then be associated to a Feature, Bug, or To Do.

Create Milestone page

Figure 1.29. Create Milestone page

Once a milestone is set, it will show up on its assigned date on the project Calendar as shown in Figure 1-30. The annotation of the milestone (November 12 on the Calendar) is actually a hyperlink that, when clicked, causes the Edit Milestone page to be displayed.

Project Calendar

Figure 1.30. Project Calendar

Milestones can stand alone or can be assigned to any or all of the project's features, bugs, or milestones. While this can be beneficial in managing a project, it does require the team lead to be diligent in determining how to associate milestones with other objects. It is important to note that any feature, bug, or to do can be assigned only one milestone at a time.

Gathering User Feedback

New in APEX 4.0 is the ability to quickly create a page that lets users report system issues to the development team lead. While this may sound like a handy feature, I may recommend you avoid it for the following reasons:

  • Nontechnical users sometimes have trouble describing system problems in a way that makes sense to developers

  • If you make it very easy to provide feedback, you'll get lots of it. You will then have to spend time filtering out the "noise" in an effort to determine what is relevant and what is not.

I've been writing code and working with users for more than 20 years, and during that time I've followed one simple approach regarding user feedback: without meaning any disrespect, I don't believe what anyone says about an application bug until I see it myself. When you are intimately familiar with a piece of code, no one can describe a bug in that code better than you. If at all possible, I recommend having a lead developer meet with the user community (or their reasonable facsimile) on a regular basis. The developer can then translate users' concerns into actionable items for the development team.

If you decide you want to include a Feedback page in your system, here's is a brief description of how to do it:

  1. Add a page to an application, selecting "Feedback" as the type.

  2. Select "Yes" to add a Navigation Bar entry.

  3. Set the label for the Navigation Bar entry.

  4. Select "Yes" to Enable Feedback for this application.

Once you complete this process, your users can access the Feedback page from the Navigation Bar (next to the Login / Logout link). An example is shown in Figure 1-31.

Feedback page

Figure 1.31. Feedback page

One more time, though, beware of asking for unfiltered end-user feedback. You'll get it.

Tracking Development Progress

Without progress reporting, all the time and effort spent recording features, bugs, and to dos would be a waste of time. APEX 4.0 addresses this reality by providing mainly dashboards to track project activities. The dashboard for each type of work shows progress and status at a glance, but to get more detailed information, additional reports are available. The APEX 4.0 Team Development system does not contain a consolidated report that shows the status of objects across Features, Bugs, and To Dos, but does have a the Team Development home page that contains dashboards for all types of work.

The main drawbacks to the Team Development system's reporting capabilities are:

  • Inconsistent status labels across Features, Bugs, and To Dos

  • No built-in report that consolidates Features, Bugs, and To Dos

Despite these drawbacks, it is still possible to effectively manage a project using the Team Development tools.

Feature Dashboard

When accessing the Features dashboard from the Team Development home page, you'll see a graphic that shows the percentage of Features that are complete and additional information on the total number of recorded features. This dashboard is unique in that a feature is considered complete when its status is set to "Functionally Complete – 80%" as opposed to 100% complete for a bug or a to do. The difference in status labels is a little annoying, especially since there is no way to set your own status labels via the Team Development interface. A feature (or a "page" in this example) that is marked "80% Functionally Complete" is shown in Figure 1-32.

Feature dashboard

Figure 1.32. Feature dashboard

Bug Dashboard

When you access the Bug dashboard from the Team Development home page, you'll see a percentage graphic showing the number of "closed" bugs. A closed bug is one that has a status of "100.Complete." If you have recorded 2 bugs and closed one, the Bug dashboard will show that 50 percent of the bugs are closed. Several additional links on the dashboard, as shown in Figure 1-33, can be used to navigate to the Bug details page.

Bug dashboard

Figure 1.33. Bug dashboard

To Dos Dashboard

The To Dos dashboard is decidedly different from the Features and Bugs dashboards. While this page does show the completion status of all recorded To Dos as a percentage, it also contains a doughnut-shaped graphic that shows the percentage of to dos assigned to each developer. If you have two developers and each is assigned one to do, the doughnut will be divided in half to show that each developer is responsible for 50% of the to dos. The graphic also acts as a hyperlink for navigating to a report of to dos by developer. Like bugs, to dos must be 100% complete before the dashboard will show a completion percentage. Figure 1-34 shows the To Do dashboard.

To Do dashboard

Figure 1.34. To Do dashboard

Although it may be difficult to see in gray-scale, the doughnut-shaped graphic is divided in half and is shown in two distinct colors. Clicking either half of the graphic or any of the other hyperlinks on this page results in navigation to the associated to do detail page.

New Development Features in APEX 4.0

APEX 4.0 includes some interesting new features to assist in development and debugging. The APEX Advisor, for instance, is a utility that will scan your application (either completely or by specific page) and report on deviations from "APEX best practices." You may or may not agree with what the Advisor says, but it makes finding certain type of errors much easier than digging through the code. For example, if you intend to include help on every field on all pages, the Advisor can quickly tell you whether you achieved your goal.

APEX Development Advisor

You access the APEX Development Advisor from the Utilities menu of an application home page, as shown in Figure 1-35. You should review each of the Checks to Perform to ensure that you get only the information you need. In the Check Pages section, you can include a comma-separated list of page numbers to scan, or leave this area blank to scan all pages.

APEX Development Advisor

Figure 1.35. APEX Development Advisor

The resulting "report" shows all of the areas that don't meet the requirements of the Advisor. Using the example stated above (make sure all fields have help text defined), the Advisor was run on Page 1 of the Buglist application. The resulting APEX Development Advisor report shown in Figure 1-36 identifies a field as having no help text and includes a "View" link to take you directly to the item in question.

APEX Development Advisor report

Figure 1.36. APEX Development Advisor report

APEX Code Debugger

The integrated Code Debugger has a clean, graphical user interface to view detailed application execution details. Debugging can be enabled or disabled for an application using the Edit Application Properties button on the Application Home Page. Once debugging has been enabled, it can be activated at any time during execution (when running in development mode) by pressing the "Debug" button at the bottom of the application page. Clicking the "View Debug" button at the bottom of any page brings up the Debug Message page, as shown in Figure 1-37. This page displays a history of the debug sessions that have been run for this application and their relative time of execution.

APEX debug history

Figure 1.37. APEX debug history

Clicking on the most recent debug session identifier opens another window showing the details of that session, as shown in Figure 1-38. The bar graph in the details window shows the execution times of each recorded activity. Hovering your mouse over the bars pops up some help text that describes the activity. Clicking on the bar will navigate to that activity in the debug details.

APEX Debugger

Figure 1.38. APEX Debugger

Summary

This chapter covered some best practices for using APEX. You don't need to follow our advice. Many people just charge in and begin coding without thinking to lay a solid foundation for their work. Some of what we advocate in this chapter may actually slow you down at first, but it will save you time in the long run. Helping you to work more efficiently, and taking a long-term view of doing that, is one of the reasons we've written this book.

As for project management, we are discussing art as opposed to science. Each project manager has his own way of doing things so no single methodology works in every case. In instances where formal project management tools are not available, APEX 4.0's Team Development tools can be a vast improvement over traditional methods that include e-mail, Excel, pencil and paper, and so forth. Hopefully, this chapter has exposed you to enough of the functionality that you can benefit from at least a portion of it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset