<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link rel="hub" href="http://tumblr.superfeedr.com/" xmlns:atom="http://www.w3.org/2005/Atom"/><description>Hello, and thanks for visiting my blog! My name is Toby Marks, and I am a consultant with Enkitec, an Oracle partner headquartered in Dallas, Texas specializing in DBA services and Exadata implementations. I have worked as a developer-consultant for 15 years in the Dallas/Ft. Worth area. In this blog I hope to share some of my experiences, perspectives, and solutions to problems I have encountered at the various places I have worked.             Enkitec Blogroll    Enkitec      Kerry Osborne      Jack Augustin      Scott Spendolini      Doug Galt      Karen Morton      Karl Arao      Tanel Poder      Kellyn Pot’Vin      Andy Colvin      Christoph Ruepprich    </description><title>Toby's Tumblr</title><generator>Tumblr (3.0; @tjmoracle)</generator><link>http://tjmoracle.tumblr.com/</link><item><title>Button Hotkeys in SQL Developer on OS X</title><description>&lt;p&gt;In Windows, most applications provide menu and button accelerator keys that allow you to control the application without having to use a mouse. Java Swing apps like SQL Developer allow you to do the same, ostensibly in a cross-platform compatible manner.&lt;/p&gt;

&lt;p&gt;One place I use this frequently is the Enter Binds dialog window that pops up when running a query using bind variables.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/a6e8e7f3b03f651923a52d1522777da4/tumblr_inline_mgdgtwosra1r8k26w.png" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;In Java 6 on OS X, the hotkey for pressing the Help and Apply buttons was OPTION-H and OPTION-A, respectively.&lt;/p&gt;

&lt;p&gt;I initially thought this capability had been broken in Java 7 on OS X, but I discovered today that it has been merely changed to CONTROL-OPTION-H and CONTROL-OPTION-A.&lt;/p&gt;

&lt;p&gt;Does anyone know if this is a configurable property of the application or Java virtual machine? If possible, I&amp;#8217;d like to set it back to the simpler 1.6 way.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/40107975599</link><guid>http://tjmoracle.tumblr.com/post/40107975599</guid><pubDate>Wed, 09 Jan 2013 14:09:45 -0500</pubDate><category>os</category><category>java</category><category>shortcut</category><category>hotkey</category><category>accelerator</category><category>sql developer</category></item><item><title>Using Jaspersoft’s free iReport Designer tool and the open...</title><description>&lt;iframe src="http://player.vimeo.com/video/55818132" width="400" height="300" frameborder="0"&gt;&lt;/iframe&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Using Jaspersoft’s free iReport Designer tool and the open source PL-JRXML2PDF project, you can generate PDF reports from Oracle Apex for free, without having to install the JasperReports and JasperReports Server components. Watch my video tutorial to find out how.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/38198419276</link><guid>http://tjmoracle.tumblr.com/post/38198419276</guid><pubDate>Mon, 17 Dec 2012 22:03:42 -0500</pubDate><category>oracle</category><category>oracle application express</category><category>apex</category><category>pl-jrxml2pdf</category><category>ireports designer</category><category>jaspersoft</category><category>jasperreports</category><category>jasperreports server</category></item><item><title>Fall is almost over, but it hardly feels like winter here in...</title><description>&lt;img src="http://25.media.tumblr.com/303dc66aa96ee7a78618e22aab329585/tumblr_meksoeILsU1rrud7ao4_500.jpg"/&gt;&lt;br/&gt; &lt;br/&gt;&lt;img src="http://24.media.tumblr.com/ae6ad2c1f42c0737efeba6264bc15219/tumblr_meksoeILsU1rrud7ao1_500.jpg"/&gt;&lt;br/&gt; &lt;br/&gt;&lt;img src="http://25.media.tumblr.com/436ad92c45f0d7992c009b6d76964086/tumblr_meksoeILsU1rrud7ao3_500.jpg"/&gt;&lt;br/&gt; &lt;br/&gt;&lt;p&gt;Fall is almost over, but it hardly feels like winter here in Dallas.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/37279052943</link><guid>http://tjmoracle.tumblr.com/post/37279052943</guid><pubDate>Wed, 05 Dec 2012 15:53:01 -0500</pubDate></item><item><title>Query an XML File Like An External Table</title><description>&lt;p&gt;Oracle&amp;#8217;s &lt;a href="http://docs.oracle.com/cd/B28359_01/server.111/b28319/et_concepts.htm"&gt;external table feature&lt;/a&gt; allows you to treat structured files on the database server as tables for query and DML purposes. To use an example from the linked documentation, a tab-delimited file like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;56november, 15, 1980  baker             mary       alice     09/01/2004
87december, 20, 1970  roper             lisa       marie     01/01/1999
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&amp;#8230;could be queried like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SELECT employee_number,
       employee_first_name,
       substr(employee_middle_name, 1, 1),
       employee_last_name,
       employee_hire_date,
       to_date(employee_dob,'month, dd, yyyy')
  FROM employees;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After defining the external table like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CREATE TABLE employees
       (employee_number      CHAR(5),
        employee_dob         CHAR(20),
        employee_last_name   CHAR(20),
        employee_first_name  CHAR(15),
        employee_middle_name CHAR(15),
        employee_hire_date   DATE)
     ORGANIZATION EXTERNAL
       (TYPE ORACLE_LOADER
        DEFAULT DIRECTORY def_dir1
        ACCESS PARAMETERS
          (RECORDS DELIMITED BY NEWLINE
           FIELDS (employee_number      CHAR(2),
                   employee_dob         CHAR(20),
                   employee_last_name   CHAR(18),
                   employee_first_name  CHAR(11),
                   employee_middle_name CHAR(11),
                   employee_hire_date   CHAR(10) date_format DATE mask "mm/dd/yyyy"
                  )
          )
        LOCATION ('info.dat')
       );
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Assuming of course that the file&amp;#8217;s name is &lt;code&gt;info.dat&lt;/code&gt;, and its path on the filesystem has already been created as a directory object &lt;code&gt;DEF_DIR1&lt;/code&gt; in the database.&lt;/p&gt;

&lt;p&gt;A similar technique exists for querying simple XML files, taking advantage of &lt;a href="http://docs.oracle.com/cd/E11882_01/appdev.112/e23094/toc.htm"&gt;Oracle&amp;#8217;s XML DB&lt;/a&gt; features. &lt;em&gt;HT to &lt;a href="https://forums.oracle.com/forums/profile.jspa?userID=695787"&gt;odie_63&lt;/a&gt; on the XML DB forums for introducing me to this method.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Suppose you have a simple XML file (elements, but no attributes; no complex column types - it could work for the preceding, but I&amp;#8217;m not sure) like so.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;Employees&amp;gt;
    &amp;lt;Employee&amp;gt;
        &amp;lt;id&amp;gt;1234&amp;lt;/id&amp;gt;
        &amp;lt;lastname&amp;gt;Jetson&amp;lt;/lastname&amp;gt;
        &amp;lt;firstname&amp;gt;George&amp;lt;/firstname&amp;gt;
        &amp;lt;department&amp;gt;10&amp;lt;/department&amp;gt;
        &amp;lt;salary&amp;gt;50000.00&amp;lt;/salary&amp;gt;
    &amp;lt;/Employee&amp;gt;
    &amp;lt;Employee&amp;gt;
        &amp;lt;id&amp;gt;1235&amp;lt;/id&amp;gt;
        &amp;lt;lastname&amp;gt;Crackorn&amp;lt;/lastname&amp;gt;
        &amp;lt;firstname&amp;gt;James&amp;lt;/firstname&amp;gt;
        &amp;lt;department&amp;gt;10&amp;lt;/department&amp;gt;
        &amp;lt;salary&amp;gt;40000.00&amp;lt;/salary&amp;gt;
    &amp;lt;/Employee&amp;gt;
    &amp;lt;Employee&amp;gt;
        &amp;lt;id&amp;gt;1236&amp;lt;/id&amp;gt;
        &amp;lt;lastname&amp;gt;Hoffman&amp;lt;/lastname&amp;gt;
        &amp;lt;firstname&amp;gt;Burl&amp;lt;/firstname&amp;gt;
        &amp;lt;department&amp;gt;20&amp;lt;/department&amp;gt;
        &amp;lt;salary&amp;gt;75000.00&amp;lt;/salary&amp;gt;
    &amp;lt;/Employee&amp;gt;
&amp;lt;/Employees&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Place it on the database server in a readable directory, e.g. &lt;code&gt;/home/oracle&lt;/code&gt;. Name it &lt;code&gt;employees.xml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now execute the following in SQL*Plus:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SQL&amp;gt; CREATE OR REPLACE DIRECTORY XML_DIR AS '/home/oracle';

SQL&amp;gt; CREATE OR REPLACE VIEW employees_v AS
SELECT *
FROM XMLTable('/Employees/Employee'
        passing xmltype(
                 bfilename('XML_DIR','employees.xml')
                , nls_charset_id('AL32UTF8')
                )
        columns id           number path 'id'
              , lastname     varchar2(30) path 'lastname'
              , firstname    varchar2(30) path 'firstname'
              , department   number path 'department'
              , salary       number path 'salary'
       );

SQL&amp;gt; SELECT * FROM employees_v;
ID   LASTNAME FIRSTNAME DEPARTMENT SALARY
---- -------- --------- ---------- ------
1234 Jetson   George    10         50000    
1235 Crackorn James     10         40000
1236 Hoffman  Burl      20         75000
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can modify construction of the view to read from multiple files by using a simple &lt;code&gt;UNION&lt;/code&gt; operator, or you can make the source filename dynamic so you can switch datasets at runtime. Here&amp;#8217;s how you might do something like that:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SQL&amp;gt; CREATE OR REPLACE VIEW employees_v AS
SELECT *
FROM XMLTable('/Employees/Employee'
        passing xmltype(
                 bfilename('XML_DIR', userenv('CLIENT_INFO'))
                , nls_charset_id('AL32UTF8')
                )
        columns id           number path 'id'
              , lastname     varchar2(30) path 'lastname'
              , firstname    varchar2(30) path 'firstname'
              , department   number path 'department'
              , salary       number path 'salary'
       );
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then at runtime, specify the file you want to use like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SQL&amp;gt; exec dbms_application_info.set_client_info('employees_01.xml');
SQL&amp;gt; SELECT * FROM employees_v;
ID   LASTNAME FIRSTNAME DEPARTMENT SALARY
---- -------- --------- ---------- ------
1234 Jetson   George    10         50000    
1235 Crackorn James     10         40000
1236 Hoffman  Burl      20         75000

SQL&amp;gt; exec dbms_application_info.set_client_info('employees_02.xml');
SQL&amp;gt; SELECT * FROM employees_v;
ID   LASTNAME FIRSTNAME DEPARTMENT SALARY
---- -------- --------- ---------- ------
1234 Ringwald Malory    30         63000    
1235 Dulles   Bob       10         100000
1236 Stein    Frank     50         55000
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Why might this be useful? Suppose you have an arbitrary number of identically structured XML files in a directory that you want to import into a relational database table. Using a shell script you can, for each file in the directory,&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;log in to SQL*Plus&lt;/li&gt;
&lt;li&gt;set the &lt;code&gt;CLIENT_INFO&lt;/code&gt; variable to the name of the current file&lt;/li&gt;
&lt;li&gt;perform a simple insert into your table reading all records from the view&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;You might even create a cron job to monitor a particular directory for new XML files, processing them into tables and then deleting or archiving the files when done.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/35671272213</link><guid>http://tjmoracle.tumblr.com/post/35671272213</guid><pubDate>Tue, 13 Nov 2012 19:53:00 -0500</pubDate><category>oracle</category><category>XML DB</category><category>SQL</category></item><item><title>Restricting Access to BI Publisher Reports</title><description>&lt;p&gt;In &lt;a href="http://tjmoracle.tumblr.com/post/35571115921/configure-oracle-bi-publisher-to-authenticate-with"&gt;my last post&lt;/a&gt;  I described how to configure BI Publisher to authenticate against an LDAP server, typically a corporate Active Directory server. Aside from authentication, you can also use LDAP group membership to determine who has access to a given report. Here&amp;#8217;s how you do it.&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Create a group in your LDAP/AD directory and assign your target users to that group.&lt;/li&gt;
&lt;li&gt;Revisit your LDAP authentication settings. These are located on the Admin Tab, on the Security Configuration page. In particular, look at the value for Group Search Filter.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;e.g.&lt;br/&gt;
(&amp;amp;(objectClass=group)(|(CN=XMLP*)(CN=DEPT_GROUP_*)))&lt;/p&gt;

&lt;p&gt;In case you don&amp;#8217;t recognize it, this is an LDAP query string. This query string determines which groups BI Publisher &amp;#8220;sees&amp;#8221;, or recognizes, when setting up report access. The groups returned by this query will determine the set from which you can choose when configuring which groups see which reports.&lt;/p&gt;

&lt;p&gt;Here is a deconstruction of the query string:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(&amp;amp;&lt;/strong&gt; &lt;em&gt;Logical AND over the following conditions&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(objectClass=group)&lt;/strong&gt; &lt;em&gt;Return LDAP groups, as opposed to users or computers, etc.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(|&lt;/strong&gt; &lt;em&gt;Logical OR over the following conditions&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(CN=XMLP*)&lt;/strong&gt; &lt;em&gt;Return groups whose name starts with &amp;#8220;XMLP&amp;#8221;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(CN=DEPT_GROUP_*)&lt;/strong&gt; &lt;em&gt;Return groups whose name starts with &amp;#8220;DEPT_GROUP_&amp;#8221;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A quick primer on LDAP query syntax &lt;a href="http://wiki.pentaho.com/display/ServerDoc2x/LDAP+Search+Filter+Syntax"&gt;can be found here.&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Modify this query string to include the group you just created.&lt;/li&gt;
&lt;li&gt;Bounce BI Publisher.&lt;/li&gt;
&lt;li&gt;Log in to BI Publisher as an administrator. Go to the Admin tab, and then to the Roles and Permissions page.&lt;/li&gt;
&lt;li&gt;Click on the role/group you just created, and using the shuttle control on the next page, select which reports your new group should have access to. If necessary, you may need to revoke permissions on your report from other groups, or move your report to the top level of the report folder hierarchy in order to segregate access appropriately.&lt;/li&gt;
&lt;/ol&gt;</description><link>http://tjmoracle.tumblr.com/post/35573375565</link><guid>http://tjmoracle.tumblr.com/post/35573375565</guid><pubDate>Mon, 12 Nov 2012 13:12:00 -0500</pubDate></item><item><title>Configure Oracle BI Publisher to Authenticate With Active Directory</title><description>&lt;ul&gt;&lt;li&gt;Log in to BI Publisher as an administrator.&lt;/li&gt;
&lt;li&gt;Click on the Admin tab.&lt;/li&gt;
&lt;li&gt;Click on Security Configuration.&lt;/li&gt;
&lt;li&gt;For Security Model, choose LDAP and enter the following parameters:&lt;/li&gt;
&lt;/ul&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Setting&lt;/th&gt;
  &lt;th&gt;Example Value&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;URL&lt;/td&gt;
  &lt;td&gt;ldap://ad.mycompany.com:389&lt;br/&gt;&lt;cite style="color:red; font-size:smaller"&gt;Your company&amp;#8217;s LDAP server and port.&lt;/cite&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Administrator Username&lt;/td&gt;
  &lt;td&gt;CN=ADMIN,OU=Users,OU=Shared Services,DC=mycompany,DC=com&lt;br/&gt;&lt;cite style="color:red; font-size:smaller"&gt;The fully qualified DN of the account you use to sign in to LDAP.&lt;/cite&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Administrator Password&lt;/td&gt;
  &lt;td&gt;myxlplyx&lt;br/&gt;&lt;cite style="color:red; font-size:smaller"&gt;The password for the above account.&lt;/cite&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Distinguished Name for Users&lt;/td&gt;
  &lt;td&gt;DC=mycompany,DC=com&lt;br/&gt;&lt;cite style="color:red; font-size:smaller"&gt;The &amp;#8220;root&amp;#8221; level at which to search for users.&lt;/cite&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Distinguished Name for Groups&lt;/td&gt;
  &lt;td&gt;OU=Global Groups,DC=mycompany,DC=com&lt;br/&gt;&lt;cite style="color:red; font-size:smaller"&gt;The &amp;#8220;root&amp;#8221; level at which to search from groups.&lt;/cite&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Group Search Filter&lt;/td&gt;
  &lt;td&gt;(&amp;amp;(objectClass=group)(|(CN=XMLP&lt;em&gt;)(CN=DEPT_GROUP_&lt;/em&gt;)))&lt;br/&gt;&lt;cite style="color:red; font-size:smaller"&gt;LDAP query string defines which groups are relevant to BI Publisher.&lt;/cite&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Group Attribute Name&lt;/td&gt;
  &lt;td&gt;cn&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Group Member Attribute Name&lt;/td&gt;
  &lt;td&gt;member&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Member Of Group Attribute Name&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Group Description Attribute Name&lt;/td&gt;
  &lt;td&gt;description&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JNDI Context Factory Class&lt;/td&gt;
  &lt;td&gt;com.sun.jndi.ldap.LdapCtxFactory&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Group Retrieval Page Size&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;attribute used for RDN&lt;/td&gt;
  &lt;td&gt;sAMAccountName&lt;br/&gt;&lt;cite style="color:red; font-size:smaller"&gt;Allows users to log in to BI Publisher using their corporate AD account name.&lt;/cite&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Automatically clear LDAP cache&lt;/td&gt;
  &lt;td&gt;Checked&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ldap Cache Interval&lt;/td&gt;
  &lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ldap Cache Interval Unit&lt;/td&gt;
  &lt;td&gt;Hour&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Default User Group Name&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Attribute Names for Data Query Bind Variables&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;ul&gt;&lt;li&gt;Bounce BI Publisher. &lt;/li&gt;
&lt;li&gt;Try logging in with your corporate AD credentials. Remember, you need to be assigned to one of the following special groups in Active Directory if you want developer or admin privileges in BI Publisher:&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;XMLP_ADMIN&lt;br/&gt;
XMLP_DEVELOPER&lt;br/&gt;
XMLP_SCHEDULER&lt;br/&gt;
XMLP_ANALYZER_EXCEL&lt;br/&gt;
XMLP_ANALYZER_ONLINE&lt;br/&gt;
XMLP_TEMPLATE_DESIGNER&lt;/p&gt;

&lt;p&gt;You can read more about those roles and what they do &lt;a href="http://docs.oracle.com/cd/E10383_01/doc/bip.1013/b40017/T421739T475591.htm"&gt;in the Oracle Documentation&lt;/a&gt;.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/35571115921</link><guid>http://tjmoracle.tumblr.com/post/35571115921</guid><pubDate>Mon, 12 Nov 2012 12:32:00 -0500</pubDate><category>BI Publisher</category><category>Active Directory</category><category>LDAP</category><category>oracle</category></item><item><title>Use Firebug's console.debug Command to Analyze Javascript, DOM Objects</title><description>&lt;p&gt;Occasionally when debugging my Javascript code I need to be able to analyze the properties of an unknown object; for instance, an unexpected DOM object passed to an event handler. How do you analyze the properties of an object when you don&amp;#8217;t know what the object is? I was looking for a way to list all the properties of a generic Javascript or DOM object when I stumbled across this blog post by Andrew Peters. In it, he very clearly outlines two ways of doing this, but the one I found most useful was Firebug&amp;#8217;s &lt;code&gt;console.debug&lt;/code&gt; command. You&amp;#8217;ll need to use Firefox with the Firebug extension for this, but the results are impressive.&lt;/p&gt;

&lt;p&gt;When you want to debug an object, simply pass it as a parameter to &lt;code&gt;console.debug&lt;/code&gt;. Here is an example where I wanted to find out about the event trigger object passed to my event handler:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_mciqg39F641r8k26w.png" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;After triggering the event, I opened the Firebug window and navigated to the console. From there I could drill down on the displayed results to get information about my unknown DOM object.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_mciqzdxYjD1r8k26w.png" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_mciqznfhu31r8k26w.png" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;Turns out in this case that the event handler was attached to the wrong object, due to the &amp;#8220;id&amp;#8221; value being placed one level above in the DOM hierarchy than I was expecting. Tricky, but I was able to spot the problem immediately using this method.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/34369068709</link><guid>http://tjmoracle.tumblr.com/post/34369068709</guid><pubDate>Fri, 26 Oct 2012 17:09:00 -0400</pubDate></item><item><title>BI Publisher: "Job scheduling failed because the user has no permission to access this report"</title><description>&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m9fl13mfcz1r8k26w.jpg" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;You can get this error if you are trying to schedule a report and your BI Publisher installation uses LDAP authentication.&lt;/p&gt;

&lt;p&gt;To avoid it, make sure that the &amp;#8220;Administrator&amp;#8221; account you use to connect to your LDAP server is in the XMLP_ADMIN group. The LDAP settings are located on the Admin tab, under Security Settings / Security Configuration.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m9flg1Mf0h1r8k26w.jpg" alt=""/&gt;&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/30335682577</link><guid>http://tjmoracle.tumblr.com/post/30335682577</guid><pubDate>Mon, 27 Aug 2012 16:48:08 -0400</pubDate><category>BI Publisher</category><category>LDAP</category></item><item><title>Fix for Page Load Issues in Safari 5.1.7</title><description>&lt;a href="https://discussions.apple.com/thread/4054658?start=0&amp;tstart=0"&gt;Fix for Page Load Issues in Safari 5.1.7&lt;/a&gt;: &lt;p&gt;Lately I had been experiencing strange problems when clicking on links in Safari 5.1.7. Usually the progress indicator would appear for only a second with no obvious results, or else never appear at all. Pages would sometimes load, but after an unusual delay and with no indication that the browser was actually doing anything. I switched to Chrome as my default browser for a few days, but that was about all I could stand. A little research on the forums showed this fix, which appears to have worked for me.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/27981866639</link><guid>http://tjmoracle.tumblr.com/post/27981866639</guid><pubDate>Wed, 25 Jul 2012 10:29:37 -0400</pubDate></item><item><title>Easy Fix for High CPU Usage in SQL Developer on OS X</title><description>&lt;p&gt;I&amp;#8217;ve been fighting for a couple of years now with an annoying performance bug in SQL Developer for OS X. After using the application for a while CPU usage would spike to around 100%, even when idle. This is a known issue associated with the &amp;#8220;Look and Feel&amp;#8221; preference.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m7gvy6DsNu1r8k26w.jpg" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;&lt;!-- more --&gt;&lt;/p&gt;

&lt;p&gt;Basically, don&amp;#8217;t use the &amp;#8220;Mac OS X&amp;#8221; setting. Instead, use the &amp;#8220;Oracle&amp;#8221; setting. The theme doesn&amp;#8217;t matter.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m7gvzvk2fM1r8k26w.jpg" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;That fixes the CPU issue, but somewhat annoyingly moves the menu out of the menu bar at the top of the screen and into the application window, a la Windows, making the interface inconsistent with pretty much every other app I use on my Mac. &lt;a href="https://forums.oracle.com/forums/thread.jspa?threadID=1980688&amp;amp;start=15&amp;amp;tstart=30"&gt;Until recently&lt;/a&gt;, that is. The big news is that &lt;em&gt;as of SQL Developer 3.1, the menu is restored to the top of the screen&lt;/em&gt; when using the Oracle Look and Feel. So for those who had been holding out (like myself) with the more native LaF, you can make the switch now and avoid the most annoying part of the performance/usability compromise.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m7gwg1DiV11r8k26w.jpg" alt=""/&gt;&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/27633030181</link><guid>http://tjmoracle.tumblr.com/post/27633030181</guid><pubDate>Fri, 20 Jul 2012 12:32:00 -0400</pubDate><category>oracle</category><category>sql developer</category><category>os x</category></item><item><title>Run Oracle Forms 11g Applications on OS X Lion</title><description>&lt;p&gt;Need to run your company&amp;#8217;s Oracle Forms application from your Mac, and can&amp;#8217;t? Annoyed with having to crank up a virtual machine for just that one task? Even with the latest published Java version for OS X (1.6.0_33, as of today), you may find that some of the newer Forms applications just won&amp;#8217;t run. Well, fret no longer. A solution may already be here, at least for Safari and Firefox.&lt;/p&gt;

&lt;p&gt;Although it&amp;#8217;s not a strictly certified configuration, you may have better luck by running the JRE 1.7 Java plug-in, which was recently made available as a pre-release download. This works for OS X Lion 10.7.3 and above. Download the plug-in &lt;a href="http://jdk7.java.net/macportpreview/"&gt;from here&lt;/a&gt; and follow the simple instructions on the page to install it. Now give your application another try, and hopefully you&amp;#8217;ll get the results I did!&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m72j2akgXi1r8k26w.jpg" alt=""/&gt;&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/27079304140</link><guid>http://tjmoracle.tumblr.com/post/27079304140</guid><pubDate>Thu, 12 Jul 2012 18:28:24 -0400</pubDate><category>os x</category><category>lion</category><category>java</category><category>oracle</category><category>oracle forms</category><category>java 1.7</category><category>forms</category><category>10.7</category><category>11g</category></item><item><title>Use a Perl CGI Script to Show Real Web Server Host Name</title><description>&lt;p&gt;Here is a short and simple Perl CGI script that you can use to expose the hostname of your web server, as opposed to just the server/domain portion of the URL string:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/perl

print "Content-type: text/html\n\n";
print `hostname`;

1;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;!-- more --&gt;&lt;/p&gt;

&lt;p&gt;Name the file something like &lt;code&gt;hostname.cgi&lt;/code&gt; and place it in your web server&amp;#8217;s &lt;code&gt;/cgi-bin/&lt;/code&gt; directory, or if your web server is configured appropriately, just name it &lt;code&gt;hostname.pl&lt;/code&gt; and drop it wherever you like.&lt;/p&gt;

&lt;p&gt;The following script can be used to dump all the CGI environment variables.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "&amp;lt;font size=+1&amp;gt;Environment&amp;lt;/font&amp;gt;\n";
foreach (sort keys %ENV)
{
  print "&amp;lt;b&amp;gt;$_&amp;lt;/b&amp;gt;: $ENV{$_}&amp;lt;br&amp;gt;\n";
}

1;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that this includes &lt;code&gt;SERVER_NAME&lt;/code&gt;, but what that really gives you is the &amp;#8220;server&amp;#8221; portion of the URL string. In other words, if your URL is &lt;a href="http://www.google.com/,"&gt;http://www.google.com/,&lt;/a&gt; then the &lt;code&gt;SERVER_NAME&lt;/code&gt; CGI environment variable would be &amp;#8220;www.google.com&amp;#8221;. Fine, but the first script can be used to identify the actual server being called, for example, when your page is hosted on multiple servers load-balanced under a virtual domain name.&lt;/p&gt;

&lt;p&gt;Of course, you could combine the two scripts to give you everything:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "&amp;lt;font size=+1&amp;gt;Environment&amp;lt;/font&amp;gt;\n";
foreach (sort keys %ENV)
{
  print "&amp;lt;b&amp;gt;$_&amp;lt;/b&amp;gt;: $ENV{$_}&amp;lt;br&amp;gt;\n";
}

print "&amp;lt;b&amp;gt;REAL SERVER HOSTNAME:&amp;lt;/b&amp;gt; ",`hostname`;

1;
&lt;/code&gt;&lt;/pre&gt;</description><link>http://tjmoracle.tumblr.com/post/26376309788</link><guid>http://tjmoracle.tumblr.com/post/26376309788</guid><pubDate>Mon, 02 Jul 2012 18:40:00 -0400</pubDate><category>perl</category><category>cgi</category></item><item><title>OS X Software for Oracle Developers</title><description>&lt;p&gt;One of the great things about working for &lt;a href="http://www.enkitec.com/"&gt;Enkitec&lt;/a&gt; is that it&amp;#8217;s a very Mac-friendly shop. When I arrived there in 2007 there were a handful of us running Macs and the attitude at the top seemed to be respectful, but skeptical. They allowed me to use my own laptop, which at the time was a 15-inch Intel Core2 Duo Macbook Pro, with the understanding that I could request a standard-issue Dell laptop at any time should the need arise. It never did. Since that time a large number of my coworkers have made the switch, abandoning their bulky, slow Dell Latitude laptops for 13-inch Macbook Pros, as have both owners of the company.&lt;/p&gt;

&lt;p&gt;&lt;!-- more --&gt;&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve been a Mac user since 1998, since the advent of the original &lt;a href="http://en.wikipedia.org/wiki/IMac_G3"&gt;bondi blue iMac&lt;/a&gt;, a machine that I greatly loved, as far as you can love a machine (and no farther!). I&amp;#8217;ve used a Mac at work since about the year 2000, a good while before &lt;a href="http://en.wikipedia.org/wiki/OS_X"&gt;OS X&lt;/a&gt; really hit the mainstream. Back in those days about the only thing I could hope to accomplish on it at the office was email, iTunes, looking up something on the internet, and running an &lt;code&gt;SSH&lt;/code&gt; session or two. Still, I faithfully carried my little &lt;a href="http://www.everymac.com/systems/apple/ibook/specs/ibook_900_14.html"&gt;white plastic iBook G3&lt;/a&gt; to work every day just because I enjoyed using the damn thing so much. I looked for any opportunity at all to integrate it into whatever I was doing at work. Over the years that came to include more and more stuff. I still remember how excited I was the day Oracle Applications earned &lt;a href="http://support.apple.com/kb/DL1184?viewlocale=en_US&amp;amp;locale=en_US"&gt;Gold Certification on OS 9&lt;/a&gt;! Woohoo! I desperately needed a life.&lt;/p&gt;

&lt;p&gt;The advent and acceptance of OS X changed everything. The Unix underpinnings gave legitimacy to the Mac as a serious &amp;#8220;office&amp;#8221; platform. I went from using my laptop as a toy to using it as a companion system, then a few years later as my primary system, then finally as my only system. Likewise, the number of Windows applications I needed to run on a daily basis, even in VMWare, was reduced from several to none. Hallelujah. In the same timeframe I also went from being the &amp;#8220;office mac weirdo&amp;#8221; to one of several &amp;#8220;elite mac-using jackasses&amp;#8221;, to just another one of the guys. Or so they tell me to my face.&lt;/p&gt;

&lt;p&gt;One of the questions I&amp;#8217;m asked most often by coworkers &lt;a href="http://www.youtube.com/watch?v=l2-UuIEOcss"&gt;switching to the Mac&lt;/a&gt; is what software they should download first. This is my list.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[Note: I provide links to the developer&amp;#8217;s sites, but many of these apps are available in the App Store.]&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;Crucial Stuff&lt;/h2&gt;

&lt;p&gt;These are the tools any Mac user, but particularly those working in IT, should have in their arsenal.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href="http://manytricks.com/butler/"&gt;Butler&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The one and only truly critical app in this list, Butler is an application launcher, search tool, universal bookmark manager, iTunes remote controller, Applescript launcher, keyboard macro tool, pasteboard manager and more. It puts the contents of your Mac at your fingertips and I couldn&amp;#8217;t imagine using a computer without it. Indispensable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://agilebits.com/onepassword/mac"&gt;1Password&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Universal password manager, syncs with iPad and iPhone version. Use it to store the hundreds of passwords you accumulate both personally and on the job. A plugin for Safari, Chrome, and Firefox handles automatic entry of password credentials on websites you visit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://www.microsoft.com/mac"&gt;MS Office&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;re in an office environment, chances are you&amp;#8217;ll need it at some point when working with more complex Word documents or spreadsheets. &lt;em&gt;Never&lt;/em&gt; use Powerpoint. See below.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://www.apple.com/iwork/"&gt;iWork&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, I recommend both office suites, as Pages is vastly superior to Word as a composition tool and Keynote simply demolishes Powerpoint for presentations. Faster, lighter, easier to use — the whole iWork suite is worth it just for Keynote. Companion apps for iPad and iPhone are cheap and automatically sync your documents.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://growl.info/"&gt;Growl&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perhaps soon to be obsoleted by the new &lt;a href="http://www.apple.com/osx/whats-new/features.html#notification"&gt;Notification Center&lt;/a&gt; feature of &lt;a href="http://www.apple.com/osx/"&gt;OS X Mountain Lion&lt;/a&gt;, Growl provides a heads-up display for various things happening on the Mac. I use it mostly for email notifications, but the feedback it provides when attaching an external device or connecting to a network is often helpful.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://www.barebones.com/products/bbedit/"&gt;BBEdit&lt;/a&gt;/&lt;a href="http://www.barebones.com/products/textwrangler/"&gt;TextWrangler&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A good text editor goes a very long way, as any coder can tell you. Some people &lt;a href="http://www.43folders.com/2005/12/12/text-setup"&gt;organize their whole lives&lt;/a&gt; with text files, though. Emacs is also available, but it doesn&amp;#8217;t have the Mac sensibilities of this storied old tool. If you&amp;#8217;d rather not spend the money, TextWrangler is the free, stripped-down counterpart.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://evernote.com/evernote/"&gt;Evernote&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Solves the problem of how to catalog, archive, and make available the vast library of information you accumulate over the years. Companion apps for iPad and iPhone put all your information at your fingertips. Can also be used as a collaborative tool for teams.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;Software for the Oracle Developer&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;
  &lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.oracle.com/technetwork/developer-tools/sql-developer/overview/index.html"&gt;SQL Developer&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Oracle&amp;#8217;s free tool for SQL and PL/SQL development.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://netbeans.org/"&gt;NetBeans&lt;/a&gt; and/or &lt;a href="http://www.oracle.com/technetwork/developer-tools/jdev/overview/index.html"&gt;JDeveloper&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;For J2EE development. Netbeans is the better tool, IMO, unless you need to work with &lt;a href="http://www.oracle.com/technetwork/developer-tools/adf/overview/index.html"&gt;Oracle ADF&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;I&amp;#8217;ve used VMWare Fusion for years and recently tried VirtualBox to take advantage of &lt;a href="http://www.oracle.com/technetwork/community/developer-vm/index.html"&gt;Oracle&amp;#8217;s developer VMs&lt;/a&gt;, and have been impressed with the performance and configurability. Beat&amp;#8217;s VMWare&amp;#8217;s price, as well.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html"&gt;Oracle 10g Instant Client&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Can&amp;#8217;t beat the convenience of being able to connect to a database without having to boot a VM.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://cord.sourceforge.net/"&gt;CoRD&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;For remote desktop (RDP) connections. Traditionally, it&amp;#8217;s been a better, more reliable option than &lt;a href="http://www.microsoft.com/mac/remote-desktop-client"&gt;Microsoft&amp;#8217;s Remote Desktop app&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.citrix.com/English/ss/downloads/details.asp?downloadId=2323407&amp;amp;productId=1689163"&gt;Citrix Receiver&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Needed If your company publishes corporate apps with Citrix Webapps, and many do.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://visualvm.java.net/"&gt;VisualVM&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Performance analysis/debugging tool for Java apps.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://directory.apache.org/studio/"&gt;Apache Directory Studio&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;A useful and fully-featured LDAP tool.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.wireshark.org/"&gt;Wireshark&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Packet sniffer.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.syntevo.com/smartcvs/index.html"&gt;SmartCVS&lt;/a&gt;/&lt;a href="http://www.syntevo.com/smartsvn/index.html"&gt;SmartSVN&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;My tools of choice for easy source code manaagement.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.macports.org/"&gt;MacPorts&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Download and compile Unix software.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.amazon.com/gp/feature.html/ref=kcp_mac_mkt_lnd?docId=1000464931"&gt;Kindle&lt;/a&gt; /&lt;a href="http://www.amazon.com/gp/feature.html/?docId=1000778781"&gt;Send to Kindle&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Take advantage of Kindle&amp;#8217;s massive collection of technical ebooks. The &amp;#8216;Send to Kindle&amp;#8217; tool allows you to upload your own PDFs for reading in the Kindle app.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.bruji.com/bwana/"&gt;Bwana&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Read &lt;code&gt;man&lt;/code&gt; pages in a browser window.&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2&gt;Productivity Apps&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;
  &lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.omnigroup.com/products/omnifocus/"&gt;OmniFocus&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;&lt;a href="http://www.davidco.com/about-gtd"&gt;GTD&lt;/a&gt; task/life management tool. Companion apps for iPhone, iPad keep everything in sync and support location-based notes.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://markedapp.com/"&gt;Marked&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;&lt;a href="http://daringfireball.net/projects/markdown/"&gt;Markdown&lt;/a&gt; preview tool, used for blogging and documentation.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://flyingmeat.com/acorn/"&gt;Acorn&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Also might have chosen &lt;a href="http://www.pixelmator.com/"&gt;Pixelmator&lt;/a&gt;, but this is the one I own. Almost everybody could use a good image editor at some time or other. Pixelmator is supposed to be closer to Photoshop.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://skitch.com/"&gt;Skitch&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Screenshot markup tool. Integrates with &lt;a href="http://www.evernote.com/evernote/"&gt;Evernote&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2&gt;For Sharing and Collaboration&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;
  &lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://www.dropbox.com/downloading?os=mac"&gt;Dropbox&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Disk space in the cloud with sharing and privacy features. Integrates with many applications on Mac, iPhone, iPad.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://spideroak.com/"&gt;SpiderOak&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;More secure alternative to Dropbox for sensitive files.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.wuala.com/"&gt;Wuala&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Another secure cloud-based storage option.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.wellredapps.com/products/dropkey/"&gt;DropKey&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;A brain dead easy way to send secure, encrypted email attachments!&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.skype.com/intl/en-us/get-skype/on-your-computer/macosx/"&gt;Skype&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Video phone for talking to your PC-using buddies. For your Mac brothers you should use &lt;a href="http://www.apple.com/mac/facetime/"&gt;Facetime&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.teamviewer.com/en/download/index.aspx"&gt;TeamViewer&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Free screen sharing software, if you don&amp;#8217;t have access to &lt;a href="http://www.gotomeeting.com/"&gt;GoToMeeting&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2&gt;System Tools&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;
  &lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.ragingmenace.com/software/menumeters/"&gt;MenuMeters&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Lightweight system monitor that sits in the menu bar. Measures CPU, memory, network, and disk usage.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html"&gt;SuperDuper!&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Creates bootable, up-to-date copies of your entire hard drive! Great, possibly indispensable, for recovering from a hard disk failure. Can also be used just to sync disk contents. Only copies changed data during file sync. Very efficient.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.coriolis-systems.com/iDefrag.php/"&gt;iDefrag&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Defrag utility. You shouldn&amp;#8217;t need to do it ever in OS X, but I&amp;#8217;m hoping it prolongs the life of my hard drive. Anybody got any real statistics on that?&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.coriolis-systems.com/iPartition.php"&gt;iPartition&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Disk Utility does pretty much everything this app does, except for allowing you to partition a disk without data loss. Unfortunately, this will not work if the disk is too full or badly fragmented. That&amp;#8217;s when iDefrag really comes in handy.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://rixstep.com/4/0/clix/"&gt;CLIX&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;GUI tool for executing Unix commands. Comes with a large library of useful commands, including many Mac-specific ones. Can be used as a learning tool and you can add your own scripts.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.omnigroup.com/products/omnidisksweeper/"&gt;OmniDiskSweeper&lt;/a&gt; and/or &lt;a href="http://grandperspectiv.sourceforge.net/"&gt;GrandPerspective&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Analyze the contents and sizes of the stuff on your hard drive. GrandPerspective provides a better visualization, but you don&amp;#8217;t always need the &amp;#8220;fluff&amp;#8221;.&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2&gt;Internet Tools&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;
  &lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Interarchy&lt;/td&gt;
  &lt;td&gt;FTP/SFTP tool with numerous useful features. Download an entire website or keep remote sites in sync with local folders. &lt;a href="http://panic.com/transmit/"&gt;Transmit&lt;/a&gt; is also popular.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.google.com/mac/"&gt;Chrome&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;The web browser. (I use Safari.)&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.mozilla.org/en-US/firefox/new/"&gt;Firefox&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;The web browser. (See above.)&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://nightly.webkit.org/"&gt;Webkit&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;See what&amp;#8217;s coming down the pipe in Safari.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.transmissionbt.com/"&gt;Transmission&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;BitTorrent client.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.videolan.org/vlc/download-macosx.html"&gt;VLC&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Open source media player. Plays a wide variety of formats.&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2&gt;If You Have The Money&amp;#8230;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;
  &lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.smilesoftware.com/PDFpenPro/"&gt;PDFPenPro&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Create, edit, sign, mark up, footnote, redact, and otherwise manipulate PDFs. Also does OCR and tables of contents. Very nice tool. Companion app for iPad.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.omnigroup.com/products/omnigraffle/"&gt;OmniGraffle&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Like Visio, only a thousand times better. The Omni Group&amp;#8217;s flagship product. Wish very much I owned this one.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.omnigroup.com/products/omniplan/"&gt;OmniPlan&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Does to MS Project what Keynote does to Powerpoint. Bought my copy a few versions back when it was far less expensive, and it still has all the project tracking features I need.&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2&gt;Obscure Use Cases&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;
  &lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://cutedgesystems.com/software/MailServeForLion/"&gt;MailServe for Lion&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Automates configuration of OS X&amp;#8217;s built in Postfix mail server. I&amp;#8217;ve had to use this in the past when I was unable to connect to the corporate email server and didn&amp;#8217;t want to send files through Gmail. (Did I mention I despise most web mail clients?)&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.joshjacob.com/mac-development/tnef.php"&gt;TNEF&amp;#8217;s Enough&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Used it once to extract the Word document embedded in a &lt;code&gt;winmail.dat&lt;/code&gt; attachment from MS Outlook.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.arrowbit.com/info/pstbridgepro"&gt;PST Bridge&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Used convert or read PST files (basically mail archives) generated by MS Outlook.&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2&gt;Just for Fun&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;
  &lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.blacktree.com/"&gt;Nocturne&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Invert your screen colors to reduce strain on the eyes at night, or to conserve battery power. Neat effect.&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.bannister.org/software/ao.htm"&gt;Audio Overload&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;Pass the time by listening to music from the console video games of long ago. Video game music files found separately, somewhere out there on the &lt;a href="http://www.google.com/"&gt;internet&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;That&amp;#8217;s it for now. Hope you find something useful.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/26025230295</link><guid>http://tjmoracle.tumblr.com/post/26025230295</guid><pubDate>Wed, 27 Jun 2012 17:39:00 -0400</pubDate><category>enkitec</category><category>os x</category><category>oracle</category></item><item><title>DML Error Logging</title><description>&lt;p&gt;Introduced with Oracle 10gR2, DML Error Logging allows you to make standard SQL &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;MERGE&lt;/code&gt; statements behave more like the SQL*Loader utility, logging errors to a table rather than failing and rolling back the first time an error condition is encountered.&lt;/p&gt;

&lt;p&gt;&lt;!-- more --&gt;&lt;/p&gt;

&lt;p&gt;The syntax is the same for all DML statements. Just add the following clause to the end of your DML:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;LOG ERRORS 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The database will automatically create an error log table for you. Per the documentation (&lt;a href="http://docs.oracle.com/cd/B28359%5F01/server.111/b28286/statements%5F9014.htm"&gt;11g&lt;/a&gt;, &lt;a href="http://docs.oracle.com/cd/E11882_01/server.112/e25494/tables004.htm#InsertDMLErrorLogging"&gt;11gR2&lt;/a&gt;):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;… the database assigns the default name generated by the DBMS_ERRLOG package. The default error log table name is ERR$_ followed by the first 25 characters of the name of the table upon which the DML operation is being executed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In most cases you&amp;#8217;ll probably want to specify your own log table, like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;LOG ERRORS INTO error_table
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you do, you&amp;#8217;ll need to create the table beforehand with &lt;code&gt;DBMS_ERRLOG&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;EXEC DBMS_ERRLOG.CREATE_ERROR_LOG('table', 'error_table');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Like SQL*Loader, you can specify a maximum number of errors to tolerate before failing and rolling back the transaction. You can also just say &lt;code&gt;UNLIMITED&lt;/code&gt; if you don&amp;#8217;t ever want to fail the transaction.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;LOG ERRORS INTO error_table REJECT LIMIT 99
LOG ERRORS INTO error_table REJECT LIMIT UNLIMITED
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Since the default limit is zero, it makes sense to always include this clause.&lt;/p&gt;

&lt;p&gt;The last option is to specify a string that identifies that particular DML operation, so that you can group sets of errors in the log table. Here is an example that incorporates &lt;code&gt;SYSDATE&lt;/code&gt; to tag all errors belonging to this DML operation.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;LOG ERRORS INTO error_table ('Import from external datasource '||TO_CHAR(SYSDATE)) REJECT LIMIT UNLIMITED
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Putting it all together, here is a short example demonstrating the use of DML Error Logging for merging data between two similar, but non-identically structured tables. An alternate strategy might have involved creating a custom interface table with a block of PL/SQL to perform inserts and mark the failed rows. Here, we take advantage of the native capabilities of the database to simplify and accelerate the process.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;create table users_loc (id number primary key, name varchar2(75));

create table users_ext (uuid varchar2(10), fname varchar2(15), lname varchar2(20));
insert into users_ext values('1000', 'Scotty',  'P');
insert into users_ext values('2000', 'Melinda', 'Manning');
insert into users_ext values('200J', 'Jamie',   'Brooks');
insert into users_ext values('1000', 'Jason',   'Jennings');
insert into users_ext values('4000', 'Marcus',  'Nevada');
insert into users_ext values('AAAA', 'Alfred',  'Butler');
insert into users_ext values('ABAB', 'Simon',   'Jones');
insert into users_ext values('6000', 'Joon',    'Patel');
insert into users_ext values('7000', 'Wanda',   'Lutz');
insert into users_ext values('8000', 'Joseph',  'McBride');

exec dbms_errlog.create_error_log('users_loc','users_errs');

insert into users_loc (id, name) 
select uuid, 
       fname||' '||lname 
  from users_ext 
log errors into users_errs ('import from users_ext: '||to_char(sysdate)) 
reject limit unlimited;

col ora_err_mesg$ format a40
col ora_err_rowid$ format a15
col ora_err_tag$ format a50
col id format a10
col name format a30
select * from users_errs;

ORA_ERR_NUMBER$ ORA_ERR_MESG$                            ORA_ERR_ROWID$  ORA_ERR_OPTYP$ ORA_ERR_TAG$                                       ID         NAME                         
--------------- ---------------------------------------- --------------- -------------- -------------------------------------------------- ---------- ------------------------------
           1722 ORA-01722: invalid number                                I              import from users_ext: 07-JUN-2012 12:00:09        200J       Jamie Brooks                   


              1 ORA-00001: unique constraint (NUCDBA.SYS                 I              import from users_ext: 07-JUN-2012 12:00:09        1000       Jason Jennings                 
                _C00128181) violated                                                                                                                                                 


           1722 ORA-01722: invalid number                                I              import from users_ext: 07-JUN-2012 12:00:09        AAAA       Alfred Butler                  


           1722 ORA-01722: invalid number                                I              import from users_ext: 07-JUN-2012 12:00:09        ABAB       Simon Jones                    
&lt;/code&gt;&lt;/pre&gt;</description><link>http://tjmoracle.tumblr.com/post/24616799679</link><guid>http://tjmoracle.tumblr.com/post/24616799679</guid><pubDate>Thu, 07 Jun 2012 13:04:00 -0400</pubDate><category>oracle</category><category>sql</category></item><item><title>The Database Is More Than Just Storage</title><description>&lt;a href="http://shallahamer-orapub.blogspot.com/2012/05/row-versus-set-processing-surprise.html"&gt;The Database Is More Than Just Storage&lt;/a&gt;: &lt;p&gt;To folks writing database-driven reports for the web: Craig Shallahamer shows why you should let the (Oracle) database handle as much of your selection and transformation logic as possible.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/24210308143</link><guid>http://tjmoracle.tumblr.com/post/24210308143</guid><pubDate>Fri, 01 Jun 2012 16:00:00 -0400</pubDate></item><item><title>SQLFiddle.com: An Online Multi-Vendor SQL Playground</title><description>&lt;a href="http://sqlfiddle.com/"&gt;SQLFiddle.com: An Online Multi-Vendor SQL Playground&lt;/a&gt;: &lt;p&gt;Build your own mini-schema and run queries against it. Publish your experiments for your friends to tweak. Or try out the examples from your favorite SQL book without having to build a database first. Works for Oracle, PostgreSQL, MySQL, and SQL Server flavors. Pretty cool!&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/23745202687</link><guid>http://tjmoracle.tumblr.com/post/23745202687</guid><pubDate>Fri, 25 May 2012 15:04:00 -0400</pubDate></item><item><title>Oracle's App Server Offerings: A High-Level Comparison</title><description>&lt;p&gt;Ran across this &lt;a href="http://www.yenlo.nl/oracle-application-server-vs-weblogic-vs-glassfish/"&gt;post&lt;/a&gt; from the Dutch software and services company &lt;a href="http://www.yenlo.nl/"&gt;Yenlo&lt;/a&gt; that compares and contrasts Oracle&amp;#8217;s major application server offerings. It would serve as a good starting point for deciding whether you truly need to pay the enormous licensing costs associated with a professional-grade application server platform, or if you were just wondering how to reconcile the continued existence of Oracle Application Server with WebLogic.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/23311020017</link><guid>http://tjmoracle.tumblr.com/post/23311020017</guid><pubDate>Fri, 18 May 2012 18:04:00 -0400</pubDate><category>oracle</category><category>oracle application server</category><category>WebLogic</category><category>Glassfish</category><category>J2EE</category></item><item><title>ODBC Connection Fails for MS Access App on Citrix </title><description>&lt;p&gt;I ran into an interesting problem recently when attempting to publish an MS Access application with &lt;a href="http://www.citrix.com/English/ps2/products/product.asp?contentID=186&amp;amp;ntref=footer"&gt;Citrix XenApp&lt;/a&gt;. The application pulled data from an Oracle database, so an ODBC connection was created on the Citrix server. What we found was that when a non-privileged user tried to run the application, the connection failed. When the Citrix server admin ran it, however, it worked. Furthermore, as long as the admin was logged in, anybody else could run the application successfully; when he logged out, the users&amp;#8217; connections broke again.&lt;/p&gt;

&lt;p&gt;&lt;!-- more --&gt;&lt;/p&gt;

&lt;p&gt;Since Access is not my area of expertise, we finally contacted Microsoft tech support after trying a few suggestions we dug up in our own research. The support analyst clued us in to the following solution, which I pulled from an Oracle Primavera &lt;a href="http://docs.oracle.com/cd/E16688_01/Technical_Documentation/Terminal_Services_and_Citrix/Terminal%20Services%20and%20Citrix.pdf"&gt;setup document&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From what I&amp;#8217;ve seen on different support forums, this seems to be a pretty standard setup task when running Oracle on Windows over Citrix.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If using an Oracle database platform, confirm [that] the Create Global Objects Windows Security Policy is set up on the Terminal Servers and add the domain Users or Groups of the end-users who will be accessing the published application. If the local security policy is not setup, the following steps can be used to add the policy:&lt;/p&gt;
  
  &lt;ol&gt;&lt;li&gt;Click Start, Programs, Administrative Tools, and then click Local Security Policy.&lt;/li&gt;
  &lt;li&gt;Expand Local Policies, and then click User Rights Assignments.&lt;/li&gt;
  &lt;li&gt;In the right pane, double-click Create Global Objects.&lt;/li&gt;
  &lt;li&gt;In the Local Security Policy Setting dialogbox, click Add.&lt;/li&gt;
  &lt;li&gt;In the Select Users or Group dialog box, select the user group that these end users are a part of, click Add, and then click OK.&lt;/li&gt;
  &lt;li&gt;Click OK.&lt;/li&gt;
  &lt;/ol&gt;&lt;p&gt;If using an Oracle database platform, the Oracle client software also requires that you give the Authenticated User Group, Read and Execute privilege to the Oracle Home Directory and ALL its sub directories and files on the Terminal Servers.&lt;/p&gt;
&lt;/blockquote&gt;</description><link>http://tjmoracle.tumblr.com/post/22857143505</link><guid>http://tjmoracle.tumblr.com/post/22857143505</guid><pubDate>Fri, 11 May 2012 16:54:00 -0400</pubDate></item><item><title>Discovering Command-line Java Profiling Tools</title><description>&lt;p&gt;At my current client I am responsible for administering a third-party Java appplication that unfortunately generates a lot of performance complaints. It&amp;#8217;s a multi-tiered system with the middle-tier running on Oracle Appication Server 10.1.3. I am already familiar with GUI profiling tools like &lt;em&gt;jconsole&lt;/em&gt; and &lt;em&gt;jvisualvm&lt;/em&gt;, both of which are included with Oracle&amp;#8217;s JDK, and I have played around with the profiling capabilities in NetBeans. All of these profiling tools are fairly similar to one another in look and functionality.&lt;/p&gt;

&lt;p&gt;&lt;!-- more --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/tobyjmarks/7136941369/in/photostream/lightbox/"&gt;&lt;img src="http://media.tumblr.com/tumblr_m3f00lImJ51r8k26w.jpg" alt="Profiling the JVM with jconsole"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/tobyjmarks/6990855922/in/photostream/lightbox/"&gt;&lt;img src="http://media.tumblr.com/tumblr_m3f01hH3qH1r8k26w.jpg" alt="Profiling the JVM with jvisualvm"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/tobyjmarks/6990855902/in/photostream/lightbox/"&gt;&lt;img src="http://media.tumblr.com/tumblr_m3f02219Wr1r8k26w.jpg" alt="Profiling the JVM with NetBeans"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using any one of these tools, I can view the size and utilization of the heap, see the status of running threads, monitor CPU loads, pull garbage collection statistics, and generate a memory dump. All of these are adequate for monitoring application performance in real-time, but while working with the application vendor to diagnose problems I found it necessary to be able to report on application performance over longer spans of time, say days or even weeks. We were trying to show patterns of eroding performance, or possibly spot instances of very slow memory leaks in parts of the application that got used less frequently. In any event, it required a way to monitor all of these details without having to have my eyes on the screen the whole time.&lt;/p&gt;

&lt;p&gt;My goal was to get all that data exposed by the aforementioned GUI tools into a database that I could report off of. A little research on the &lt;a href="http://www.ibm.com/developerworks/java/library/j-5things8/index.html"&gt;internet&lt;/a&gt; (HT to Ted Neward) and some &lt;a href="http://docs.oracle.com/javase/6/docs/technotes/tools/share/jps.html"&gt;digging&lt;/a&gt; &lt;a href="http://docs.oracle.com/javase/6/docs/technotes/tools/share/jmap.html"&gt;around&lt;/a&gt; &lt;a href="http://docs.oracle.com/javase/6/docs/technotes/tools/share/jhat.html"&gt;in&lt;/a&gt; &lt;a href="http://docs.oracle.com/javase/6/docs/technotes/tools/share/jstack.html"&gt;man&lt;/a&gt; &lt;a href="http://docs.oracle.com/javase/6/docs/technotes/tools/share/jstat.html"&gt;pages&lt;/a&gt; provided the solution.&lt;/p&gt;

&lt;p&gt;Recent versions of the JDK come packaged with some nifty command line tools to assist with profiling your Java applications. They can all be found in the &lt;code&gt;/bin&lt;/code&gt; directory under the JDK base directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/tobyjmarks/7136941341/in/photostream/lightbox/"&gt;&lt;img src="http://media.tumblr.com/tumblr_m3f02i1QLU1r8k26w.jpg" alt="Commands bundled with the JDK"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;jps&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;jps&lt;/em&gt; command is a lot like the &lt;em&gt;ps&lt;/em&gt; command in Unix/Linux, in that it lists running processes. &lt;em&gt;Jps&lt;/em&gt;, however, will only list discoverable Java processes. If a running Java process is not included in the list returned by &lt;em&gt;jps&lt;/em&gt;, you may still be able to profile it; it just doesn&amp;#8217;t advertise itself as attachable.&lt;/p&gt;

&lt;p&gt;Each Java process is identified by the &amp;#8220;VMID&amp;#8221; returned by &lt;em&gt;jps&lt;/em&gt;, usually corresponding to the process ID on the host system. It is this VMID which is required as an argument to all of the other tools we will discuss, so be sure to always use &lt;em&gt;jps&lt;/em&gt; to correctly identify your processes to the other tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/tobyjmarks/7136941355/in/photostream/lightbox/"&gt;&lt;img src="http://media.tumblr.com/tumblr_m3f02xOQ5J1r8k26w.jpg" alt="Running jps"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;jstack&lt;/h2&gt;

&lt;p&gt;Use &lt;em&gt;jstack&lt;/em&gt; to generate a thread dump for a running Java process. It gives you a nice snapshot of thread activity comparable to what you might get in one of the GUI tools mentioned above. You get the name of the thread, the current state, a stack trace, and with the &lt;code&gt;-l&lt;/code&gt; option you get additional information about locking conditions. If an actual deadlock is detected you get a detailed breakdown of which threads are holding the locks! For comprehending the overall state of threads in an application it can actually prove superior to some of its GUI counterparts, as it&amp;#8217;s faster to scan the results; i.e. no need to click from thread to thread or drill down to view the details.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Jstack&lt;/em&gt; can be used in conjunction with &lt;em&gt;jmap&lt;/em&gt; and &lt;em&gt;jhat&lt;/em&gt; to get a more comprehensive picture of application state at a critical point in time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you have a 64-bit JDK, you&amp;#8217;ll want to use the &lt;code&gt;-J-d64&lt;/code&gt; flag when running the command.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/tobyjmarks/7136941351/in/photostream/lightbox/"&gt;&lt;img src="http://media.tumblr.com/tumblr_m3f03aolfr1r8k26w.jpg" alt="Running jstack"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;jmap &amp;amp; jhat&lt;/h2&gt;

&lt;p&gt;These two tools work together to help you analyze the contents of the heap. Use &lt;em&gt;jmap&lt;/em&gt; to generate a heap dump, and &lt;em&gt;jhat&lt;/em&gt; to help analyze the dump file.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/tobyjmarks/7136941291/in/photostream/lightbox/"&gt;&lt;img src="http://media.tumblr.com/tumblr_m3f03l7qLd1r8k26w.jpg" alt="Running jmap and jhat"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Jhat&lt;/em&gt; works by spawning an HTTP server that presents the contents of the heap as a web page, allowing you to drill down on a specific object to view its members and references. In order to get the most out of &lt;em&gt;jhat&lt;/em&gt;, however, it would be worth investigating OQL, the &lt;strong&gt;O&lt;/strong&gt;bject &lt;strong&gt;Q&lt;/strong&gt;uery &lt;strong&gt;L&lt;/strong&gt;anguage, as the web page generated by &lt;em&gt;jhat&lt;/em&gt; allows you to run OQL queries against the heap. If you know a little about your application class structure, this makes your heap analysis far more efficient. Thankfully, the &lt;em&gt;jhat&lt;/em&gt; web page provides you with a short primer.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/tobyjmarks/6990855834/in/photostream/lightbox/"&gt;&lt;img src="http://media.tumblr.com/tumblr_m3f03yOX2U1r8k26w.jpg" alt="OQL help page"/&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/tobyjmarks/7136941267/in/photostream/lightbox/"&gt;&lt;img src="http://media.tumblr.com/tumblr_m3f049p5Yr1r8k26w.jpg" alt="OQL primer"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Jmap&lt;/em&gt; is good for more than just generating heap dumps, however. When used with the &lt;code&gt;-heap&lt;/code&gt; option it gives you a nicely formatted heap usage summary.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/tobyjmarks/7136941255/in/photostream/lightbox/"&gt;&lt;img src="http://media.tumblr.com/tumblr_m3f04kBjmj1r8k26w.jpg" alt="jmap with the -heap option"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I was getting closer to my goal. In fact, this was originally the output I parsed during my first attempt at statistics gathering. Unfortunately, what I found while running on an RHEL 4.7 box with JDK 1.6.0_30 was that &lt;em&gt;jmap&lt;/em&gt; would from time to time inexplicably refuse to attach to my Java process, leaving gaping holes in my report. I noticed &lt;a href="http://ubuntuforums.org/showthread.php?t=1886859"&gt;similar&lt;/a&gt; &lt;a href="https://forums.oracle.com/forums/thread.jspa?threadID=1693766"&gt;complaints&lt;/a&gt; &lt;a href="http://blog.thecodingmachine.com/fr/comment/reply/91"&gt;online&lt;/a&gt;, so I figured there may be problems with it.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# jmap -J-d64 -heap 11028
Attaching to process ID 11028, please wait...
Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Once again, be sure to use the &lt;code&gt;-J-d64&lt;/code&gt; flag with &lt;em&gt;jmap&lt;/em&gt; if you&amp;#8217;re running a 64-bit JVM.&lt;/p&gt;

&lt;h2&gt;jstat&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Jstat&lt;/em&gt; was what saved the day for me. &lt;em&gt;Jstat&lt;/em&gt; displays detailed heap usage, class loader, and garbage collection statistics for the JVM; basically everything I wanted to report on. &lt;em&gt;Jstat&lt;/em&gt; has numerous options that allow you to focus in on usage statistics for different parts of the JVM.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;-class&lt;/strong&gt;&lt;br/&gt;
Statistics on the behavior of the class loader.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-compiler&lt;/strong&gt;&lt;br/&gt;
Statistics of the behavior of the HotSpot Just-in-Time compiler.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-gc&lt;/strong&gt;&lt;br/&gt;
Statistics of the behavior of the garbage collected heap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-gccapacity&lt;/strong&gt;&lt;br/&gt;
Statistics of the capacities of the generations and their corresponding spaces.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-gccause&lt;/strong&gt;&lt;br/&gt;
Summary of garbage collection statistics (same as -gcutil), with the cause of the last and current (if applicable) garbage collection events.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-gcnew&lt;/strong&gt;&lt;br/&gt;
Statistics of the behavior of the new generation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-gcnewcapacity&lt;/strong&gt;&lt;br/&gt;
Statistics of the sizes of the new generations and its corresponding spaces.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-gcold&lt;/strong&gt;&lt;br/&gt;
Statistics of the behavior of the old and permanent generations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-gcoldcapacity&lt;/strong&gt;&lt;br/&gt;
Statistics of the sizes of the old generation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-gcpermcapacity&lt;/strong&gt;&lt;br/&gt;
Statistics of the sizes of the permanent generation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-gcutil&lt;/strong&gt;&lt;br/&gt;
Summary of garbage collection statistics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-printcompilation&lt;/strong&gt;&lt;br/&gt;
HotSpot compilation method statistics.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Like the heap profiling features of the GUI tools mentioned earlier, the results of &lt;em&gt;jstat&lt;/em&gt; can be used to check for memory leaks or to better tune your JVM startup parameters. Like the &lt;em&gt;vmstat&lt;/em&gt; command, &lt;em&gt;jstat&lt;/em&gt; can be set to repeat at a specified interval, for an infinite or specific number of iterations. Since &lt;em&gt;jstat&lt;/em&gt; has so many options and output formats, I will examine it in more detail in a later blog post when I discuss how I used it to populate my application instrumentation table.&lt;/p&gt;

&lt;p&gt;One thing that should be noted is that all of these tools are documented as being &amp;#8220;unsupported&amp;#8221; and experimental. Oracle make no guarantees that they will be included in future versions of the JDK or that their output or arguments will not change. Still, though, they&amp;#8217;ve been around for a while and since they&amp;#8217;re there, why not make use of them? Just don&amp;#8217;t build any critical production processes off of them if you want total flexibility in taking future JDK upgrades.&lt;/p&gt;

&lt;p&gt;In my next post I&amp;#8217;ll examine how I used these tools to automate the process of instrumenting my third-party Java app. I&amp;#8217;ll also provide you with a script I wrote that formats the output of &lt;em&gt;jstat&lt;/em&gt; to look something like the &lt;code&gt;-heap&lt;/code&gt; option of &lt;em&gt;jmap&lt;/em&gt;, providing an equivalent, but more reliable, solution.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/22276471924</link><guid>http://tjmoracle.tumblr.com/post/22276471924</guid><pubDate>Wed, 02 May 2012 17:41:00 -0400</pubDate><category>java</category><category>jstat</category><category>jmap</category><category>jhat</category><category>jstack</category><category>jvm</category><category>profiling</category><category>instrumentation</category></item><item><title>Table Functions: 9i Feature Worth Revisiting (Part 2)</title><description>&lt;p&gt;In &lt;a href="http://tjmoracle.tumblr.com/post/21936214942/table-functions-9i-feature-worth-revisiting-part-1"&gt;part I of this series&lt;/a&gt; I showed a simple example of how table functions can be used to treat PL/SQL collection types as tables in a SQL query. We used a nested table collection type to which we manually added a few entries. In this example, I intend to show a more practical use of table functions using all three PL/SQL collection types: associative arrays, nested tables, and VARRAYs.&lt;/p&gt;

&lt;p&gt;&lt;!-- more --&gt;&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s revisit a use case I alluded to in my earlier blog entry: incorporating LDAP data into a query. Suppose your task is to code a custom authorization scheme for an enterprise application reporting system. You want row access to be determined by a user&amp;#8217;s group memberships in the company&amp;#8217;s Active Directory server. If a user is assigned to &amp;#8220;the Company A group&amp;#8221; in Active Directory, he gets to see Company A&amp;#8217;s data. You can configure that kind of restriction at a database level using &lt;a href="http://www.oracle.com/technetwork/database/security/index-088277.html"&gt;Oracle&amp;#8217;s Virtual Private Database&lt;/a&gt; feature. Configuring VPD is beyond the scope of this blog entry, but ultimately you will be associating a table to a portion of a SQL WHERE clause that gets appended to every query written against that table. You can find more documentation on VPD &lt;a href="http://download.oracle.com/docs/cd/B28359_01/network.111/b28531/vpd.htm"&gt;on the OTN site&lt;/a&gt;. In this case, let&amp;#8217;s assume you know that another team has written a PL/SQL database package to query data from Active Directory, and you want to make use of that code. The only problem is that all the functions in that package return collections of various types, and you need to be able to access that information from the WHERE clause you are building with VPD. This is where table functions come in handy.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s begin by setting up our example schema. We&amp;#8217;ll be looking at client sales data. First, let&amp;#8217;s set up our clients.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CREATE TABLE clients (
  client_id NUMBER,
  client_name VARCHAR2(50));

INSERT INTO clients VALUES (100, 'Timco Industries');
INSERT INTO clients VALUES (200, 'Moneybros');
INSERT INTO clients VALUES (300, 'The Monacle Corporation');
INSERT INTO clients VALUES (400, 'Schmapple');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We also need application users who will be logging in to view the report data. We&amp;#8217;ll assume each application user is uniquely linked to a user in Active Directory via the external_id column.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CREATE TABLE users (
  sequence_number NUMBER,
  user_name VARCHAR2(20),
  external_id VARCHAR2(20),
  first_name VARCHAR2(30),
  last_name VARCHAR2(30));

INSERT INTO users VALUES (1, 'JACKA', 'jaugusti', 'Jack', 'Augustine');
INSERT INTO users VALUES (2, 'KENKI', 'kkidd', 'Ken', 'Kidd');
INSERT INTO users VALUES (3, 'BRIAH', 'bhill', 'Brian', 'Hill');
INSERT INTO users VALUES (4, 'KEOVO', 'kvongkas', 'Keo', 'Vongkaseum');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, we need a data table on which to report.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CREATE TABLE quarterly_sales_data (
  sequence_number NUMBER,
  client_id NUMBER,
  sales_year NUMBER,
  sales_quarter NUMBER,
  sales_amount NUMBER);

INSERT INTO quarterly_sales_data VALUES (1, 100, 2011, 1, 20000);
INSERT INTO quarterly_sales_data VALUES (2, 100, 2011, 2, 17500);
INSERT INTO quarterly_sales_data VALUES (3, 100, 2011, 3, 33000);
INSERT INTO quarterly_sales_data VALUES (4, 200, 2011, 1, 4500);
INSERT INTO quarterly_sales_data VALUES (5, 200, 2011, 2, 7000);
INSERT INTO quarterly_sales_data VALUES (6, 200, 2011, 3, 6300);
INSERT INTO quarterly_sales_data VALUES (7, 300, 2011, 1, 11100);
INSERT INTO quarterly_sales_data VALUES (8, 300, 2011, 2, 12800);
INSERT INTO quarterly_sales_data VALUES (9, 300, 2011, 3, 14000);
INSERT INTO quarterly_sales_data VALUES (10, 400, 2011, 1, 55600);
INSERT INTO quarterly_sales_data VALUES (11, 400, 2011, 2, 80000);
INSERT INTO quarterly_sales_data VALUES (12, 400, 2011, 3, 78500);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Our basic report query will look something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SELECT client_name, sales_year, sales_quarter, sales_amount
  FROM quarterly_sales_data NATURAL JOIN clients;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Our Active Directory groups will be TIMCO_GRP, MONEYBROS_GRP, MONACLE_GRP, and SCHMAPPLE_GRP. If a user belongs to one or more of these groups, he has permission to view the respective client&amp;#8217;s data.&lt;/p&gt;

&lt;p&gt;Now we need a function that returns data from Active Directory. Just to keep it simple we will create a package that hardcodes the group memberships and returns them as collections of various types. We will then look at how table functions can be used to transform each one of those collections into an aggregate result set. (NOTE: querying LDAP directories from PL/SQL is not too difficult, and might serve as the basis for a future blog entry.)&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s our function. We&amp;#8217;ve placed it in a package called LDAP_UTIL.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PACKAGE ldap_util AS

  TYPE ldap_entry_typ IS RECORD (
    cn VARCHAR2(30),
    description VARCHAR2(1000),
    distinguished_name VARCHAR2(200),
    name VARCHAR2(30),
    samaccountname VARCHAR2(30));

  TYPE ldap_groups_nt IS TABLE OF ldap_entry_typ;
  TYPE ldap_groups_aa IS TABLE OF ldap_entry_typ INDEX BY PLS_INTEGER;
  TYPE ldap_groups_va IS VARRAY(100) of ldap_entry_typ;

  FUNCTION get_user_groups_nt (p_user VARCHAR2) RETURN ldap_groups_nt;
  FUNCTION get_user_groups_aa (p_user VARCHAR2) RETURN ldap_groups_aa;
  FUNCTION get_user_groups_va (p_user VARCHAR2) RETURN ldap_groups_va;

END ldap_util;
/
SHOW ERRORS

CREATE OR REPLACE PACKAGE BODY ldap_util AS

  FUNCTION get_user_groups_nt (p_user VARCHAR2) RETURN ldap_groups_nt IS
    l_groups ldap_groups_nt := ldap_groups_nt();
  BEGIN
    CASE p_user
      WHEN 'jaugusti' THEN

        l_groups.extend;
        l_groups(1).cn := 'TIMCO_GRP';
        l_groups(1).description := 'Employees working on the Timco Industries account.';
        l_groups(1).distinguished_name := 'CN=TIMCO_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'TIMCO_GRP';
        l_groups(1).samaccountname := 'TIMCO_GRP';

      WHEN 'kkidd' THEN

        l_groups.extend;
        l_groups(1).cn := 'MONEYBROS_GRP';
        l_groups(1).description := 'Employees working on the Moneybros account.';
        l_groups(1).distinguished_name := 'CN=MONEYBROS_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'MONEYBROS_GRP';
        l_groups(1).samaccountname := 'MONEYBROS_GRP';

      WHEN 'bhill' THEN

        l_groups.extend;
        l_groups(1).cn := 'TIMCO_GRP';
        l_groups(1).description := 'Employees working on the Timco Industries account.';
        l_groups(1).distinguished_name := 'CN=TIMCO_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'TIMCO_GRP';
        l_groups(1).samaccountname := 'TIMCO_GRP';

        l_groups.extend;
        l_groups(2).cn := 'MONACLE_GRP';
        l_groups(2).description := 'Employees working on the Monacle Corporation account.';
        l_groups(2).distinguished_name := 'CN=MONACLE_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(2).name := 'MONACLE_GRP';
        l_groups(2).samaccountname := 'MONACLE_GRP';

      WHEN 'kvongkas' THEN

        l_groups.extend;
        l_groups(1).cn := 'MONEYBROS_GRP';
        l_groups(1).description := 'Employees working on the Moneybros account.';
        l_groups(1).distinguished_name := 'CN=MONEYBROS_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'MONEYBROS_GRP';
        l_groups(1).samaccountname := 'MONEYBROS_GRP';

        l_groups.extend;
        l_groups(2).cn := 'MONACLE_GRP';
        l_groups(2).description := 'Employees working on the Monacle Corporation account.';
        l_groups(2).distinguished_name := 'CN=MONACLE_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(2).name := 'MONACLE_GRP';
        l_groups(2).samaccountname := 'MONACLE_GRP';

        l_groups.extend;
        l_groups(3).cn := 'SCHMAPPLE_GRP';
        l_groups(3).description := 'Employees working on the Schmapple account.';
        l_groups(3).distinguished_name := 'CN=SCHMAPPLE_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(3).name := 'SCHMAPPLE_GRP';
        l_groups(3).samaccountname := 'SCHMAPPLE_GRP';

    END CASE;
    RETURN l_groups;
  END get_user_groups_nt;

  FUNCTION get_user_groups_aa (p_user VARCHAR2) RETURN ldap_groups_aa IS
    l_groups ldap_groups_aa;
  BEGIN
    CASE p_user
      WHEN 'jaugusti' THEN

        l_groups(1).cn := 'TIMCO_GRP';
        l_groups(1).description := 'Employees working on the Timco Industries account.';
        l_groups(1).distinguished_name := 'CN=TIMCO_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'TIMCO_GRP';
        l_groups(1).samaccountname := 'TIMCO_GRP';

      WHEN 'kkidd' THEN

        l_groups(1).cn := 'MONEYBROS_GRP';
        l_groups(1).description := 'Employees working on the Moneybros account.';
        l_groups(1).distinguished_name := 'CN=MONEYBROS_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'MONEYBROS_GRP';
        l_groups(1).samaccountname := 'MONEYBROS_GRP';

      WHEN 'bhill' THEN

        l_groups(1).cn := 'TIMCO_GRP';
        l_groups(1).description := 'Employees working on the Timco Industries account.';
        l_groups(1).distinguished_name := 'CN=TIMCO_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'TIMCO_GRP';
        l_groups(1).samaccountname := 'TIMCO_GRP';

        l_groups(2).cn := 'MONACLE_GRP';
        l_groups(2).description := 'Employees working on the Monacle Corporation account.';
        l_groups(2).distinguished_name := 'CN=MONACLE_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(2).name := 'MONACLE_GRP';
        l_groups(2).samaccountname := 'MONACLE_GRP';

      WHEN 'kvongkas' THEN

        l_groups(1).cn := 'MONEYBROS_GRP';
        l_groups(1).description := 'Employees working on the Moneybros account.';
        l_groups(1).distinguished_name := 'CN=MONEYBROS_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'MONEYBROS_GRP';
        l_groups(1).samaccountname := 'MONEYBROS_GRP';

        l_groups(2).cn := 'MONACLE_GRP';
        l_groups(2).description := 'Employees working on the Monacle Corporation account.';
        l_groups(2).distinguished_name := 'CN=MONACLE_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(2).name := 'MONACLE_GRP';
        l_groups(2).samaccountname := 'MONACLE_GRP';

        l_groups(3).cn := 'SCHMAPPLE_GRP';
        l_groups(3).description := 'Employees working on the Schmapple account.';
        l_groups(3).distinguished_name := 'CN=SCHMAPPLE_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(3).name := 'SCHMAPPLE_GRP';
        l_groups(3).samaccountname := 'SCHMAPPLE_GRP';

    END CASE;
    RETURN l_groups;
  END get_user_groups_aa;

  FUNCTION get_user_groups_va (p_user VARCHAR2) RETURN ldap_groups_va IS
    l_groups ldap_groups_va := ldap_groups_va();
  BEGIN
    CASE p_user
      WHEN 'jaugusti' THEN

        l_groups.extend;
        l_groups(1).cn := 'TIMCO_GRP';
        l_groups(1).description := 'Employees working on the Timco Industries account.';
        l_groups(1).distinguished_name := 'CN=TIMCO_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'TIMCO_GRP';
        l_groups(1).samaccountname := 'TIMCO_GRP';

      WHEN 'kkidd' THEN

        l_groups.extend;
        l_groups(1).cn := 'MONEYBROS_GRP';
        l_groups(1).description := 'Employees working on the Moneybros account.';
        l_groups(1).distinguished_name := 'CN=MONEYBROS_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'MONEYBROS_GRP';
        l_groups(1).samaccountname := 'MONEYBROS_GRP';

      WHEN 'bhill' THEN

        l_groups.extend;
        l_groups(1).cn := 'TIMCO_GRP';
        l_groups(1).description := 'Employees working on the Timco Industries account.';
        l_groups(1).distinguished_name := 'CN=TIMCO_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'TIMCO_GRP';
        l_groups(1).samaccountname := 'TIMCO_GRP';

        l_groups.extend;
        l_groups(2).cn := 'MONACLE_GRP';
        l_groups(2).description := 'Employees working on the Monacle Corporation account.';
        l_groups(2).distinguished_name := 'CN=MONACLE_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(2).name := 'MONACLE_GRP';
        l_groups(2).samaccountname := 'MONACLE_GRP';

      WHEN 'kvongkas' THEN

        l_groups.extend;
        l_groups(1).cn := 'MONEYBROS_GRP';
        l_groups(1).description := 'Employees working on the Moneybros account.';
        l_groups(1).distinguished_name := 'CN=MONEYBROS_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(1).name := 'MONEYBROS_GRP';
        l_groups(1).samaccountname := 'MONEYBROS_GRP';

        l_groups.extend;
        l_groups(2).cn := 'MONACLE_GRP';
        l_groups(2).description := 'Employees working on the Monacle Corporation account.';
        l_groups(2).distinguished_name := 'CN=MONACLE_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(2).name := 'MONACLE_GRP';
        l_groups(2).samaccountname := 'MONACLE_GRP';

        l_groups.extend;
        l_groups(3).cn := 'SCHMAPPLE_GRP';
        l_groups(3).description := 'Employees working on the Schmapple account.';
        l_groups(3).distinguished_name := 'CN=SCHMAPPLE_GRP,OU=Global Groups,DC=acme,DC=com';
        l_groups(3).name := 'SCHMAPPLE_GRP';
        l_groups(3).samaccountname := 'SCHMAPPLE_GRP';

    END CASE;
    RETURN l_groups;
  END get_user_groups_va;

END ldap_util;
/
SHOW ERRORS
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, LDAP_UTIL has three functions that return the three kinds of collections in PL/SQL: nested tables, associative arrays, and varrays.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;FUNCTION get_user_groups_nt (p_user VARCHAR2) RETURN ldap_groups_nt;
FUNCTION get_user_groups_aa (p_user VARCHAR2) RETURN ldap_groups_aa;
FUNCTION get_user_groups_va (p_user VARCHAR2) RETURN ldap_groups_va;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The collections consist of a group of records representing &amp;#8220;group&amp;#8221; data from Active Directory.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;TYPE ldap_entry_typ IS RECORD (
    cn VARCHAR2(30),
    description VARCHAR2(1000),
    distinguished_name VARCHAR2(200),
    name VARCHAR2(30),
    samaccountname VARCHAR2(30));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The p_user parameter would be the user&amp;#8217;s network id, corresponding to the sAMAccountName attribute in Active Directory; since we&amp;#8217;re hardcoding group data this is not important to us at the moment. The point is you pass in a username and you get a collection of groups.&lt;/p&gt;

&lt;p&gt;Using the same technique we demonstrated in part one, we now create a wrapper package of pipelined functions to transform the collection data into something we can query - an aggregate result set.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PACKAGE ldap_util_pipelined AS

  FUNCTION get_user_groups_nt (p_user VARCHAR2) RETURN ldap_util.ldap_groups_nt PIPELINED;
  FUNCTION get_user_groups_aa (p_user VARCHAR2) RETURN ldap_util.ldap_groups_nt PIPELINED;
  FUNCTION get_user_groups_va (p_user VARCHAR2) RETURN ldap_util.ldap_groups_va PIPELINED;

END ldap_util_pipelined;
/
SHOW ERRORS

CREATE OR REPLACE PACKAGE BODY ldap_util_pipelined AS

  FUNCTION get_user_groups_nt (p_user VARCHAR2) 
  RETURN ldap_util.ldap_groups_nt PIPELINED IS
    l_groups ldap_util.ldap_groups_nt;
  BEGIN
    l_groups := ldap_util.get_user_groups_nt(p_user);
    FOR i IN 1 .. l_groups.count LOOP
      PIPE ROW (l_groups(i));
    END LOOP;
    RETURN;
  END get_user_groups_nt;

  FUNCTION get_user_groups_aa (p_user VARCHAR2) 
  RETURN ldap_util.ldap_groups_nt PIPELINED IS
    l_groups ldap_util.ldap_groups_aa;
  BEGIN
    l_groups := ldap_util.get_user_groups_aa(p_user);
    FOR i IN 1 .. l_groups.count LOOP
      PIPE ROW (l_groups(i));
    END LOOP;
    RETURN;
  END get_user_groups_aa;

  FUNCTION get_user_groups_va (p_user VARCHAR2) 
  RETURN ldap_util.ldap_groups_va PIPELINED IS
    l_groups ldap_util.ldap_groups_va;
  BEGIN
    l_groups := ldap_util.get_user_groups_va(p_user);
    FOR i IN 1 .. l_groups.count LOOP
      PIPE ROW (l_groups(i));
    END LOOP;
    RETURN;
  END get_user_groups_va;

END ldap_util_pipelined;
/
SHOW ERRORS
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note the return types of the three functions in ldap_util_pipelined.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;FUNCTION get_user_groups_nt (p_user VARCHAR2) RETURN ldap_util.ldap_groups_nt PIPELINED;
FUNCTION get_user_groups_aa (p_user VARCHAR2) RETURN ldap_util.ldap_groups_nt PIPELINED;
FUNCTION get_user_groups_va (p_user VARCHAR2) RETURN ldap_util.ldap_groups_va PIPELINED;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first two return a nested table collection, while the third returns a VARRAY. These two collections can be pipelined because they correspond to native SQL datatypes in Oracle; not so for associative arrays. In order to pipeline the associative array returned from ldap_util.get_user_groups_aa, we need to map that collection to one of the other types. Here, we map it to a nested table. The iterative logic is the same, though, as the other two functions.&lt;/p&gt;

&lt;p&gt;Now we can verify that our approach worked by issuing some simple queries, supported by the SQL &amp;#8220;TABLE&amp;#8221; construct.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SQL&amp;gt; select * from table(ldap_util_pipelined.get_user_groups_nt('jaugusti'));

CN        DESCRIPTION                                        DISTINGUISHED_NAME                            NAME      SAMACCOUN
--------- -------------------------------------------------- --------------------------------------------- --------- ---------
TIMCO_GRP Employees working on the Timco Industries account. CN=TIMCO_GRP,OU=Global Groups,DC=acme,DC=com  TIMCO_GRP TIMCO_GRP
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, the individual attributes of the record structure form the columns of our new table, and we can reference those columns directly, if we wish.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SQL&amp;gt; select cn from table(ldap_util_pipelined.get_user_groups_nt('kvongkas'));

CN
---------------
MONEYBROS_GRP
MONACLE_GRP
SCHMAPPLE_GRP
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All that&amp;#8217;s left for us at this point is to construct a WHERE clause condition to use with Oracle&amp;#8217;s Virtual Private Database feature to limit data returned from queries against the table QUARTERLY_SALES_DATA. That condition could look something like this, assuming that our application connects to the database as the logged in user.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;client_id IN (SELECT CASE cn
                       WHEN 'TIMCO_GRP' THEN 100
                       WHEN 'MONEYBROS_GRP' THEN 200
                       WHEN 'MONACLE_GRP' THEN 300
                       WHEN 'SCHMAPPLE_GRP' THEN 400
                     END
                FROM TABLE(ldap_util_pipelined.get_user_groups_nt(USER)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here the mapping between AD group name and client ID is hardcoded in the CASE expression. A more flexible way would be to create a mapping table. Also, this is probably not a case study in writing a well performing VPD clause, but the real point here is that using SQL table functions and PL/SQL pipelined functions, we can translate data retrieved from an API, LDAP directory, or web service into table-like structures that we can query, join, and otherwise manipulate just like real tables.&lt;/p&gt;</description><link>http://tjmoracle.tumblr.com/post/21936772570</link><guid>http://tjmoracle.tumblr.com/post/21936772570</guid><pubDate>Fri, 27 Apr 2012 18:35:00 -0400</pubDate><category>9i</category><category>Oracle</category><category>PIPE ROW</category><category>PIPELINED</category><category>TABLE</category><category>table functions</category></item></channel></rss>
