Saturday, March 30, 2019

Monitoring your Oracle APEX environment; when is a good time to do an upgrade?

Yesterday Oracle released Oracle APEX 19.1. We typically upgrade our environment within a week.

The question is always, when is a good time to upgrade?

As we are hosting multiple applications and sites in our environment, we want to find a spot with minimal impact to our customers. For some applications which are only accessed by some people in Belgium, it's very easy to schedule an upgrade at night as the chances are not high people accessing the app. But in the case of APEX Office Print, we have customers all over the world in different timezones. So they might want to look at their dashboard or download the latest version at any moment in time.

For our AOP service itself, we have an internal policy we don't allow downtime. We load balance, so even when performing upgrades of AOP, there will always be a server available to handle the requests. For our Oracle database and APEX environment, we are fine with a few minutes downtime while upgrading the server.

In Oracle APEX itself, when you go to Monitor Activity - Page Views by User by Hour you get a graphical overview when your applications are being used:

This screen works well for a given workspace. If you want to look cross workspaces, for your entire APEX instance, you could build something yourself similar to the above. An overview like that would give you an idea based on historical usage which hours have the least amount of impact.

Now that you found the window, you still want to check if somebody is using our APEX apps.

So before I start an upgrade, I always check the last activity in our APEX apps.
The script I use to monitor the activity in our entire APEX environment - as SYS user run in SQL>

SELECT
    workspace_name,
    apex_session_id,
    user_name,
    remote_addr,
    TO_CHAR(session_created, 'DD-MON-YYYY HH24:MI') AS session_created,
    TO_CHAR(session_idle_timeout_on, 'DD-MON-YYYY HH24:MI') AS session_idle_timeout_on,
    TO_CHAR(session_idle_timeout_on-(session_max_idle_sec/24/60/60), 'DD-MON-YYYY HH24:MI') AS last_activity,
    TO_CHAR(sysdate, 'DD-MON-YYYY HH24:MI') AS time_now,
    round((sysdate-(session_idle_timeout_on-(session_max_idle_sec/24/60/60)))*24*60) as minutes_ago
 FROM apex_workspace_sessions
WHERE user_name NOT IN ('APEX_PUBLIC_USER','nobody')
ORDER BY minutes_ago, workspace_name, session_idle_timeout_on DESC;

This gives me the following overview:


So I see how many minutes ago (last column) which APEX workspace was used by which user. I could even follow the APEX session to find out more about the APP etc. but that is not really relevant for me. In the script, I only check for logged in users.

In case I also want to see for direct connections to the database, or I want more real-time info from the APEX session, I run the following script to check the session in the Oracle Database:

SELECT
    sid,
    serial#,
    username,
    osuser,
    machine,
    program,
    --sql_id, sql_exec_start, event,
    logon_time,    
    client_info         AS workspace_id_auth_user,
    module              AS db_schema_apex_app_id_page_id,
    action              AS action,
    client_identifier   AS auth_user_apex_session
 FROM gv$session
WHERE type = 'USER'
  AND service_name = 'apex_pdb';

This gives me the following overview:


In the above screen you see I'm connected to the database with SQL Developer. The other sessions are coming from our connection pool. What is interesting is that APEX is instrumented to populate session info in the database like module, client_info, and client_identifier. This is important as APEX/ORDS is working with a connection pool so it would be hard to see what APEX session corresponded to which database session in case they didn't.

The above helps me to find a good time to perform upgrades, without impacting too many people.
So I thought to share my scripts as it might help you too.

Friday, March 29, 2019

Where are my static application files of Oracle APEX?

Ever got the error that there's an issue with your environment because the Application Express files have not been loaded when you try to open Oracle APEX in your environment?


Since the Announcement of the availability of Oracle APEX Static Resources on a Content Delivery Network I typically configure APEX to use the CDN.

Steps to do so for the entire environment:
  • Navigate to the apex/utilities subdirectory from the directory you unzipped APEX to
  • Connect to your database as the SYS user
  • Run:  @reset_image_prefix.sql
  • when prompted for the image prefix, enter the correct path, e.g. https://static.oracle.com/cdn/apex/18.2.0.00.12/
Now, when I got the above error, I forgot which images folder APEX was using. Was there an issue with the connection to the CDN, was it a local issue?

Below is how I found out how the APEX images directory was configured and if it was using the CDN or local files.
Connect as SYS in a SQL window (SQLcl, SQL Developer, SQL Plus, ...):

SQL> set serveroutput on
SQL> begin
  2  dbms_output.put_line(apex_180100.wwv_flow_image_prefix.g_image_prefix);
  3  end;
  4  /
/i/

PL/SQL procedure successfully completed.

SQL> begin
  2  dbms_output.put_line(apex_180200.wwv_flow_image_prefix.g_image_prefix);
  3  end;
  4  /
https://static.oracle.com/cdn/apex/18.2.0.00.12/

PL/SQL procedure successfully completed.

Above you see that in APEX 18.1 the /i/ directory is used on the local web server.
On APEX 18.2 the CDN is being used.

Note: whenever you run the above command change to the correct APEX user (version), so for APEX 19.1 you use apex_190100.

Friday, March 01, 2019

Including an image from APEX Static Application Files in an email

Some people use APEX Office Print (AOP) to send out HTML emails based on a custom HTML template they created. Today we got a question in our AOP support inbox how to include images in those emails.

In an AOP Template, you can use a tag {%image} and AOP will replace that tag with a base64 encoded image. This works great when you open the resulting HTML file in a browser, but when using emails it's not so great as some email clients don't support base64 encoded images e.g. Outlook.

That the images don't always work is not really an AOP issue, it's more an issue with HTML emails.
Here's a good read on supporting images in HTML emails.

In short, it's better to include an URL to the image as that is supported by most, if not all, email clients. Typically the images are stored in a BLOB column in a table or people want to reference an image that they uploaded in APEX > Shared Components > Static Application Files.

This blog post shows a technique to reference an image stored in the database through a URL.

1. Create a new Public Page in an Oracle APEX Application e.g. Page 22

2. Create a region on the page

3. Create a hidden item on the page e.g. P22_FILE_NAME

4. Create a Before header Process "Retrieve image from DB" with following PL/SQL Code:

declare
  l_file_name     apex_application_static_files.file_name%type;
  l_mime_type     apex_application_static_files.mime_type%type;
  l_file_charset  apex_application_static_files.file_charset%type;  
  l_file_content  apex_application_static_files.file_content%type;
begin
  select nvl(file_name, 'output') as file_name, 
         nvl(mime_type, 'application/octet-stream') as mime_type, 
         nvl(file_charset, 'utf-8') as file_charset, 
         file_content
    into l_file_name, 
         l_mime_type, 
         l_file_charset, 
         l_file_content  
    from apex_application_static_files 
   where application_id = :APP_ID 
     and file_name      = :P22_FILE_NAME;
  
  sys.htp.flush;
  sys.htp.init;
  owa_util.mime_header(l_mime_type, false);
  sys.htp.p('Content-length:'||dbms_lob.getlength(l_file_content));
  sys.htp.p('Content-Disposition:attachment; filename="'||l_file_name||'"; '||'filename*='||l_file_charset||''''''||l_file_name);
  owa_util.http_header_close;
  wpg_docload.download_file(l_file_content);
  apex_application.stop_apex_engine;

exception
  when no_data_found
  then
    sys.htp.p('');
    apex_application.stop_apex_engine;
end;  

5. That's it.

Now you can reference the image with the following URL: https://my_server/ords/f?p=MY_APP:22:0::::P22_FILE_NAME:my_image.png

If you want to test the functionality in an email, go to SQL Workshop >SQL Commands and use the following code:

begin
  apex_mail.send(
    p_to        => 'my@email.com',
    p_from      => 'my@email.com',
    p_subj      => 'Mail from APEX with inline image',
    p_body      => 'Please review the email in html.',
    p_body_html => 'Here the inline image: <br/> <img alt="Image" height="150" width="150" src="https://my_server/ords/f?p=MY_APP:22:0::::P22_FILE_NAME:my_image.png">');

  apex_mail.push_queue;
end;

Now you can send HTML emails in APEX with references to images that are stored in the database.

Sunday, February 10, 2019

Region only shown in development mode in Oracle APEX

In the last months, I had to look up several times in different projects how to show a region only when I was logged in into the App Builder in Oracle Application Express (APEX). So I thought to write a quick post on it.

In one project we had to log in with different users to test the behavior of the authorization schemes, so people saw the things they should see and could do the things they are allowed to do. As the logins where not straight forward we created a region with those test credentials. Other people were testing too with their own credentials, so we really wanted to keep the original login page, and decided to just add a region on the page we would only see when we were logged in into APEX itself.

Today I added some new pages to an app and wanted to make sure the navigation to those pages were only visible to me. I know, you should do this in DEV, and then when all is fine, propagate the app to TEST and PROD. The truth is, I've some applications that only exist in one environment and so I update straight in "production". Those apps are still backed up automatically every night, so worst case I can always take the version of the previous day. But just to be clear, this is not good practice ;)

So how do you show a region only when you are in development mode in Oracle APEX?

You go to the Conditions section of your region, list entry or any component in APEX really, and add a PL/SQL Expression: apex_application.g_edit_cookie_session_id is not null


Alternative you can use "Item / Column not null" and specify APP_BUILDER_SESSION:
*) updated blog post 11-FEB-2019 after comment of Christian (thanks Christian!)


It would be cool if there was a condition type "Development Mode", but maybe I'm the only one needing this...

Typically you would use Build Options (see Shared Components) to include or exclude certain functionality in your APEX app, but in the above two use cases, it won't really work.


Another enhancement would be that the Status of the Build option would include "Dev Only" next to Include and Exclude.

Monday, January 28, 2019

The best free database... Google is wrong!

When you search on Google for "the best free database", the below is what you get (search done 11-NOV-2018 and again on 12-JAN-2019 and 28-JAN-2019). To my surprise, there's no Oracle on the list? The reason is Google took the answer from this review. As I don't want people to see this screenshot, I put in red what is wrong with the answer, so in case Google shows images, and people don't read this blog post, they don't get the wrong answer ;)

The above is so wrong for me, the best free database in the world is Oracle Express Edition (XE). Oracle released XE18c on October 19th, 2018. This database is unbelievable. You basically get an Enterprise Edition version and almost all options are turned on! It's amazing, the only restrictions you have is on the amount of RAM (2GB) and disk space (12GB). You even have the pluggable database architecture and can create 3 PDBs (pluggable databases).
In my opinion, there is no other free database in the world that will beat this. Below I will go in more detail why I like this Oracle XE18c so much, but first, let me show you Google actually knows the right answer too.

Google says "People also ask": "What are the top 5 databases available on the market?" and here Oracle is number one. The other question is "What is the best database software for small businesses?" Oracle number one again. If the question would be "What is the best database software for enterprise businesses?" Oracle is number one too, this is common knowledge.


Google's algorithm to answer the first question, just got it wrong. How can Oracle be number one and be the best, but not in the free section, whereas their best database is available for free? :)

Google allows you to comment on their search results, which I did:



Why do I like Oracle XE 18c so much?

When we talk about Oracle XE, we really talk about the full Oracle database in general. Yes, there are a couple of limitations, but nevertheless, you get the full feature set of the Oracle database! All the good stuff why Oracle shines is there: to have the best performance you can use partitioning and online index rebuilds (and in the future automatic index creation!), to increase high availability you have the full flashback technology to your disposal, for security Oracle has VPD, Real Application Security, Database Vault... Oracle plans to release a new version of XE every year too, so you have always the latest and greatest.

I should write another blog post why I like the Oracle database so much, but I encourage you to just try it and decide yourself.

Getting started with Oracle XE

If you just want to try Oracle XE, most likely the easiest way without hitting your system, is to go with the Oracle docker container. Here're the steps to get Oracle XE running in an Oracle docker container.

If you don't have Docker and Git yet, download and install first.

Open a Terminal or Command Prompt and run following commands:

git clone https://github.com/oracle/docker-images.git

cd docker-images/OracleDatabase/SingleInstance/dockerfiles

Download Oracle XE
Copy the oracle-database-xe-18c-1.0-1.x86_64.rpm in the docker-images/OracleDatabase/SingleInstance/dockerfiles/18.4.0 directory

Move on by running following command:

 ./buildDockerImage.sh -v 18.4.0 -x -i



docker images

docker run --name OracleXE -p 1521:1521 -p 8080:8080 -e ORACLE_SID=XE -e ORACLE_PWD=oracle -v /Users/dgielis/git/docker-images/OracleDatabase/SingleInstance/dockerfiles:/opt/oracle/oradata oracle/database:18.4.0-xe

    ...

And voila, you are done! (more info on the Oracle Docker images)

To get a view in your database you can use Oracle SQL Developer. Here's how you connect to it:



The next thing would be to install Oracle APEX, so you don't only have the best database in the world, but also the best low code platform in the world, which works absolutely awesome with the Oracle database.

Friday, January 25, 2019

Trying to decide which APEX events to attend? Here's where I'll be.

I love going to conferences, to catch up with friends, be part of the vibrant Oracle APEX community, gain and share knowledge and of course demo APEX Office Print (AOP).

Conferences are a great way to get some new ideas and insights. You learn not only from the content but even more from the different cultures and background of people and the way they approach things.

My schedule till June looks like this:

ITOUG Tech Day Milano, Italy: 30-JAN-2019

This day is part of the ITOUG Tech Days. I've never presented in Italy, so I really look forward to this first time. I'll present on "Bringing your Oracle Database alive with APEX".

APEX Meetup Düsseldorf, Germany: 5-FEB-2019

As we did some major releases with AOP 18.1 and 19.1 and we have many customers in Germany, Niels asked to present on Reporting in APEX. As it's a meetup, we have more room to make it really interactive, so if you are in the region of Düsseldorf and Ratingen, stop by and ask me any question. I'll show some cool tricks of AOP and will give some insight what's coming later in the year.

APEX World, the Netherlands: 25/26-MAR-2019

I don't think I've ever missed a version of APEX World. It became the biggest APEX only conference in the world, and it's basically in my backyard :) This year is special too, as it's the 10th year anniversary! My company, APEX R&D, has been a long time sponsor of the event. This is the only conference where we also have a booth, so you can stop by any time you want and ask us many questions :) At our booth, we show the latest and greatest dev build of APEX Office Print (AOP)!
I'll give my "Augmented Reality & Virtual Reality with APEX" presentation this year.



APEX Alpe Aldria, Croatia: 12-APR-2019

Last year this conference got its first edition and it was a great success. My friends Peter, Aljaz, and Dario really know how to make a great conference. Last year AOP sponsored and Sunil, our lead backend architect of AOP, was there to answer any questions. This year most of our team is going. I'll give a presentation "APEX Reporting Tips & Tricks", similar to the webinar I did for AUSOUG.

APEX Connect, Germany: 7/9-MAY-2019

The last couple of years I've presented at this conference. APEX is being used a lot in Germany, so it's great to see so many use cases of APEX. This year I was asked to do my "Augmented Reality & Virtual Reality with APEX" presentation. In our office we have 3 VR systems running (with HTC Vive) and are doing some research on cool use cases. I dream one day we can develop our APEX apps like Tom Cruise did in the movie Minority Report :)



ODTUG KScope, Seattle, US: 23/27-JUN-2019

Since my first attendance in 2006, this is my favorite conference. It's the go-to place to meet the APEX development team, and so many smart people you meet daily on the internet.
It's unsure yet what I'll present on this year, reviews are still ongoing, but for sure there are many good presentations there and the latest and greatest of the APEX Dev Team. If you can ever go to this conference, I would definitely recommend it!


If you are at one of the above events and you want to chat, don't hesitate to stop me. I would love to meet you. I'm thankful you read my blog and I would love to hear more about you.

Wednesday, January 23, 2019

APEX Office Print (AOP) 19.1: Printing and Exporting made easy in Oracle APEX

To start the new year in an awesome way, we released APEX Office Print (AOP) 19.1 on January 11th. I typically don't blog about every new AOP release, but this is an important release in our history of the product.

Our dream is that every developer can use AOP, that is why in AOP 18.1 (released September 2018) we made a Free Cloud Tier.

With AOP 19.1 we go one step further... from now on you can run AOP in Developer Mode, which means you can call our cloud as much as you like in this mode. Your number of reports/credits in your plan are not touched at all. When you want to test AOP or are developing some new reports, you can use development mode so you don't need to use your credits for that.

We had been thinking about this setting for some time, but Jon from JMJ Cloud reminded us how important it is for you in his nice blog post: APEX Office Print – Is it the APEX printing solution we have all been waiting for?

Just like in previous versions we added a number of new features, fixed some bugs and made several enhancements. You can view the release notes for every version here.

AOP 19.1 is again a free update for all our users, Cloud and On-Premises (with a valid maintenance contract).

We love to listen to our customers and help them as good as we can to make their projects a success. That is why we like to release frequently, so creating the reports you want is easy, fast and integrated with Oracle APEX.

In this post, I want to highlight some of the APEX Office Print (AOP) features that I believe make a difference, and why AOP became the go-to solution when you want to print and export your data in Oracle APEX. If you want to see AOP in action, definitely check out the AOP Sample App.

Architecture

Just like BI Publisher or XSL-FO, AOP has a server component. The difference is that AOP is one executable, has a built-in web server which can autoscale depending the number of CPU cores and it listens on incoming requests with a JSON payload. The AOP Server is very simple to install, upgrade and maintain, yet flexible and scalable out-of-the-box.

To make it really easy to do calls to the AOP Server, we built a PL/SQL API on top of the REST interface. As we love Oracle APEX so much, we built an APEX Plug-in on top of the PL/SQL API.

Our idea is that you are up-and-running in less than 5 minutes.



Integration with Oracle APEX

AOP is the most integrated solution for APEX apps on the market. We went much further than the built-in BI Publisher and XSL-FO support. AOP understands your Classic Reports, Interactive Reports, and Grids, Charts and Calendars. AOP lets you print or export your Interactive Report and Grid exactly as you see it on the screen, with highlights, computations, breaks, group by etc.


In the APEX Plug-in you specify the static id of the region(s) and AOP will do the rest.


The APEX Plug-in is really flexible and the easiest way to get your reports done in no time. 
You specify your template, the data source(s) and your output, that's it.

Based on Templates

AOP is template driven, you basically create a template in your favorite editor. AOP supports templates in Word, Excel, Powerpoint, PDF, HTML, Markdown, Text and CSV.
In the template, you use specific tags AOP understands. You find an overview of tags in the AOP documentation.



PDF Tools

As many people generate PDF documents, since AOP 18 we are focussing to include more and more PDF-specific features. AOP allows you to generate PDFs from almost any file format, even images, you can merge PDFs, split them, password protect the PDF and you can even include a watermark.


We also have a special PDF, called Single Page PDF. Some people don't know the size of the document, so they just want a PDF with a variable size. For example when you get a receipt in a restaurant, depending on the number of dishes and drinks the PDF is different in size.

One more thing... AOP Web Editor

We are developers ourselves and have been using AOP since 2015. Sometimes you don't know what's going on, so from day one we heavily instrumented AOP to see exactly what it's doing and give you all the tools to make debugging fun. The Web Editor allows you to create documents really fast, look at debug info, see the different requests and try different options really fast.



Support

We love our customers and find support extremely important as we want you to be successful. You can contact us through our support email or through the new AOP Slack Channel on apex.world.

Final words

The above is just a small set of features I believe make a huge difference compared to any other engine. You don't have to take my word for it, just give AOP a try with our free version and development mode and decide yourself :) And know you are not alone, we are proud of every single customer we have from large customers like Siemens, NASA and even Oracle, to smaller customers like Storm-Petrel (who use AOP extensively!).

Monday, January 21, 2019

Upgrading of Oracle APEX 18.x and future

Late September 2018 Oracle Application Express (APEX) 18.2 was released.

Since 2018, Oracle adopts a new versioning for their software, and APEX is following that. The plan of the Oracle APEX development team is to do two releases a year which carry the year and the release number of that year e.g. 18.1, 18.2, 19.1.

I like this new way of providing us with new releases. You don't know the exact timing of the releases, but you know the development team will be close to release version 1 in March/April and version 2 in September/October timeframe. In between you can download one-of patches or patch set releases through Oracle Support.

However there's one important change compared to before you have to be aware of:
every new version of Oracle APEX will be a complete install, with its own schema.

The biggest benefit of this approach is that the downtime to upgrade an Oracle APEX instance can be very minimal and the install is fast. You can read more about it in Maximizing Uptime During an Application Express Upgrade. I believe the Oracle Cloud is a big driver for this, but it benefits us all.

Another benefit is that if you can download and install all APEX releases in Oracle XE more easily. Before if you had installed APEX 5.1.0 and wanted to upgrade to APEX 5.1.2, 5.1.3, 5.1.4 you had to download the software from Oracle Support. But you could only do that with a valid support contract. If you wanted a complete free system with Oracle XE, you could download the latest version of APEX and do a new install and migrate over the workspaces and apps. Since APEX 18 you can always install those versions and APEX itself is doing the migration for you.

Now the biggest disadvantage of this approach is that you can't easily export and import your apps and plug-ins to a different version. Before when you might have Oracle APEX 5.1.0 (released Dec. 2016) in one environment and Oracle APEX 5.1.4 (released Dec. 2017) in another (or anything in between) and you could easily export and import between those environments. Oracle APEX exports have always been compatible till the second dot, so you could export import from any 5.1.x to another 5.1.x. You can import an APEX 18.1 version in an APEX 18.2 environment, but not the other way.

We typically move fast to a new version of APEX as we have to support those versions for APEX Office Print. I also blogged how we upgrade major releases by running multiple PDBs. With APEX 5.0, released in April 2015 and APEX 18.1 released in May 2018, we covered over 3 years in 3 PDBs (1 PDB for every major release of APEX 5.0, 5.1, 18.1). With the new version numbers, with 3 PDBs we cover a maximum of 1.5 years.

If you are aware of this, you can plan your application releases with it. And these new release numbers will make customers probably want to upgrade faster, or at least once a year as numbers go up fast :) As developers it's not only nice to work with new technology, it's also more productive for everyone.

Oracle APEX is not the only software in our stack right? ORDS, SQL Developer, Oracle Database, they follow the new release numbers. ORDS released a few days ago 18.4. The ORDS/SQL Developer/SQLcl team releases every quarter and typically use the quarter of development in their release number.

Tim Hall wrote a nice article on his thoughts on the new release numbers for the Oracle Database.

I love getting new versions and play with the new features or not wait long on bug fixes. We release frequently with APEX Office Print (AOP) too, 10 days ago we released AOP 19.1, but that is for another blog post!

Happy upgrading :)

Monday, September 10, 2018

Increasing Maximum Web Service Requests in Oracle APEX 18.1

While running our final tests of APEX Office Print (AOP) 18.1 we hit "ORA-20000: Issue calling Main AOP Service (REST call: ): ORA-20001: You have exceeded the maximum number of web service requests per workspace."


When you login into the Internal Workspace and navigate to a workspace, there's a setting for  Maximum Web Service Requests. The default value is 1000 requests per 24h (rolling window).

If you know that AOP has next to hundreds of server tests, also around 500 automated tests through APEX, we hit this limit after the second full run. After setting the value to 20000, we are able to continue our final testing :)

I guess the chances are small you will hit the limit in a normal APEX app, but if you do, it's easy to fix by setting a higher value for your workspace.

Wednesday, August 15, 2018

Replace deprecated apex_util.string_to_table (APEX 5.1/18.1)

Sometimes the Oracle APEX documentation announces some packages will become deprecated in a release. It's not that those packages are suddenly gone, but you should not use them anymore. Your code will run fine still, but in the future, APEX might take it out completely, so it's best to replace them with the new package.

One of those packages announced in Oracle APEX 5.1 that are deprecated, and which I used a lot, was apex_util.string_to_table.

For example, in APEX Office Print (AOP) we read the session state of page items and we have some code like this:

declare
  l_string          varchar2(4000) := 'P1_X:P1_Y';
  l_page_items_arr  apex_application_global.vc_arr2;
begin  
  l_page_items_arr := apex_util.string_to_table(p_string => l_string, p_separator => ':');
  for i in 1..l_page_items_arr.count
  loop
    sys.htp.p(l_page_items_arr(i)||':'||apex_util.get_session_state(l_page_items_arr(i)));
  end loop;
end;    

As the function is deprecated and APEX is already on release 18.1, it's good to start replacing those calls. The new function you can use is apex_string.split.

The above code becomes then:
declare
  l_string          varchar2(4000) := 'P1_X:P1_Y';
  l_page_items_arr  apex_t_varchar2;
begin  
  l_page_items_arr := apex_string.split(p_str => l_string, p_sep => ':');
  for i in 1..l_page_items_arr.count
  loop
    sys.htp.p(l_page_items_arr(i)||':'||apex_util.get_session_state(l_page_items_arr(i)));
  end loop;
end;

Depending on your application, you might need to be careful. For example with AOP, we support customers with versions of Oracle APEX 5.0, 5.1 and 18.1. We can't really force customers to move to higher APEX versions, so the way we solve it is by using conditional compilation of our code. If we see you are on APEX 5.1 or above we will use apex_string.split if not, and you are still on an earlier version, we will use apex_util.string_to_table.

Here's an example of what the code with conditional compilation looks like:
  $if wwv_flow_api.c_current >= 20160824
  $then
    l_page_items_arr := apex_string.split(p_str=>l_string, p_sep=>':');
  $else
    l_page_items_arr := apex_util.string_to_table(p_string=>l_string, p_separator=>':');
  $end

Note the conditional compilation you also need to do on the variable if they are different, or you can choose to conditional compile on the entire function.

To conclude, I recommend with every new release of Oracle APEX to look for deprecated components and search for those and make notes to change those when needed.

Tuesday, July 24, 2018

"ORA-22902: CURSOR expression not allowed" in ORDS and APEX and how to fix them

When you want to define different blocks of data, some hierarchical, some not, you can do that by using the cursor expressions in SQL. An example of a query looks like this:

  select
    c.cust_first_name,
    c.cust_last_name,
    c.cust_city,
    cursor(select o.order_total, order_name,
              cursor(select p.product_name, i.quantity, i.unit_price
                       from demo_order_items i, demo_product_info p
                      where o.order_id = i.order_id
                        and i.product_id = p.product_id
                    ) product                
             from demo_orders o
            where c.customer_id = o.customer_id
          ) orders
  from demo_customers c

In the above query you see you can nest the cursor expressions. But you can also define the cursors next to each other. We use this technique a lot when defining where the data comes from in the APEX Office Print (AOP) APEX plugin:


In Oracle Application Express 18.1 there's a small bug (Bug 28298260 - REGRESSION: SQL QUERY CONTAINING CURSOR EXPRESSION CAN'T BE PARSED) that when you validate the query you get "ORA-22902: CURSOR expression not allowed".

The APEX Dev team already fixed it - you can download from Oracle Support the bundle PSE patch #28128115. Once applied everything is validating correctly again.


AOP also supports REST web services, and some people define those in ORDS (Oracle REST Data Services). Depending the version of ORDS you might get the same error: "Error during evaluation of resource template: GET test/cursor/, SQL Error Code: 22,902, SQL Error Message: ORA-22902: CURSOR expression not allowed"



This doesn't mean your environment can not use the cursor syntax, you just have to set the pagination size to 0 and it's fixed.

In the latest version of ORDS (18.2) you get by default the 500 error without the error number:


Fix is the same, set pagination to 0 and you are good to go.

Sunday, July 22, 2018

Hide certain objects on an APEX page

A few days ago I got a question on how to hide the title row from the Interactive Report Pivot view.
So the person didn't want to show the red area:


The solution to this problem is to add the following CSS to your page:


table.a-IRR-table--pivot tr:nth-child(3) {
    display:none;
}

The result is this - the title is gone:


Doing this blog post is not about giving the solution to the above problem. I find it more important to show you the process to come to your answer. It comes down to find the right elements on the page which you can manipulate with CSS or JavaScript. To hide something, you can either use CSS with display: none or a JavaScript function (or JQuery hide()). The first thing you do is a search for the element. You want to use the Developer Tools of your browser for that. Most of the time you can right click on your page and do Inspect Element. The browser will show the HTML that is behind what you see on the page.


In the above screenshot, I see that row is a TR in a Table.
So the next step is to find a way to select that element. Typically you would use the id or class tag and look that up. The TR in our case doesn't have any of those, so I went up a line in the hierarchy until I find a good selector. The table has a class a-IRR-table--pivot which we can use.
Once we have the selector, we want to go to the real element, so we navigate back down. Now you need to know a bit of JavaScript or CSS or search on the internet how to do that. You can add elements after each other and it will drill down in the hierarchy again.
In our case, the TR is the third TR in the table, and there's a function to select that, which I used in CSS (nth-child).

If this is all new to you, learning about JavaScript and CSS selectors is a great start. For example, W3School is a nice site to get started learning more about HTML, CSS, JavaScript, and general web.


Note that in Oracle APEX, you can also use a dynamic action to hide or show certain elements. A dynamic action is a declarative way to do JavaScript, so when you use the Hide / Show in a dynamic action, behind the scenes it will do the necessary call for you. If the item or region is not known for APEX you would use the same technique as I described above to find the right element which you can reference in the dynamic action with a JavaScript selector (. for class # for id).
Typically doing something with CSS is more performant than doing it with JavaScript, but it all depends on your use case what technique makes sense to use.

Sunday, July 15, 2018

I'll be at APEX Meetup Munich: Thu 19 Jul 2018

Just a quick note I'll do two presentations at the APEX Meetup in Munich on Thursday, July 19th 2018.

In the first presentation I'll bring you to a virtual and augmented world, entirely build in Oracle Application Express (APEX). There are 30 Google Cardboards available to make the experience complete. Fun guaranteed! :)


At KScope I was also interviewed by Bob Rhubart on my talks over there, which the AR/VR presentation was one of them.


In my second presentation at Munich I'll show the upcoming version of APEX Office Print (AOP).
I'll show some features nobody has seen before :) With every major release of AOP I feel like this:


If you are in the Munich area I would love to meet you at the meetup.

Saturday, July 14, 2018

My top 5 APEX 18.1 Plug-ins

With every new version of Oracle Application Express (APEX) new features are added and the life of a developer is made even easier. If the feature set is not enough or you see you need to build the same functionality more often, you can always extend APEX with plug-ins.

There are six different types of plug-ins: dynamic action, item,  region, process, authentication scheme and authorization scheme.

Plug-ins are absolutely fantastic to extend the native functionalities of APEX in a declarative way. The APEX plug-in becomes a declarative option in the APEX Builder and has the [Plug-in] text next to it. In the next screenshot, you see the dynamic actions being extended by two Plug-ins.


If you are searching for an APEX plug-in, I typically go to APEX World > Plug-ins. The nice thing about that site is that the plug-ins seem to be maintained, so if a plug-in is not supported anymore it gets the status deprecated.

!! And here lays the catch with using plug-ins. When you decide to use a plug-in in your project, you become responsible for this and need to make sure it's compatible with every release of Oracle APEX. Many plug-ins are open source and many plug-in developers maintain their plug-ins, but it's really important you understand that at the end you are responsible for things you put in your application. If the plug-in is not secure or it breaks in the next release of APEX, you need to find a solution. So use plug-ins with care and see for example how many likes the plug-in has or what the comments are about the plug-in or author. Oracle is not reviewing or supporting the plug-ins !!

When I saw the tweet of Travis, I thought to do a blog post on my top 5 plug-ins I use in almost every project.


Here we go:

1. Built with love using Oracle APEX

I'm proud to built applications with Oracle Application Express, and this plug-in makes it very clear :) At the bottom of the app, you will see this text:


Note that in Oracle APEX 18.1 this text in included by default and you don't even need to add the plug-in. Nevertheless, I wanted to include it in this list as it should be there in every app, even the ones built before APEX 18.1 :)

2. Select2

When a select list (or drop-down) has many values, it takes too long to find the right value. Select2 makes it easy to search for values, it also supports lazy loading and multiple select.


3. APEX Office Print

APEX Office Print extends APEX so it becomes possible to export to native Excel files and generate documents in Word, Powerpoint, PDF, HTML and Text, all based on your own template. It has many more features, I blogged about some before.



4. Dropzone

APEX 18.1 has declarative multi-file upload, but still, I love the Dropzone plug-in developed by Daniel Hochleitner. You can drag multiple files from your desktop straight in your APEX app. Daniel is one of my favorite plug-in developers. When he releases something, you know it will be good.



5. Modal LOV

This is a newer plug-in and I haven't used it that much yet, but I'm sure I will do. The nice thing with this item type plug-in is that it also supports Interactive Grid. Where Select2 stays within the page, this Modal LOV comes with a modal list of values (pop-up) which is great if you want to show multiple columns or need more context for the record you need to select.


There are many more plug-ins out there, most of them work on APEX 5.x and upwards. For example, Pretius has some cool plug-ins too, the one to create nested reports I recently used in a project. Another site you can find plug-ins is APEX-Plugin.com.

Saturday, June 30, 2018

Automatically capture all errors and context in your APEX application

Let me start this post with a conversation between an end-user (Sarah) and a developer (Harry):

End-user: "Hey there, I'm receiving an error in the app."
Developer: "Oh, sorry to hear that. What is the message saying?"
End-user: "Unable to process row of table EBA_PROJ_STATUS_CATS.  ORA-02292: integrity constraint (XXX.SYS_C0090660) violated - child record found"
Developer: "Oh, what are you trying to do?"
End-user: "I'm trying to delete a category."
Developer: "Oh, most likely this category is in use, so you can't delete the category, you first need ..."
End-user: "Ehh?!"


You might ask yourself, what is wrong with this conversation?

The first thing is that the end-user gets an error which is hard to understand. She probably got the error before but tried a few times before calling the developer (or support). Most likely Sarah has a tight deadline and these errors don't really help their mood. 
The other problem is that the developer was most likely just busy working on some complex logic and now gets interrupted. It takes some minutes before Harry can understand what Sarah is talking about. He needs to ask a few questions to know what Sarah is doing and doesn't have much context. He might ask to send a screenshot of the error and a few minutes later he receives this (app in APEX 5.1):

Harry is a smart cookie, so he knows in which schema to look for that constraint name, so he knows which table it's linked to. If Harry read my previous blog post on how to remotely see what Sarah was doing, he has more context too.

If the application is running in APEX 18.1, it's a different story. The screenshot will look like this:

APEX 18.1 actually enhanced the default error message. The user gets fewer details and sees a debug id. With this debug id the developer can get actually more info in Your App > Utilities > Debug Messages:


You might also want to check this blog post by Joel Kallman where to find more info when receiving an internal error with debug id.

Although APEX 18.1 captures more info, there's a more recommended way to deal with errors.

In APEX you can define an Error Handling Function which will kick in every time an error occurs. You can define this function in the Application Definition:


When you look in the Packaged applications that are shipped with Oracle Application Express (APEX), you find some examples. The above screenshot comes from P-Track.

The error handling function has this definition:

function apex_error_handling (p_error in apex_error.t_error )
  return apex_error.t_error_result

The example used in P-Track gives a good overview (read the comments in the package) of the different errors you want to capture:

function apex_error_handling (
    p_error in apex_error.t_error )
    return apex_error.t_error_result
is
    l_result          apex_error.t_error_result;
    l_constraint_name varchar2(255);
begin
    l_result := apex_error.init_error_result (
                    p_error => p_error );
    -- If it is an internal error raised by APEX, like an invalid statement or
    -- code which can not be executed, the error text might contain security sensitive
    -- information. To avoid this security problem we can rewrite the error to
    -- a generic error message and log the original error message for further
    -- investigation by the help desk.
    if p_error.is_internal_error then
        -- mask all errors that are not common runtime errors (Access Denied
        -- errors raised by application / page authorization and all errors
        -- regarding session and session state)
        if not p_error.is_common_runtime_error then
            add_error_log( p_error );
            -- Change the message to the generic error message which doesn't expose
            -- any sensitive information.
            l_result.message := 'An unexpected internal application error has occurred.';
            l_result.additional_info := null;
        end if;
    else
        -- Always show the error as inline error
        -- Note: If you have created manual tabular forms (using the package
        --       apex_item/htmldb_item in the SQL statement) you should still
        --       use "On error page" on that pages to avoid loosing entered data
        l_result.display_location := case
                                       when l_result.display_location = apex_error.c_on_error_page then apex_error.c_inline_in_notification
                                       else l_result.display_location
                                     end;
        -- If it's a constraint violation like
        --
        --   -) ORA-00001: unique constraint violated
        --   -) ORA-02091: transaction rolled back (can hide a deferred constraint)
        --   -) ORA-02290: check constraint violated
        --   -) ORA-02291: integrity constraint violated - parent key not found
        --   -) ORA-02292: integrity constraint violated - child record found
        --
        -- we try to get a friendly error message from our constraint lookup configuration.
        -- If we don't find the constraint in our lookup table we fallback to
        -- the original ORA error message.
        if p_error.ora_sqlcode in (-1, -2091, -2290, -2291, -2292) then
            l_constraint_name := apex_error.extract_constraint_name (
                                     p_error => p_error );
            begin
                select message
                  into l_result.message
                  from eba_proj_error_lookup
                 where constraint_name = l_constraint_name;
            exception when no_data_found then null; -- not every constraint has to be in our lookup table
            end;
        end if;
        -- If an ORA error has been raised, for example a raise_application_error(-20xxx)
        -- in a table trigger or in a PL/SQL package called by a process and we
        -- haven't found the error in our lookup table, then we just want to see
        -- the actual error text and not the full error stack
        if p_error.ora_sqlcode is not null and l_result.message = p_error.message then
            l_result.message := apex_error.get_first_ora_error_text (
                                    p_error => p_error );
        end if;
        -- If no associated page item/tabular form column has been set, we can use
        -- apex_error.auto_set_associated_item to automatically guess the affected
        -- error field by examine the ORA error for constraint names or column names.
        if l_result.page_item_name is null and l_result.column_alias is null then
            apex_error.auto_set_associated_item (
                p_error        => p_error,
                p_error_result => l_result );
        end if;
    end if;
    return l_result;
end apex_error_handling;

When defining this error handling function the error the user gets is more like a notification message and embedded in your app. You can also define a custom message, in the above package there's a lookup in an error_lookup table, but as it can't find the constraint name, it falls back to the normal message.


The real power comes when you start to combine the error handling function with a call to also log session state information. Then you know exactly which record this error was produced for.

There are a couple of ways to include the session state:

Team Development

I typically include a feedback page in my apps. When the user logs feedback by clicking on the feedback link, this is saved in Team Development. The really cool thing is that whenever feedback is logged, automatically the session state of items and some other info like the browser that was being used at the moment of the logging is included. But you can also log feedback through an APEX API:

apex_util.submit_feedback (
    p_comment         => 'Unexpected Error',
    p_type            => 3,
    p_application_id  => v('APP_ID'),
    p_page_id         => v('APP_PAGE_ID'),
    p_email           => v('APP_USER'),
    p_label_01        => 'Session',
    p_attribute_01    => v('APP_SESSION'),
    p_label_02        => 'Language',
    p_attribute_02    => v('AI_LANGUAGE'),
    p_label_03        => 'Error orq_sqlcode',
    p_attribute_03    => p_error.ora_sqlcode,
    p_label_04        => 'Error message',
    p_attribute_04    => p_error.message,
    p_label_05        => 'UI Error message',
    p_attribute_05    => l_result.message
);


Logger 

Logger is a PL/SQL logging and debugging framework. If you don't know it yet, you should definitely check it out. In my opinion, Logger is the best way to instrument your PL/SQL code. Logger has many cool features, one of them is the ability to log your APEX items:

logger.log_apex_items('Debug Items from Error log');
With the above methods, you know which record the end-user was looking at and what the context was. Note that you might find this information too if you look at their session, but it would take more time to figure things out.

Be pro-active

Now, to prevent the conversation from happening again, you can take it one step further and start logging and monitoring those errors. Whenever errors happen you can, for example, log it in your own error table, or in your support ticket system and send yourself an email or notification.
Then instead of the end-user calling you, you call them and say "Hey, I saw you had some issues...".

By monitoring errors in your application, you can pro-actively take actions :)

Note that APEX itself also stores Application Errors. You find under Monitor Activity > Application Errors:


The report gives the error and the session, so you look further into what happened:


So, even when you didn't have an error handling function in place, you can still start monitoring errors that happen in your app. I know the readers of this blog are really smart so you might not see any errors, but still, it might be worthwhile to check it once and a while :)

You find another example of the error handling function in my Git account. I included an example of logging in your own error table and sending an email.