Saturday, June 28, 2008

Getting friends timeline from Twitter to Oracle APEX (PL/SQL)


Now that you have posted your Twitter updates from APEX, you might want to read your friends time line. The procedure is very similar to posting of updates, except this time you want to receive the XML and process it for display inside a SQL query report. The returning XML will show the 20 most recent statuses posted in the last 24 hours from the authenticating user and that user's friends.


create or replace
FUNCTION get_twitter_timeline(t_user IN VARCHAR2, t_pass IN VARCHAR2)
RETURN XMLTYPE
IS
http_req utl_http.req;
http_resp utl_http.resp;
t_update_send VARCHAR2(200);
res_value VARCHAR2(4000);
XML_RETURN CLOB;
BEGIN
t_update_send := '--head';
--utl_http.set_proxy('http://www-yourproxy.com:80'); --If you need to specify a proxy use this.
http_req := utl_http.begin_request('http://twitter.com/statuses/friends_timeline.xml', 'POST', utl_http.http_version_1_1);
utl_http.set_response_error_check(TRUE);
utl_http.set_detailed_excp_support(TRUE);
utl_http.set_body_charset(http_req, 'UTF-8');
utl_http.set_header(http_req, 'User-Agent', 'Mozilla/4.0');
utl_http.set_header(http_req, 'Content-Type', 'application/x-www-form-urlencoded');
utl_http.set_header(http_req, 'Content-Length', to_char(LENGTH(t_update_send)));
utl_http.set_transfer_timeout(to_char('60'));
utl_http.set_authentication(http_req, t_user, t_pass, 'Basic');
utl_http.write_text(http_req, t_update_send);
http_resp := utl_http.get_response(http_req);
BEGIN
WHILE 1 = 1
LOOP
utl_http.read_line(http_resp, res_value, TRUE);
XML_RETURN := XML_RETURN || res_value;
END LOOP;
EXCEPTION
WHEN utl_http.end_of_body THEN
NULL;
END;
utl_http.end_response(http_resp);
return XMLTYPE(XML_RETURN);
EXCEPTION
WHEN others THEN
XML_RETURN := sqlerrm;
RAISE;
END get_twitter_timeline;

Usage:

SELECT EXTRACTVALUE(VALUE(twitter), '/status/user/name') AS NAME
, EXTRACTVALUE(VALUE(twitter), '/status/text') AS TEXT
, EXTRACTVALUE(VALUE(twitter), '/status/created_at') AS CREATED_AT
, EXTRACTVALUE(VALUE(twitter), '/status/source') AS SOURCE
, EXTRACTVALUE(VALUE(twitter), '/status/user/screen_name') AS SCREEN_NAME
, EXTRACTVALUE(VALUE(twitter), '/status/user/profile_image_url') AS PROFILE_IMAGE_URL
FROM
TABLE(XMLSEQUENCE(EXTRACT(get_twitter_timeline('twitter_user','twitter_password'), '/statuses/*'))) twitter
Note that according to Twitter API the public timeline is cached for 60 seconds so requesting it more often than that is a waste of resources

Happy Posting :)

Tuesday, June 24, 2008

Post Updates to Twitter from Oracle APEX (PL/SQL)


I started a pet project called OraTweet. It is an Oracle internal "Twitter" with some key advantages such as the ability to follow groups and IM (XMPP/Jabber) integration. Aside from having our own internal "tweets" a user can direct a message @oratweet and the application will post it to the generic OraTweet Twitter account. Also the user will be able to store its own credentials and post to its own account (ie @noelportugal). I started playing with the Twitter API and first start sending updates with a combination of a Java Class to execute a shell Curl command. I soon realized that was not the best way to do it, so I went to work and got the following Procedure. (note that this is the quick and dirty version, feel free to extend it and make it more foolproof)

CREATE OR REPLACE PROCEDURE update_twitter(t_user IN VARCHAR2,   t_pass IN VARCHAR2,   t_update IN VARCHAR2) AS
http_req utl_http.req;
http_resp utl_http.resp;
h_name VARCHAR2(255);
h_value VARCHAR2(1023);
t_update_send VARCHAR2(4000);
res_value VARCHAR2(32767);
show_header NUMBER := 0;--0 False, 1 True
show_xml NUMBER := 1;--0 False, 1 True
BEGIN
t_update_send := 'status=' || SUBSTR(t_update, 1, 140) || '';
--utl_http.set_proxy('http://www,yourpoxy.com:80'); --If you need to specify a proxy un comment this line.
http_req := utl_http.begin_request('http://twitter.com/statuses/update.xml', 'POST', utl_http.http_version_1_1);
utl_http.set_response_error_check(TRUE);
utl_http.set_detailed_excp_support(TRUE);
utl_http.set_body_charset(http_req, 'UTF-8');
utl_http.set_header(http_req, 'User-Agent', 'Mozilla/4.0');
utl_http.set_header(http_req, 'Content-Type', 'application/x-www-form-urlencoded');
utl_http.set_header(http_req, 'Content-Length', to_char(LENGTH(t_update_send)));
utl_http.set_transfer_timeout(to_char('60'));
utl_http.set_authentication(http_req, t_user, t_pass, 'Basic');
utl_http.write_text(http_req, t_update_send);
http_resp := utl_http.get_response(http_req);

DBMS_OUTPUT.PUT_LINE('status code: ' || http_resp.status_code);
DBMS_OUTPUT.PUT_LINE('reason phrase: ' || http_resp.reason_phrase);

IF show_header = 1 THEN
FOR i IN 1 .. utl_http.get_header_count(http_resp)
LOOP
utl_http.get_header(http_resp, i, h_name, h_value);
DBMS_OUTPUT.PUT_LINE(h_name || ': ' || h_value);
END LOOP;
END IF;

IF show_xml = 1 THEN
BEGIN
WHILE 1 = 1
LOOP
utl_http.read_line(http_resp, res_value, TRUE);
DBMS_OUTPUT.PUT_LINE(res_value);
END LOOP;

EXCEPTION
WHEN utl_http.end_of_body THEN
NULL;
END;
END IF;

utl_http.end_response(http_resp);

EXCEPTION
WHEN others THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm);
RAISE;

END update_twitter;


Usage:

begin
UPDATE_TWITTER('my_twitter_acct','my_twitter_pass','Hello World from Oracle PL/SQL and APEX http://apextoday.blogspot.com');
end;


My following post will include on how to create an XMPP/Jabber listener with Java and how I hooked it up to my application.

Friday, May 16, 2008

Adding IM (Jabber/XMPP) messaging to Oracle APEX

One of my APEX application relies heavily on email notifications for work flow approval and task assignment notification. A few months ago it occurred to me that it would be really nice to implement IM notification and let users decide how they like to be notified. So I went to work and found a great java open source XMPP/Jabber client called Smack. My first implementation was just a simple class that called smack functions loaded on the DB by loadjava. But I recently found a project started by Adrien Sales that wraps more of the Smack functions such as rosters. To add IM functionality to your application follow these steps:

  1. Download Oracle XMPP/Jabber Instant Messenger for Oracle 11G.
    If you are using Oracle 10g download from here (I had to recompile using an older smack version and JDK1_4)
  2. Login to sqlplus as sysdba and create account XMPP to load java classes and package.
    create user XMPP identified by XMPP
    default tablespace your_tablespace
    temporary tablespace your_temp_tablespace
    quota unlimited on users;
    GRANT "CONNECT" TO XMPP;
    GRANT "DBA" TO XMPP;
  3. From the shell go to the dist folder and excute
    loadjava -u XMPP/XMPP -resolve lib/smack.jar
    loadjava -u XMPP/XMPP -resolve lib/smackx.jar
    loadjava -u XMPP/XMPP -resolve OracleXMPP.jar
  4. Login to sqlplus as sysdba and execute:
    call dbms_java.grant_permission('PUBLIC', 'java.net.SocketPermission', '*', 'accept, connect,
    listen, resolve');
    call dbms_java.grant_permission('PUBLIC', 'java.net.NetPermission', '*', 'accept, connect,
    listen, resolve');
  5. Connect as XMPP/XMPP and run the following script.

  6. Grant privileges on the new package to APEX_PUBLIC_USER:
    grant execute, debug on dbms_xmpp to APEX_PUBLIC_USER;
  7. Login to APEX to SQL Workshop> SQL Commands and test:
    select XMPP.dbms_xmpp.send_plain_text_message('talk.google.com',
    5222,
    'gmail.com',
    'my_user',
    'my_password',
    'recipient@gmail.com',
    'XMPP Test',
    'Hello from Oracle APEX') xmpp_return_code
    from dual;
And thats it! Now if your company happens to be running a XMPP/Jabber server (like we do at Oracle) or if you have a user base that has gmail accounts or any of the other services running on XMPP, this could be a great alternative to emails. This is a huge hit on my applications and I bet anyone can come with more creative ways to include Jabber with APEX. Let me know how you use it.

I am also planning to post soon how I'm running my own Jabber server and how I can integrate it with my APEX application. Stay tuned!

For more information on Jabber please visit http://www.jabber.org/.

Thursday, May 15, 2008

Apache 2 Compression with mod_deflate

Today, after a report from one my users that the our APEX application was getting "slower", I decided to implement mod_deflate for content compression. The performance is noticeable faster! I went with mod_deflate since is included in the Apache 2.0.x source package. Here is what I added to my httpd.conf:

LoadFile /usr/lib/libz.so
LoadModule deflate_module modules/mod_deflate.so

<IfModule mod_deflate.c>
# Insert filter
SetOutputFilter DEFLATE
# Netscape 4.x has some problems...
BrowserMatch ^Mozilla/4 gzip-only-text/html
# Netscape 4.06-4.08 have some more problems
BrowserMatch ^Mozilla/4\.0[678] no-gzip
# MSIE masquerades as Netscape, but it is fine
# BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
# NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48
# the above regex won't work. You can use the following
# workaround to get the desired effect:
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
# Don't compress images
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png|htc|wsdl|exe|t?gz|bz2|sit|rar|pdf|zip)$ no-gzip dont-vary
# Make sure proxies don't deliver the wrong content
Header append Vary User-Agent env=!dont-vary
</IfModule>

After that I all did was a quick
opmnctl restartproc ias-component=HTTP_Server
and voila!

For more information on mod_deflate go to http://httpd.apache.org/docs/2.0/mod/mod_deflate.html

Tuesday, May 13, 2008

Adding Firefox Search Engine to APEX




Today, I got tired of constantly having to login to Metalink to search for SR's that I needed to check. I do have other tools (internal) that let me do the this (oracle forms) but I do prefer to use Metalink since it is built with APEX. So here is what I did to speed up the search process; I added this to my intranet APEX application so my users can install the search engine. I also created an alternate example using Ask Tom's Site.

First I created asktom.xml with the following (note that for space purposes could not include the whole data:image string. To create your own go to The data: URI kitchen).

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/" xmlns:os="http://a9.com/-/spec/opensearch/1.1/">
<os:ShortName>Ask Tom</os:ShortName>
<os:Description>Ask Tom Search</os:Description>
<os:InputEncoding>utf-8</os:InputEncoding>
<os:Image width="16" height="16">data:image/x-icon,%01%01%10%10%01%20h% ...
</os:Image>
<SearchForm>http://asktom.oracle.com/pls/asktom/f?p=100:1</SearchForm>
<os:Url type="text/html" method="GET" template="http://asktom.oracle.com/pls/asktom/f?p=100:1:0::NO:RP:P1_SEARCH_CRITERIA:{searchTerms}">
</os:Url>
</SearchPlugin>

Then I added the following inside the <header> of this blog:

<link href='http://noelportugal.googlepages.com/asktom.xml' rel='search' title='Ask Tom' type='application/opensearchdescription+xml'/>

Now all you have to do (assuming you are using Firefox) is go to the Search engine and go to the bottom where it says: Add "Ask Tom", and thats it!

You can create as many Search Engines and have them available to your Firefox users.
For more info visit http://developer.mozilla.org/en/docs/Creating_OpenSearch_plugins_for_Firefox