Archive

Archive for the ‘Ajax’ Category

Setting and retrieving CLOB values in Apex

February 7, 2014 8 comments

Imagine you have a textarea in your app where you want your users to be able to type in large quantities of text, click a button to store this in the database and then later retrieve this large quantity of text and redisplay it in that textarea.

You can’t do this as you would with other page items (i.e. just submit the page and have the values stored in session state). Any value over 32kb in size won’t work. To get round this there is a technique which involves using an APEX Collection called CLOB_CONTENT. Essentially, you just need the two Javascript functions below to get this working. They are both asynchronous and can be passed a Javascript callback function to be executed once the CLOB value has been set or retrieved.

The Javascript function to save large value to the CLOB_CONTENT Apex Collection


function setApexCollectionClob (pBigValue, callback) {

var apexAjaxObj = new apex.ajax.clob (

function() {

var rs = p.readyState;

if (rs == 4) {

callback();

} else {

return false;

};

}

);

apexAjaxObj._set(pBigValue);

}

Example usage:

setApexCollectionClob ('Some large text value...', function(){alert('Data saved to Apex Collection!')})

The Javascript function to retrieve a large value from the CLOB_CONTENT Apex Collection


function getApexCollectionClob(callback) {

var apexAjaxObj = new apex.ajax.clob (

function() {

var rs = p.readyState;

if(rs==4){

callback(p.responseText);

}else{

return false;

}

}

);

apexAjaxObj._get();
}

Example usage:

getApexCollectionClob (function(pReturnedClobValue){ $('#P1_TEXTAREA').val(pReturnedClobValue) })

Retrieving the set value via PL/SQL in, for example, an Application Process

DECLARE
L_CLOB CLOB
BEGIN
SELECT CLOB001
INTO l_clob
FROM apex_collection
WHERE collection_name = 'CLOB_CONTENT'

INSERT INTO my_table (id, myclob) values (123,l_clob);

HTP.p('SUCCESS');

END;

Categories: Ajax, ApEx

Passing more than 10 values with apex.server.process

February 7, 2014 Leave a comment

You may be familiar with the apex.server.process function exposed by Apex’s Javascript API. It allows you to asynchronously interact with Apex Application Processes.

A simple example would be.

Apex Application Process


HTP.p('You passed "'||APEX_APPLICATION.g_x01 ||'" as the value for x01. ');

HTP.p('You passed "'||APEX_APPLICATION.g_x02 ||'" as the value for x02. ');

HTP.p('You passed "'||APEX_APPLICATION.g_x03 ||'" as the value for x03. ');

Javascript

apex.server.process ( 
  "MY_APP_PROCESS"
,   {   x01: 'my first custom value'
    ,   x02: 'mysecond custom value'
    ,   x03: 'my third custom value'
    }
 , { dataType: 'text'
 ,success: function(pData){alert(pData)}
}
);

If you were to create the Application Process “MY_APP_PROCESS” and run the Javascript above, you’d see an alert popup:

——–

You passed “my first custom value” as the value for x01.
You passed “mysecond custom value” as the value for x02.
You passed “my third custom value” as the value for x03.

———

You can use use x01 through to x10 to pass up to 10 parameters to your application process. What about if you want to pass more than 10 parameters, though? To do this, you first need to create a number of Application Items. You might like to call them :G_11, :G_12, :G_13 etc..

You can then set the values of these items in session state (and hence make them available in your Application Process) by doing the following:

apex.server.process ( 
  "MY_APP_PROCESS"
,   {   x01: 'my first custom value'
    ,   x02: 'mysecond custom value'
    ,   x03: 'my third custom value'
    ,   p_arg_names: ['G_11','G_12','G_13']
    ,   p_arg_values: ['My 11th custom value','My 12th custom value','My 13th custom value']
    }
 , {    dataType: 'text'
    ,   success: function(pData){alert(pData)}
    }
);

Referencing these values inside your Application Process is simply a case of using Bind Variable syntax, e.g.:


HTP.p('You passed "'||APEX_APPLICATION.g_x01 ||'" as the value for x01. ');

HTP.p('You passed "'||APEX_APPLICATION.g_x02 ||'" as the value for x02. ');

HTP.p('You passed "'||APEX_APPLICATION.g_x03 ||'" as the value for x03. ');

HTP.p('You passed "'||:G_11 ||'" as the value for G_11. ');

Categories: Ajax, ApEx Tags:

Apex and SSO blank white screen problem: SOLVED

June 18, 2010 1 comment

Apex and SSO: The White Screen Problem

Unicorn MeatYou may have configured Apex to use Oracle Single Sign On (SSO) as your authentication scheme using instructions such as those found here. If you have, you may have noticed that if you leave the login screen (i.e. login.jsp) sat idly displayed in your browser for more than a certain amount of time (I’ve not done precise timings but it seems to be something like 5 minutes) and then try to login, when you click the Login button you’re presented with a blank white screen and nothing more.

Below, I offer a solution to this problem.

Why it happens

Before we talk about how to fix this, let me describe what I think the problem is here…

When you first request access to your Apex application which has SSO set as its authentication scheme, Apex generates a brand new shiny Apex session id for you and then redirects you to the SSO login screen (login.jsp be default but you may have modified this to be your own custom jsp) for you to enter your credentials. This jsp contains a hidden item with a name of site2pstoretoken (a name mandated by SSO). The value of this hidden item is a long string of seemingly random characters (V1.2~75D127345~6a5DDF88CFDCE765FBEF…). This string is in fact an encoded representation of various bits of data. The important bit of data for us which is encoded in this long string is the URL that SSO should forward us to when we have clicked the Login button and been successfully authenticated by SSO. The actual URL that is encoded in this string which SSO will try to forward us to is:

http://server/pls/apex/f?p=100:10:12345678

Where server and pls/apex and 100 and 10 are replaced by the relevant server name, DAD, application number and page number respectively. The crucial bit is the session id at the end (12345678 in the example). This will be replaced by the brand new shiny Apex session ID that Apex assigned to us. How did this URL get encoded into site2pstoretoken? It was passed when Apex originally redirected us to SSO.

So site2pstoretoken contains a URL to forward to. So what?

Good question. It seems that the brand new shiny Apex session ID which was originally assigned to us has the ability to “time out”. Once it has “timed out”, if you click Login on the login.jsp page, SSO will try to redirect you to f?p=100:10:12345678 (*). Since the session has “timed out”, Apex shows you just a blank white screen.

(*) Note: To be precise, the login.jsp submits to /sso/auth which in turn redirects you to /pls/apex/wwv_flow_custom_auth_sso.process_success. In the redirection to wwv_flow_custom_auth_sso.process_success, the site2pstoretoken is included in the url in the “urlc” parameter (e.g. /pls/apex/wwv_flow_custom_auth_sso.process_success?urlc=V1.2~75D127345~6a5DDF88CFDCE765FBEF…). It is this process_success procedure which appears to display the blank white screen. For info: the wwv_flow_custom_auth_sso is created when you ran custom_auth_sso.sql (see step 5 in the instructions for setting up Apex with SSO). I am not sure exactly what the process_success procedure is checking to cause it to display a white screen (the code is wrapped so can’t be inspected). The only thing I can hazard a guess at is that it looks in the <Apex schema>.wwv_flow_session$ table and if the ON_NEW_INSTANCE_FIRED_FOR column has not been populated within a certain amount of time since the session was created (CREATED_ON column) then it shows a white screen. (The ON_NEW_INSTANCE_FIRED column I’ve noticed is only populated once you actually access a page of your application. Just being redirected to SSO does not populate it.)

Anyway, how do we fix it?

So even though we can’t tell exactly the reason why wwv_flow_custom_auth_sso.process_success displays a white screen if you attempt to login after the login screen has been idle for some time, we can put in place a workaround to stop this happening. If we were to somehow refresh the site2pstoretoken on the login JSP periodically so that the value it held always represented (in an encoded form) a URL to redirect to which contained a valid, non timed-out Apex session then the problem should disappear. This can be achieved with a bit of Ajax in the login.jsp as follows. The javascript is all contained in +”…” syntax because it’s all part of one long out.println() call in your JSP. Also notice that I’ve wrapped 3 of the longer lines (the ones that don’t begin with a + below) just to fit into the format of this blog. You’d need to remove these 3 extra line breaks in your jsp :

+"<script>\n"
+"function getXmlHttpRequestObject() {\n"
+"if (window.XMLHttpRequest) {return new XMLHttpRequest();} \n" 
+"else if(window.ActiveXObject) {return new ActiveXObject('Microsoft.XMLHTTP');} \n" 
+"else { alert('Your browser does not seem to support XMLHTTP.');} \n"
+"}\n"

+"function getToken() { \n"
+"if (receiveReq.readyState == 4 || receiveReq.readyState == 0) { \n"
+"receiveReq.open('GET','http://myserver:7777/gentoken/f?p=234:10',true); \n"
+"receiveReq.onreadystatechange = handleGetToken; \n"
+"receiveReq.send(null);"
+"}"
+"}"

+"function handleGetToken() { \n"
+"if (receiveReq.readyState == 4) { \n"
+"processedResponse = receiveReq.responseText;\n"
+"processedResponse = processedResponse.substring(
   processedResponse.indexOf('VALUE=\"')+7); \n"
+"processedResponse = processedResponse.substring(
   0,processedResponse.indexOf('\"')); \n"
+"document.getElementsByName('site2pstoretoken').item(0).value =
   processedResponse; \n"
+"}"
+"}"
+" \n"
+"var receiveReq = getXmlHttpRequestObject(); \n"
+"setInterval('getToken()',60000);"
+"</script>"

You’ll notice above that the Ajax / XMLHTTP request is made to http://myserver:7777/gentoken/f?p=100:10

In this example configuration, the Infrastructure tier (where SSO is installed) is accessed on port 7777. This is the same port on which you will be accessing login.jsp. The apex application is served using MODPLSQL from a different instance of Apache on port 80. So, really, we would need to make a call to server:80/pls/apex/f?p=100:10 but the problem is that browsers do not permit you to make cross-domain Ajax / XMLHTTP requests. Hence, we make the request to the same domain as we are currently on (myserver.domain.com:7777) and add a proxy entry into the Infrastructure’s httpd.conf file to automatically forward any requests to server:7777/gentoken/ to server:80/pls/apex/. Whether you need to do this kind of configuration of httpd.conf will depend on your setup but for completeness the kind of entry you would need to add to your Infrastructure’s httpd.conf would be as follows:

<IfModule mod_proxy.c>
ProxyRequests On
ProxyPass /gentoken/ http://server:80/pls/apex/
ProxyPassReverse /gentoken/ http://server:80/pls/apex/
</IfModule>

What is all that Javascript doing?

It is essentially requesting access to our Apex application again. Again, Apex realises the application is protected by SSO so assigns us a shiny new Apex session ID and forwards us to SSO. Or rather, it can’t actually forward us in this case because all this is is an XMLHTTP request, but it does still return HTML which, if we were a browser and not an XMLHTTP request, would cause us to redirect to SSO. Our Javascript scrapes the new site2pstoretoken which is contained in this HTML and assigns it to the value of the hidden site2pstoretoken already contained in our login.jsp. It does this every 60 seconds (well under the time period in which the Apex session ids seem to expire). Hence, whenever you click Login on your JSP, whether you’ve had it on screen for 30 seconds or 30 days, you will still be submitting a valid encoded Apex URL and will see your app and not just a blank white screen.

Categories: Ajax, ApEx

Implementing session timeout in your ApEx app – Part 3

October 30, 2008 2 comments

This entry builds on the session timeout functionality described in part 1 and part 2. Be sure to check those posts out first.

One of the comments I received about my last post on session timeout was to do with Oracle Maps/MapViewer. Oracle Maps is a nifty web 2.0 application which enables Google-style maps to be embedded into your web applications, including Apex applications. (Click here for info on Oracle Maps/MapViewer.)

The problem mentioned in the comment was that if you are interacting with your Oracle Maps map for more than 18 minutes then the popup indicating that your session is about to expire will still appear even though you have certainly not been inactive in the application over that time (you have been busy panning, zooming and interacting with your map).

The below is one way to get round this problem. We will add events to the Map object so that the session is kept alive by simply interacting with the map itself (and hence the popup indicating that your session is about to timeout will not popup inappropriately.)

Step 1 – Create an HTML region on your mapping Apex page

Add an HTML region to the Apex page which contains your embedded map.

Set its template to "No template".

Set the source of this region to be the entire contents of the file found here.

Step 2 – Register the new javascript functions with the Oracle Maps map object

If you already have an Oracle Maps map embedded into your application you will have some javascript which sets up the various options for this object (setting its initial X and Y centre position, setting its initial zoom level, adding map decorations etc.). You most likely have this javascript in a .js file which is referenced from your Apex page. What you need to do is add the following code to your javascript. It sets a listener so that whener the user of your application changes the zoom level of the map or recentres the map, their Last Activity Date is updated on the server.

mapview.addEventListener("before_zoom_level_change"
           ,        resetUserLastActivityDateIfNotResetInLastMinute);

mapview.addEventListener("recenter"
           ,        resetUserLastActivityDateIfNotResetInLastMinute);

And there you have it. Simply interacting with the map will now keep the session alive.

Categories: Ajax, ApEx, MapViewer

Implementing session timeout in your ApEx app – Part 2

April 3, 2008 14 comments

This entry builds on the session timeout functionality described in part 1. Be sure to check that out first. In this one I describe how to get your application to popup a warning window just before a user’s session is about to timeout due to inactivity (and give them the option to remain logged in).

STEP 1 – Configure Page Zero

If you do not already have one, create a Page 0 in your application.

Add an HTML region to page 0, calling it “Session Timeout JS”.

Be sure to set its “Template” to “No Template”.

Set its conditions to be “Current Page is NOT in Expression 1” and set “Expression 1” to be the id of your user-friendly “Your session has timed out” page (Please see the previous blog entry in this series for details about this page):

Paste all of the text found in the file here into this region’s source.

When you have pasted the code, you will notice that one of the lines reads:

$x(‘pFlowId’).value+’&p_next_flow_page_sess=’+$x(‘pFlowId’).value+’:3′;

You should change the value of 3 in this line to be whatever the number of your user-friendly “Your session has timed out” page is. (Please see the previous post in this series for details about this page.)

STEP 2 – Setup the Application Process

Select Shared Components > Logic > Application Processes

And click create. Give your Application Process a name of “SET_LAST_ACTIVITY_DATE”. You must not give it a different name as this is referenced from within the javascript you pasted into the region on page zero in the previous step. Set its “Point” to “On Demand: Run this application when requested by a page process”.

Specify the “Process Text” as:

BEGIN

   APP_USERS.set_last_activity_date;

   htp.prn(‘1’);

EXCEPTION

   WHEN OTHERS THEN

      htp.p(‘0’);

END;

Step 3 – Finished. Have another cup of tea.

That’s it. Done.

If you want to test it to make sure it works (but don’t want to have to wait 18 minutes!) you could temporarily alter the timeout values in the javascript on page zero (notice below I have changed them to 1000*20 [i.e. 20 seconds] and 1000*40 [i.e. 40 seconds]).

Now log in to the application and wait.

After 20 seconds you will see a warning:

If you do nothing, after a further 20 seconds the popup will close and you will be logged out of the application.

If you click the link, the popup will close and you will remain logged in.

Categories: Ajax, ApEx, Javascript

Ajax: Hello World

AjaxThere are lots of pre-built javascript functions you can get your hands on which offer Ajax functionality (such as ApEx’s htmldb_Get() function). On the one hand, these are great for productivity because they remove much of the complexity of writing Ajaxified javascript. On the other, they can make it very difficult to understand the basic underlying concepts: “What actually happens with all this Ajax stuff?”

Initially, to get a handle on what Ajax “does” and how, in code, it does it, I found it very useful to write the most basic function I could, working on the principle that one good example is as good as a hundred lines of explanation. So here it is, an Ajaxified “Hello World”.

You can see it in action here: http://andrew.tulley.co.uk/blogfiles/ajax_hello_world.html

And the code:

<html>
<head>
<script>
function getXmlHttpRequestObject() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest(); //Not IE
} else if(window.ActiveXObject) {
return new ActiveXObject("Microsoft.XMLHTTP"); //IE
} else {
alert("Your browser can't handle AJAX type stuff. Better upgrade to F1r3foks.");
}
}

function sayHello() {
if (receiveReq.readyState == 4 || receiveReq.readyState == 0) {
receiveReq.open('GET','hello_world.txt',true);
receiveReq.onreadystatechange = handleSayHello;
receiveReq.send(null);
}
}

function handleSayHello() {
if (receiveReq.readyState == 4) {
document.getElementById('receiveDiv').innerHTML = receiveReq.responseText;
}
}

var receiveReq = getXmlHttpRequestObject();
</script>
</head>

<body>
<h3>Ajax Hello World</h3>
<div>
<a href=”javascript:sayHello();”>[ Hello World! ]</a>
</div>

<div name=”receiveDiv” id=”receiveDiv” class=”textDiv1″>
</div>
</body>
</html>

Categories: Ajax