<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Daniel Hochleitner - Blog]]></title><description><![CDATA[Daniel Hochleitner - Blog]]></description><link>https://blog.danielhochleitner.de</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 13:31:23 GMT</lastBuildDate><atom:link href="https://blog.danielhochleitner.de/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[New apex.date JS API in APEX 21.2]]></title><description><![CDATA[As John already teased in his blog post about the JavaScript API changes in APEX 21.2, there is a new apex.date namespace coming.
Main purpose of it is to have an out-of-the-box replacement for Moment.js which is part of APEX since we introduced our ...]]></description><link>https://blog.danielhochleitner.de/new-apexdate-js-api-in-apex-212</link><guid isPermaLink="true">https://blog.danielhochleitner.de/new-apexdate-js-api-in-apex-212</guid><dc:creator><![CDATA[Daniel Hochleitner]]></dc:creator><pubDate>Sun, 10 Oct 2021 22:00:58 GMT</pubDate><content:encoded><![CDATA[<p>As John already teased in his <a target="_blank" href="https://hardlikesoftware.com/weblog/2021/10/11/apex-21-2-new-javascript-apis-joelkallmanday/">blog post</a> about the JavaScript API changes in APEX 21.2, there is a new <em>apex.date</em> namespace coming.</p>
<p>Main purpose of it is to have an out-of-the-box replacement for <a target="_blank" href="https://github.com/moment/moment/">Moment.js</a> which is part of APEX since we introduced our Calendar region with FullCalendar version 3. In APEX 21.1 this region was updated to use the newer version 5 of the underlying FullCalendar library, and this particular version doesn't come with Moment.js anymore and changed many other things, especially from an API point of view. If you're interested in these changes in more detail, we created a <a target="_blank" href="https://blogs.oracle.com/apex/post/calendar-region-upgrade-guide-in-apex-211">blog posting</a> back when 21.1 was released to help developers to get started with this new version.</p>
<p>Additionally even Moment.js itself states, that you shouldn't use this library anymore:</p>
<p><img src="https://blogdanielhochleitner.files.wordpress.com/2021/10/moment_legacy.png?w=1024" alt /></p>
<p>So we thought it would be really helpful for all APEX developers to have an integrated API which can do most of the things Moment.js did + things that make sense in an APEX or database environment.</p>
<p>There are 4 main categories in which you will find functions to use in <em>apex.date</em>:</p>
<ul>
<li><p>Date Manipulation</p>
</li>
<li><p>Date Formatting &amp; Parsing</p>
</li>
<li><p>Date Comparison</p>
</li>
<li><p>Miscellaneous Functions</p>
</li>
</ul>
<p>Before starting with the description of the actual categories and showing some code examples, it is worth to mention that <em>apex.date</em> operates with native JavaScript date objects and doesn't introduce new custom objects like Moment.js did.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();
</code></pre>
<h3 id="heading-date-manipulation">Date Manipulation</h3>
<p>This category contains functions to manipulate JavaScript date objects, like adding or subtracting given amounts to or from a date.</p>
<p>So lets add something to a given date:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();

<span class="hljs-comment">// add 2 years</span>
apex.date.add( date, <span class="hljs-number">2</span>, apex.date.UNIT.YEAR );

<span class="hljs-comment">// add 5 days</span>
apex.date.add( date, <span class="hljs-number">5</span>, apex.date.UNIT.DAY );

<span class="hljs-comment">// add 45 minutes</span>
apex.date.add( date, <span class="hljs-number">45</span>, apex.date.UNIT.MINUTE );

<span class="hljs-built_in">console</span>.log( date );
</code></pre>
<p>And now lets subtract something from a date:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();

<span class="hljs-comment">// subtract 2 months</span>
apex.date.subtract( date, <span class="hljs-number">2</span>, apex.date.UNIT.MONTH );

<span class="hljs-comment">// subtract 30 seconds</span>
apex.date.subtract( date, <span class="hljs-number">30</span>, apex.date.UNIT.SECOND );

<span class="hljs-comment">// subtract 4 weeks</span>
apex.date.subtract( date, <span class="hljs-number">4</span>, apex.date.UNIT.WEEK );

<span class="hljs-built_in">console</span>.log( date );
</code></pre>
<p>As you can see, we used the <em>apex.date.UNIT</em> object in both examples, this has constants for all units used by <em>apex.date</em></p>
<p><img src="https://blogdanielhochleitner.files.wordpress.com/2021/10/apex_date_unit.png?w=1024" alt /></p>
<h3 id="heading-date-formatting-andamp-parsing">Date Formatting &amp; Parsing</h3>
<p>In this category there's everything related to date formatting &amp; parsing. The most important part to mention is that <em>apex.date</em> handles it formatting &amp; parsing logic using Oracle Database compatible format masks, which makes it easy for APEX developers to re-use their DB logic also on the client, without investigating too much brain power in thinking in 2 separate format mask flavors.</p>
<p>Date formatting:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(),
    dateString = <span class="hljs-string">""</span>;

dateString = apex.date.format( date, <span class="hljs-string">"YYYY-MM-DD HH24:MI:SS"</span> );
<span class="hljs-comment">// output: 2021-10-11 22:15:45</span>

dateString = apex.date.format( date, <span class="hljs-string">"YYYY/MM/DD"</span> );
<span class="hljs-comment">// output: 2021/10/11</span>

dateString = apex.date.format( date, <span class="hljs-string">"YYYY, IW"</span> );
<span class="hljs-comment">// output: 2021, 41</span>

dateString = apex.date.format( date, <span class="hljs-string">"Day, DD Month YYYY"</span> );
<span class="hljs-comment">// output: Monday, 11 October 2021</span>

dateString = apex.date.format( date, <span class="hljs-string">"DL"</span> );
<span class="hljs-comment">// output: Monday, October 11, 2021</span>

dateString = apex.date.format( date, <span class="hljs-string">"DL"</span>, <span class="hljs-string">"de"</span> );
<span class="hljs-comment">// output: Montag, 11. Oktober 2021</span>

dateString = apex.date.format( date, <span class="hljs-string">"DL"</span>, <span class="hljs-string">"es"</span> );
<span class="hljs-comment">// output: lunes, 11 de octubre de 2021</span>

<span class="hljs-comment">// relative date / time as words</span>
dateString = apex.date.since( date );
<span class="hljs-comment">// output: "10 months from now" or "2 months ago"</span>

dateString = apex.date.since( date, <span class="hljs-literal">true</span> );
<span class="hljs-comment">// output: "in 10mo" or "2mo"</span>
</code></pre>
<p>Date parsing:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> date;

date = apex.date.parse( <span class="hljs-string">"2021-10-11 22:15"</span>, <span class="hljs-string">"YYYY-MM-DD HH24:MI"</span>);
date = apex.date.parse( <span class="hljs-string">"2021/10/11"</span>, <span class="hljs-string">"YYYY/MM/DD"</span>);
date = apex.date.parse( <span class="hljs-string">"2021-OCT-11 22:15:45"</span>, <span class="hljs-string">"YYYY-MON-DD HH24:MI:SS"</span>);
</code></pre>
<p>If no format or language is specified, <em>apex.date</em> automatically uses the format and language defined in your application. You can also get this information using:</p>
<pre><code class="lang-javascript">apex.locale.getDateFormat();
apex.locale.getLanguage();
</code></pre>
<h3 id="heading-date-comparison">Date Comparison</h3>
<p>This category contains functions useful to compare 2 or more JavaScript date objects with each other.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> date1 = apex.date.parse( <span class="hljs-string">"2021/10/01"</span>, <span class="hljs-string">"YYYY/MM/DD"</span>),
    date2 = apex.date.parse( <span class="hljs-string">"2021/10/15"</span>, <span class="hljs-string">"YYYY/MM/DD"</span>),
    date3 = apex.date.parse( <span class="hljs-string">"2021/10/31"</span>, <span class="hljs-string">"YYYY/MM/DD"</span>);

<span class="hljs-comment">// is date 1 &lt; date 2</span>
apex.date.isBefore( date1, date2, apex.date.UNIT.DAY );
<span class="hljs-comment">// output: true</span>

<span class="hljs-comment">// is date 1 &gt; date 2</span>
apex.date.isAfter( date1, date2, apex.date.UNIT.DAY );
<span class="hljs-comment">// output: false</span>

<span class="hljs-comment">// is date 1 = date 2</span>
apex.date.isSame( date1, date2, apex.date.UNIT.DAY );
<span class="hljs-comment">// output: false</span>

<span class="hljs-comment">// is date 1 &lt;= date 2</span>
apex.date.isSameOrBefore( date1, date2, apex.date.UNIT.DAY );
<span class="hljs-comment">// output: true</span>

<span class="hljs-comment">// is date 1 &gt;= date 2</span>
apex.date.isSameOrAfter( date1, date2, apex.date.UNIT.DAY );
<span class="hljs-comment">// output: false</span>

<span class="hljs-comment">// is date 1 between date 2 and date 3</span>
apex.date.isBetween( date1, date2, date3, apex.date.UNIT.DAY );
<span class="hljs-comment">// output: false</span>

<span class="hljs-comment">// get the minimum date of given date arguments</span>
<span class="hljs-keyword">var</span> minDate = apex.date.min( date1, date2, date3 );
<span class="hljs-comment">// output: date1 as date object</span>

<span class="hljs-comment">// get the maximum date of given date arguments</span>
<span class="hljs-keyword">var</span> maxDate = apex.date.max( date1, date2, date3 );
<span class="hljs-comment">// output: date3 as date object</span>
</code></pre>
<p>Most compare functions are also utilizing <em>apex.date.UNIT</em> constants. This enables developers to precisely compare dates from milliseconds up to years.</p>
<h3 id="heading-miscellaneous">Miscellaneous</h3>
<p>Beside the other categorized functionality, <em>apex.date</em> also has more generic functions or useful helper functions on board, which I won't describe here in full length, but to just get an idea, here are a few of them:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();

<span class="hljs-comment">// get ISO week number</span>
<span class="hljs-keyword">var</span> week = apex.date.ISOWeek( date );

<span class="hljs-comment">// get total days of month</span>
<span class="hljs-keyword">var</span> days = apex.date.daysInMonth( date );

<span class="hljs-comment">// get first of month as date object</span>
<span class="hljs-keyword">var</span> first = apex.date.firstOfMonth( date );

<span class="hljs-comment">// get last of month as date object</span>
<span class="hljs-keyword">var</span> last = apex.date.lastOfMonth( date );

<span class="hljs-comment">// get months between 2 dates</span>
<span class="hljs-keyword">var</span> months = apex.date.monthsBetween( now, date );

<span class="hljs-comment">// check if it's a leap year</span>
<span class="hljs-keyword">var</span> isLeapYear = apex.date.isLeapYear( date );
</code></pre>
<p>The whole <em>apex.date</em> API documentation is part of our official JavaScript API documentation, which when APEX 21.2 is ready will be available <a target="_blank" href="https://docs.oracle.com/en/database/oracle/application-express/21.2/aexjs/index.html">here</a>. I also plan to publish a demo app show casing everything in this post live in an APEX app. This app will be released as soon as 21.2 is available on <a target="_blank" href="https://apex.oracle.com/">apex.oracle.com</a>.</p>
<p>Edit 18/11/2021: As promised, and now that APEX 21.2 pre-production release is available at apex.oracle.com, you can find the demo app <a target="_blank" href="https://apex.oracle.com/pls/apex/dhrest/r/apex-date-demo">here</a>.</p>
<p>Happy APEXing! 🚀</p>
]]></content:encoded></item><item><title><![CDATA[Check local APEX plugin version against apex.world]]></title><description><![CDATA[There's something new on apex.world: A REST endpoint to retrieve information about listed open source plugins from the apex.world plugins directory! Just fire up a HTTP GET call to https://apex.world/ords/apex_world/plugin/list/ to retrieve all plugi...]]></description><link>https://blog.danielhochleitner.de/check-local-apex-plugin-version-against-apexworld</link><guid isPermaLink="true">https://blog.danielhochleitner.de/check-local-apex-plugin-version-against-apexworld</guid><dc:creator><![CDATA[Daniel Hochleitner]]></dc:creator><pubDate>Tue, 04 Feb 2020 23:04:44 GMT</pubDate><content:encoded><![CDATA[<p>There's something new on apex.world: A REST endpoint to retrieve information about listed open source plugins from the apex.world plugins directory! Just fire up a HTTP GET call to <a target="_blank" href="https://apex.world/ords/apex_world/plugin/list/">https://apex.world/ords/apex_world/plugin/list/</a> to retrieve all plugin information in JSON format... You can now use this information to compare your locally installed plugins (which of course are installed from apex.world before) with the ones listed on apex.world. In this example I use it to compare the locally installed plugin version to the newest version available, very useful for update checks where you want to know if there's a newer version of the plugins available! Just create a view like this in your schema (using APEX_DATA_PARSER: for all DBs and APEX versions &gt;= 19.1): [code lang=text] create or replace force view v_apex_world_plugins as select apex_world_plugins_t.line_number ,apex_world_plugins_t.col001 as plg_int_name ,apex_world_plugins_t.col002 as plg_name ,apex_world_plugins_t.col003 as plg_desc ,apex_world_plugins_t.col004 as plg_type ,apex_world_plugins_t.col005 as plg_version ,apex_world_plugins_t.col006 as created ,apex_world_plugins_t.col007 as updated ,apex_world_plugins_t.col008 as apex_versions ,apex_world_plugins_t.col009 as demo_url ,apex_world_plugins_t.col010 as download_url ,apex_world_plugins_t.col011 as homepage_url ,apex_world_plugins_t.col012 as like_count ,apex_world_plugins_t.col013 as author_name ,apex_world_plugins_t.col014 as author_email ,apex_world_plugins_t.col015 as author_url ,apex_world_plugins_t.col016 as author_twitter from table(apex_data_parser.parse(p_content =&gt; apex_web_service.make_rest_request_b(p_url =&gt; 'https://apex.world/ords/apex_world/plugin/list/' ,p_http_method =&gt; 'GET') ,p_file_name =&gt; 'apex_world_plugins.json' ,p_file_profile =&gt; q'[ { "file-type": 4, "single-row": false, "file-encoding": "AL32UTF8", "row-selector": "items", "headings-in-first-row": false, "csv-enclosed": "\"", "columns": [ { "name": "PLG_INT_NAME", "data-type": 1, "data-type-len": 255, "selector": "plg_int_name" }, { "name": "PLG_NAME", "data-type": 1, "data-type-len": 255, "selector": "plg_name" }, { "name": "PLG_DESC", "data-type": 1, "data-type-len": 4000, "selector": "plg_desc" }, { "name": "PLG_TYPE", "data-type": 1, "data-type-len": 50, "selector": "plg_type" }, { "name": "PLG_VERSION", "data-type": 1, "data-type-len": 50, "selector": "plg_version" }, { "name": "CREATED", "data-type": 5, "selector": "created", "format-mask": "YYYY\"-\"MM\"-\"DD\"T\"HH24\":\"MI:SSTZR" }, { "name": "UPDATED", "data-type": 5, "selector": "updated", "format-mask": "YYYY\"-\"MM\"-\"DD\"T\"HH24\":\"MI:SSTZR" }, { "name": "APEX_VERSIONS", "data-type": 1, "data-type-len": 255, "selector": "apex_versions" }, { "name": "DEMO_URL", "data-type": 1, "data-type-len": 255, "selector": "demo_url" }, { "name": "DOWNLOAD_URL", "data-type": 1, "data-type-len": 255, "selector": "download_url" }, { "name": "HOMEPAGE_URL", "data-type": 1, "data-type-len": 255, "selector": "homepage_url" }, { "name": "LIKE_COUNT", "data-type": 2, "decimal-char": ".", "selector": "like_count" }, { "name": "AUTHOR_NAME", "data-type": 1, "data-type-len": 100, "selector": "author_name" }, { "name": "AUTHOR_EMAIL", "data-type": 1, "data-type-len": 255, "selector": "author_email" }, { "name": "AUTHOR_URL", "data-type": 1, "data-type-len": 255, "selector": "author_url" }, { "name": "AUTHOR_TWITTER", "data-type": 1, "data-type-len": 50, "selector": "author_twitter" } ], "parsed-rows": 200 } ]')) apex_world_plugins_t; [/code] Or like this (using XMLTABLE: for all DBs and APEX versions): [code lang=text] create or replace view v_apex_world_plugins as select apex_world_plugins_t.plg_int_name ,apex_world_plugins_t.plg_name ,apex_world_plugins_t.plg_desc ,apex_world_plugins_t.plg_type ,apex_world_plugins_t.plg_version ,apex_world_plugins_t.created ,apex_world_plugins_t.updated ,apex_world_plugins_t.apex_versions ,apex_world_plugins_t.demo_url ,apex_world_plugins_t.download_url ,apex_world_plugins_t.homepage_url ,apex_world_plugins_t.like_count ,apex_world_plugins_t.author_name ,apex_world_plugins_t.author_email ,apex_world_plugins_t.author_url ,apex_world_plugins_t.author_twitter from xmltable('/json/items/row' passing apex_json.to_xmltype(apex_web_service.make_rest_request(p_url =&gt; 'https://apex.world/ords/apex_world/plugin/list/' ,p_http_method =&gt; 'GET')) columns plg_int_name path 'plg_int_name' ,plg_name path 'plg_name' ,plg_desc path 'plg_desc' ,plg_type path 'plg_type' ,plg_version path 'plg_version' ,created path 'created' ,updated path 'updated' ,apex_versions path 'apex_versions' ,demo_url path 'demo_url' ,download_url path 'download_url' ,homepage_url path 'homepage_url' ,like_count path 'like_count' ,author_name path 'author_name' ,author_email path 'author_email' ,author_url path 'author_url' ,author_twitter path 'author_twitter') apex_world_plugins_t [/code] Or like this (using JSON_TABLE: for DBs &gt;= 12.1 and all APEX versions): [code lang=text] create or replace view v_apex_world_plugins as select apex_world_plugins_t.plg_int_name ,apex_world_plugins_t.plg_name ,apex_world_plugins_t.plg_desc ,apex_world_plugins_t.plg_type ,apex_world_plugins_t.plg_version ,apex_world_plugins_t.created ,apex_world_plugins_t.updated ,apex_world_plugins_t.apex_versions ,apex_world_plugins_t.demo_url ,apex_world_plugins_t.download_url ,apex_world_plugins_t.homepage_url ,apex_world_plugins_t.like_count ,apex_world_plugins_t.author_name ,apex_world_plugins_t.author_email ,apex_world_plugins_t.author_url ,apex_world_plugins_t.author_twitter from json_table(apex_web_service.make_rest_request(p_url =&gt; 'https://apex.world/ords/apex_world/plugin/list/' ,p_http_method =&gt; 'GET') ,'$.items[*]' columns(plg_int_name varchar2(250) path '$.plg_int_name' ,plg_name varchar2(250) path '$.plg_name' ,plg_desc varchar2(4000) path '$.plg_desc' ,plg_type varchar2(100) path '$.plg_type' ,plg_version varchar2(50) path '$.plg_version' ,created varchar2(50) path '$.created' ,updated varchar2(50) path '$.updated' ,apex_versions varchar2(100) path '$.apex_versions' ,demo_url varchar2(1000) path '$.demo_url' ,download_url varchar2(1000) path '$.download_url' ,homepage_url varchar2(1000) path '$.homepage_url' ,like_count number path '$.like_count' ,author_name varchar2(250) path '$.author_name' ,author_email varchar2(250) path '$.author_email' ,author_url varchar2(1000) path '$.author_url' ,author_twitter varchar2(100) path '$.author_twitter')) apex_world_plugins_t; [/code] <em>Note: Thus the web service is running over HTTPS a Oracle Wallet is needed to connect to it!</em> <em>I created a script which creates such a wallet with all valid public root CAs a while back: <a target="_blank" href="https://github.com/Dani3lSun/oracle-ca-wallet-creator">https://github.com/Dani3lSun/oracle-ca-wallet-creator</a></em> And with this select statement you get the local plugin version and the version from apex.world for easier comparison: [code lang=text] select apex_appl_plugins.name ,apex_appl_plugins.display_name ,apex_appl_plugins.version_identifier as version_local ,v_apex_world_plugins.plg_version as version_apex_world from apex_appl_plugins ,v_apex_world_plugins where apex_appl_plugins.name = v_apex_world_plugins.plg_int_name and apex_appl_plugins.application_id = 103 [/code] The output should be like that: <img src="https://blogdanielhochleitner.files.wordpress.com/2020/02/compare_plugin_versions.png" alt /> And if it differs, it's time for an update! Just as easy as 123! :)</p>
]]></content:encoded></item><item><title><![CDATA[Beautiful images of your source code]]></title><description><![CDATA[About a year ago I came across some handy little tool to create nice looking images of your source code, it´s called carbon and can be used via your web browser. Just copy & paste your code into the textbox, do some adjustments like selecting a theme...]]></description><link>https://blog.danielhochleitner.de/beautiful-images-of-your-source-code</link><guid isPermaLink="true">https://blog.danielhochleitner.de/beautiful-images-of-your-source-code</guid><dc:creator><![CDATA[Daniel Hochleitner]]></dc:creator><pubDate>Mon, 20 Aug 2018 22:07:59 GMT</pubDate><content:encoded><![CDATA[<p>About a year ago I came across some handy little tool to create nice looking images of your source code, it´s called <a target="_blank" href="https://carbon.now.sh/"><strong>carbon</strong></a> and can be used via your web browser. Just copy &amp; paste your code into the textbox, do some adjustments like selecting a theme, background color or your code language. After that you are able to export it to an image file, which of course can be used everywhere, I usually use it for examples or in my presentation slides... What was new for me is, that someone created a command line utility which wraps the functionality of the homepage, which of course is way nicer to use :)<br />So I don´t have to copy &amp; paste the code again and again, instead I save my work to a local file and then can generate an nice image just be firing some commands, even batch processing is now possible and saves me a lot of time.<br />You will find the whole project with instructions on GitHub: <a target="_blank" href="https://github.com/mixn/carbon-now-cli">https://github.com/mixn/carbon-now-cli</a> That´s it, hope the one or other also finds this tool helpful! And what´s nicer than beautiful source code :) <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2018/08/carbon_webpage.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2018/08/carbon_webpage.png?w=1024" alt /></a></p>
]]></content:encoded></item><item><title><![CDATA[Oracle APEX Plugin Performance]]></title><description><![CDATA[Last week a customer of mine hired me to look into performance issues they had at larger forms within their APEX inhouse app. After digging into it, I recognized that they used a lot of plugins in this particular app (~ 90), and of course a lot of kn...]]></description><link>https://blog.danielhochleitner.de/oracle-apex-plugin-performance</link><guid isPermaLink="true">https://blog.danielhochleitner.de/oracle-apex-plugin-performance</guid><dc:creator><![CDATA[Daniel Hochleitner]]></dc:creator><pubDate>Sat, 10 Feb 2018 23:07:14 GMT</pubDate><content:encoded><![CDATA[<p><a target="_blank" href="https://orclapex.io/ords/f?p=PLG_PERFORMANCE"><img src="https://blogdanielhochleitner.files.wordpress.com/2018/02/plugin_performance.png?w=1024" alt /></a> Last week a customer of mine hired me to look into performance issues they had at larger forms within their APEX inhouse app. After digging into it, I recognized that they used a lot of plugins in this particular app (~ 90), and of course a lot of known ones from the community like Select2 or Dropzone. The problematic pages were mostly larger forms with a lot of items, in this case also a lot of plugin items, like mentioned above Select2. This item type plugin was used around 30 times on this page... So first I had a look into the debug output of the page and I saw that most of the output was the PL/SQL source code of this plugin. So 30 times the APEX engine had to parse and execute the complete PL/SQL source of Select2, and the newest version of it has 773 lines of PL/SQL code... The second problem I found was that all static files of the plugin (e.g JavaScript and CSS files) came from the database, you will see that in the debug output in APEX &gt; 5.0 environments on the file path of the files that are loaded, e.g: [code language="text"] Load JavaScript file=demo/r/140/files/plugin/53116667758949585/v1/select2-apex.js [/code] File paths which contain the virtual directory "/r/" shows us that the file being loaded comes from the DB, so each time a static file was loaded it hits the DB which returns a BLOB. Of course browsers will cache such a file after getting it once, but in my opinion the best place for static files is the web server itself not the DB... <strong>So I found 2 problems in this case:</strong> 1) Large PL/SQL code of a plugin 2) Plugin static files are loaded from DB <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2018/02/apex_plugin_source_before.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2018/02/apex_plugin_source_before.png?w=300" alt /></a><a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2018/02/apex_plugin_files_before.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2018/02/apex_plugin_files_before.png?w=300" alt /></a> <strong>A possible solution for both:</strong> 1) Transfer the PL/SQL code from the plugin to a real DB object, e.g a PL/SQL package 2) Transfer all plugin static files to the web server After creating a PL/SQL package (e.g. select2_plg_pkg) with the copied source code from the plugin and moving all static files to the web server, the plugin looked like this: <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2018/02/apex_plugin_source_after1.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2018/02/apex_plugin_source_after1.png?w=300" alt /></a><a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2018/02/apex_plugin_files_after.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2018/02/apex_plugin_files_after.png?w=300" alt /></a> So lets compare the page performance before and after the changes (30 Select2 items / 2 Dropzone Regions). With the orginal plugins the page rendering took: ~ 4.3 seconds With the modified plugins the page rendering took: ~ 1.3 seconds This is an performance improvement of ~330% !! So the page is 3x faster in rendering than before. To be honest I didn´t expect such an performance increase by changing the plugin code... If you want to see it in action, here´s a little demo app: <a target="_blank" href="https://orclapex.io/ords/f?p=PLG_PERFORMANCE">https://orclapex.io/ords/f?p=PLG_PERFORMANCE</a> Use demo / demo for login! <strong>Conclusion:</strong> As best practise learns us to have as less code in APEX as possible (e.g only function calls), this is also valid for plugins. So be careful in using plugins and always try to review the code. In this case the plugins are not bad coded or something like that, instead it´s the most easy way to share plugins with others, having files and PL/SQL source code inside the plugin. This means that you only need the plugin export SQL file to import and use it. But some plugins have a lot of source code (here over 700 lines) which makes it problemtatic using such a plugin many times on a page, because this source code have to be parsed again and again. If you decide to also improve the performance of your plugins like I told you here, be careful, thus the plugin now consists of several parts: - the plugin export SQL file - the static files / folder - the pl/sql package And if you want to share your plugin or use it in another application you have to import all 3 parts of it! Another disadvantage would be, if an plugin developer updates the open source plugin you have to catch up the changes by yourself, because you have changed some logic inside of the plugin before...</p>
]]></content:encoded></item><item><title><![CDATA[DOAG 2017: APEX APIs - Slides and Demo]]></title><description><![CDATA[Thus my presentation was to 90% demo and I think demos should always be available, so attendees can reproduce what they saw, here´s a link to an online version of it: https://orclapex.io/ords/f?p=APEXAPI Use demo / demo to login... If you´re interest...]]></description><link>https://blog.danielhochleitner.de/doag-2017-apex-apis-slides-and-demo</link><guid isPermaLink="true">https://blog.danielhochleitner.de/doag-2017-apex-apis-slides-and-demo</guid><dc:creator><![CDATA[Daniel Hochleitner]]></dc:creator><pubDate>Thu, 23 Nov 2017 23:08:50 GMT</pubDate><content:encoded><![CDATA[<p>Thus my presentation was to 90% demo and I think demos should always be available, so attendees can reproduce what they saw, here´s a link to an online version of it: <a target="_blank" href="https://orclapex.io/ords/f?p=APEXAPI">https://orclapex.io/ords/f?p=APEXAPI</a> Use <em>demo / demo</em> to login... If you´re interested in an export file you can download it <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/11/f119_apex_api_demo.zip">here</a> to import it into your own workspace... The complete presentation (in german) can be found on my homepage: <a target="_blank" href="https://danielhochleitner.de/ords/web/slides/">https://danielhochleitner.de/ords/web/slides/</a> Thanks for attending &amp; see you at the next conference!</p>
]]></content:encoded></item><item><title><![CDATA[Blockchain with Oracle and APEX]]></title><description><![CDATA[Thus the whole IT world talks about blockchains, the bitcoin price is as high as never (which underlying technology is a blockchain mechanism) and even Oracle has announced a new Blockchain Cloud Service at OOW, I looked into that topic and tried to ...]]></description><link>https://blog.danielhochleitner.de/blockchain-with-oracle-and-apex</link><guid isPermaLink="true">https://blog.danielhochleitner.de/blockchain-with-oracle-and-apex</guid><dc:creator><![CDATA[Daniel Hochleitner]]></dc:creator><pubDate>Mon, 09 Oct 2017 22:09:31 GMT</pubDate><content:encoded><![CDATA[<p>Thus the whole IT world talks about blockchains, the bitcoin price is as high as never (which underlying technology is a blockchain mechanism) and even Oracle has announced a new <a target="_blank" href="https://cloud.oracle.com/en_US/blockchain">Blockchain Cloud Service</a> at OOW, I looked into that topic and tried to create a simple blockchain by my own using the Oracle Database and of course APEX... Before we can start developing on a blockchain mechanism we have to investigate some time to get a basic understanding what a blockchain is and how it works all together. So lets get started with some terminology:</p>
<p><strong>What is a Blockchain?</strong></p>
<p>A blockchain is a distributed database with a list (chain) of records (blocks) linked and secured by digital fingerprints (crypto hashes)</p>
<pre><code class="lang-plaintext">[#&lt;Block:0x1eed2a0
  @index = 0,
  @timestamp = 1637-09-15 20:52:38,
  @data = "Genesis",
  @previous_hash = "0",
  @hash = "edbd4e11e69bc399a9ccd8faaea44fb27410fe8e3023bb9462450a0a9c4caa1b"&gt;,
 #&lt;Block:0x1eec9a0
  @index = 1,
  @timestamp = 1637-09-15 21:02:38,
  @data = "Transaction Data...",
  @previous_hash = "edbd4e11e69bc399a9ccd8faaea44fb27410fe8e3023bb9462450a0a9c4caa1b",
  @hash = "eb8ecbf6d5870763ae246e37539d82e37052cb32f88bb8c59971f9978e437743"&gt;,
 #&lt;Block:0x1eec838
  @index = 2,
  @timestamp = 1637-09-15 21:12:38,
  @data = "Transaction Data......",
  @previous_hash = "eb8ecbf6d5870763ae246e37539d82e37052cb32f88bb8c59971f9978e437743",
  @hash = "be50017ee4bbcb33844b3dc2b7c4e476d46569b5df5762d14ceba9355f0a85f4"&gt;,
  ...
</code></pre>
<p><strong>What's a Hash? What's a Crypto(graphic) Hash Digest Checksum?</strong></p>
<p>A hash e.g. <em>eb8ecbf6d5870763ae246e37539d82e37052cb32f88bb8c59971f9978e437743</em> is a small digest checksum calculated with a one-way crypto(graphic) hash digest checksum function e.g. SHA256 (Secure Hash Algorithm 256 Bits) from the data</p>
<pre><code class="lang-plaintext">sha = Digest::SHA256.new
sha.update( @index + @timestamp + @data + @previous_hash )
</code></pre>
<p><strong>A blockchain uses</strong></p>
<ul>
<li><p>the block index <em>(e.g. 1,2,3,4, etc.)</em></p>
</li>
<li><p>the block timestamp <em>(e.g. 10-OKT-2017 16:16:59,216566 +02:00)</em></p>
</li>
<li><p>the block data <em>(e.g. Transaction Data...)</em></p>
</li>
<li><p>the hash from the previous block <em>(e.g. edbd4e11e69bc399a9ccd8faaea44fb27410fe8e3023bb9462450a0a9c4caa1b)</em></p>
</li>
<li><p>the hash <em>(e.g. be50017ee4bbcb33844b3dc2b7c4e476d46569b5df5762d14ceba9355f0a85f4)</em>: calculated by the hash digest checksum of (@index + @timestamp + @data + @previous_hash)</p>
</li>
</ul>
<p>By design, blockchains are inherently resistant to modification of the data. You can not modify one block without modifying all other blocks in the chain too. If the blockchain is shared and distributed with others it is even more resistant to modification... Now, after knowing some basic concepts about blockchains the next step would be: How can we integrate such technology into the Oracle Database? And of course a basic blockchain is kind of simple to implement. Our blockchain would be nothing more than a simple table with the columns we already saw in the example above: index, timestamp, data, previous hash and hash. One table row would then be one block of the blockchain. So lets get finally into what we all like, PL/SQL stuff to explain it a bit better:</p>
<p><strong>1. Our blockchain table</strong></p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Create table</span>
<span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> BLOCKCHAIN
(
  bc_index         <span class="hljs-built_in">number</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>,
  bc_timestamp     <span class="hljs-built_in">timestamp</span> <span class="hljs-keyword">with</span> <span class="hljs-built_in">time</span> zone <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>,
  bc_data          <span class="hljs-keyword">clob</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>,
  bc_previous_hash <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">500</span>) <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>,
  bc_hash          <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">500</span>) <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>
);
<span class="hljs-comment">-- Create/Recreate primary, unique and foreign key constraints </span>
<span class="hljs-keyword">alter</span> <span class="hljs-keyword">table</span> BLOCKCHAIN
  <span class="hljs-keyword">add</span> <span class="hljs-keyword">constraint</span> BLOCKCHAIN_PK primary <span class="hljs-keyword">key</span> (BC_INDEX);
</code></pre>
<p><strong>2. A sequence that increments our index column (Primary Key)</strong></p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Create sequence </span>
<span class="hljs-keyword">create</span> <span class="hljs-keyword">sequence</span> BLOCKCHAIN_SEQ
<span class="hljs-keyword">minvalue</span> <span class="hljs-number">1</span>
maxvalue <span class="hljs-number">9999999999999999999999999999</span>
<span class="hljs-keyword">start</span> <span class="hljs-keyword">with</span> <span class="hljs-number">1</span>
<span class="hljs-keyword">increment</span> <span class="hljs-keyword">by</span> <span class="hljs-number">1</span>
nocache;
</code></pre>
<p><strong>3. An before insert trigger that set our index column to a new incremented value on each insert</strong></p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Create Before Insert Trigger</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">OR</span> <span class="hljs-keyword">REPLACE</span> <span class="hljs-keyword">TRIGGER</span> blockchain_bi_trg
  <span class="hljs-keyword">BEFORE</span> <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">ON</span> blockchain
  <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">EACH</span> <span class="hljs-keyword">ROW</span>
<span class="hljs-keyword">DECLARE</span>
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-keyword">IF</span> :new.bc_index <span class="hljs-keyword">IS</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">THEN</span>
    :new.bc_index := blockchain_seq.nextval;
  <span class="hljs-keyword">END</span> <span class="hljs-keyword">IF</span>;
<span class="hljs-keyword">END</span>;
</code></pre>
<p>After that, our blockchain is in place and waits to get filled with data. Before we can do this we will need some kind of API to do that. So lets create a PL/SQL package that manages some basic functionality to interact with our blockchain. That's what it should be able to do in the end:</p>
<ul>
<li><p>calculate hashes</p>
</li>
<li><p>get a block</p>
</li>
<li><p>add a block</p>
</li>
<li><p>check blockchain health (valid or not)</p>
</li>
<li><p>get complete blockchain (starting with specified index)</p>
</li>
</ul>
<p>And guess what? The Oracle Database has all features to do this! Thus we need cryptographic functionality (hashing) we will use <em>DBMS_CRYPTO</em> for this, so we will need a grant to our schema:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Grant to DBMS_CRYPTO as SYS</span>
<span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">EXECUTE</span> <span class="hljs-keyword">ON</span> sys.dbms_crypto <span class="hljs-keyword">TO</span> blockchain_schema;
</code></pre>
<p>If you´re not able or allowed to grant this package, then there´s an alternative open source PL/SQL utility for that: <a target="_blank" href="https://github.com/OraOpenSource/oos-utils">OOS Utils</a> Now we can start with the PL/SQL package, here are only some code snippets to get a understanding, the whole source code with an APEX demo app is available on GitHub: <a target="_blank" href="https://github.com/Dani3lSun/oracle-blockchain">https://github.com/Dani3lSun/oracle-blockchain</a></p>
<p><strong>Get a block (basically get an table row record)</strong></p>
<pre><code class="lang-sql">FUNCTION get_block(p_bc_index IN blockchain.bc_index%TYPE)
  RETURN blockchain%ROWTYPE IS
  <span class="hljs-comment">--</span>
  l_blockchain_row blockchain%ROWTYPE;
  <span class="hljs-comment">--</span>
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-comment">--</span>
  <span class="hljs-keyword">BEGIN</span>
    <span class="hljs-keyword">SELECT</span> *
      <span class="hljs-keyword">INTO</span> l_blockchain_row
      <span class="hljs-keyword">FROM</span> blockchain
     <span class="hljs-keyword">WHERE</span> blockchain.bc_index = p_bc_index;
  EXCEPTION
    WHEN no_data_found THEN
      l_blockchain_row.bc_index         := 0;
      l_blockchain_row.bc_timestamp     := to_timestamp('01-01-1970 00:00:00',
                                                        'DD-MM-YYYY HH24:MI:SS');
      l_blockchain_row.bc_previous_hash := 0;
      l_blockchain_row.bc_hash          := 0;
  <span class="hljs-keyword">END</span>;
  <span class="hljs-comment">--</span>
  RETURN l_blockchain_row;
  <span class="hljs-comment">--</span>
<span class="hljs-keyword">END</span> get_block;
</code></pre>
<p><strong>Calculate a SHA256 hash of a block</strong></p>
<pre><code class="lang-sql">FUNCTION calculate_hash(p_bc_index       IN blockchain.bc_index%TYPE,
                        p_bc_timestamp   IN blockchain.bc_timestamp%TYPE,
                        p_bc_data        IN blockchain.bc_data%TYPE,
                        p_hash_algorithm IN PLS_INTEGER := dbms_crypto.hash_sh256)
  RETURN VARCHAR2 IS
  <span class="hljs-comment">--</span>
  l_prev_blockchain_row blockchain%ROWTYPE;
  l_prev_hash           VARCHAR2(500);
  l_hash                VARCHAR2(500);
  l_hash_src            CLOB;
  <span class="hljs-comment">--</span>
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-comment">--</span>
  l_prev_blockchain_row := blockchain_pkg.get_block(p_bc_index =&gt; blockchain_pkg.get_previous_block_index(p_current_index =&gt; p_bc_index));
  l_prev_hash           := l_prev_blockchain_row.bc_hash;
  <span class="hljs-comment">--</span>
  l_hash_src := p_bc_index ||
                to_char(p_bc_timestamp,
                        'DD.MM.RRRR HH24:MI:SSXFF TZH:TZM',
                        'nls_numeric_characters = ''. ''') || p_bc_data ||
                l_prev_hash;
  <span class="hljs-comment">--</span>
  l_hash := dbms_crypto.hash(l_hash_src,
                             p_hash_algorithm);
  <span class="hljs-comment">--</span>
  RETURN l_hash;
  <span class="hljs-comment">--</span>
<span class="hljs-keyword">END</span> calculate_hash;
</code></pre>
<p><strong>Add a new block to our blockchain</strong></p>
<pre><code class="lang-sql">FUNCTION add_block(p_bc_timestamp IN blockchain.bc_timestamp%TYPE := systimestamp,
                   p_bc_data      IN blockchain.bc_data%TYPE)
  RETURN blockchain.bc_index%TYPE IS
  <span class="hljs-comment">--</span>
  <span class="hljs-keyword">PRAGMA</span> AUTONOMOUS_TRANSACTION;
  <span class="hljs-comment">--</span>
  l_prev_blockchain_row blockchain%ROWTYPE;
  l_prev_hash           VARCHAR2(500);
  l_hash                VARCHAR2(500);
  l_bc_index            blockchain.bc_index%TYPE;
  <span class="hljs-comment">--</span>
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-comment">--</span>
  <span class="hljs-keyword">LOCK</span> <span class="hljs-keyword">TABLE</span> blockchain <span class="hljs-keyword">IN</span> EXCLUSIVE <span class="hljs-keyword">MODE</span>;
  <span class="hljs-comment">--</span>
  l_bc_index            := blockchain_seq.nextval;
  l_prev_blockchain_row := blockchain_pkg.get_latest_block;
  l_prev_hash           := l_prev_blockchain_row.bc_hash;
  l_hash                := blockchain_pkg.calculate_hash(p_bc_index     =&gt; l_bc_index,
                                                         p_bc_timestamp =&gt; p_bc_timestamp,
                                                         p_bc_data      =&gt; p_bc_data);
  <span class="hljs-comment">--</span>
  <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> blockchain
    (bc_index,
     bc_timestamp,
     bc_data,
     bc_previous_hash,
     bc_hash)
  <span class="hljs-keyword">VALUES</span>
    (l_bc_index,
     p_bc_timestamp,
     p_bc_data,
     l_prev_hash,
     l_hash);
  <span class="hljs-comment">--</span>
  <span class="hljs-keyword">COMMIT</span>;
  <span class="hljs-comment">--</span>
  RETURN l_bc_index;
  <span class="hljs-comment">--</span>
<span class="hljs-keyword">END</span> add_block;
</code></pre>
<p><strong>Check health of blockchain (valid or not)</strong></p>
<pre><code class="lang-sql">FUNCTION is_blockchain_valid RETURN BOOLEAN IS
  <span class="hljs-comment">--</span>
  l_current_block blockchain%ROWTYPE;
  l_prev_block    blockchain%ROWTYPE;
  l_current_hash  VARCHAR2(500);
  <span class="hljs-comment">--</span>
  CURSOR l_cur_blockchain IS
    <span class="hljs-keyword">SELECT</span> blockchain.bc_index
      <span class="hljs-keyword">FROM</span> blockchain
     <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> blockchain.bc_index;
  <span class="hljs-comment">--</span>
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-comment">--</span>
  <span class="hljs-keyword">FOR</span> l_rec_blockchain <span class="hljs-keyword">IN</span> l_cur_blockchain <span class="hljs-keyword">LOOP</span>
    l_current_block := blockchain_pkg.get_block(p_bc_index =&gt; l_rec_blockchain.bc_index);
    l_prev_block    := blockchain_pkg.get_block(p_bc_index =&gt; blockchain_pkg.get_previous_block_index(p_current_index =&gt; l_rec_blockchain.bc_index));
    l_current_hash  := blockchain_pkg.get_block_hash(p_blockchain_block =&gt; l_current_block);
    <span class="hljs-comment">--</span>
    IF l_current_block.bc_hash != l_current_hash THEN
      RETURN FALSE;
    <span class="hljs-keyword">END</span> <span class="hljs-keyword">IF</span>;
    <span class="hljs-comment">--</span>
    IF l_current_block.bc_previous_hash != l_prev_block.bc_hash THEN
      RETURN FALSE;
    <span class="hljs-keyword">END</span> <span class="hljs-keyword">IF</span>;
    <span class="hljs-comment">--    </span>
  <span class="hljs-keyword">END</span> <span class="hljs-keyword">LOOP</span>;
  <span class="hljs-comment">--</span>
  RETURN TRUE;
  <span class="hljs-comment">--</span>
<span class="hljs-keyword">END</span> is_blockchain_valid;
</code></pre>
<p>But nothings better than see it in action, here´s a APEX live demo: <a target="_blank" href="https://orclapex.io/ords/f?p=BC">https://orclapex.io/ords/f?p=BC</a> As mentioned above, an export of this demo is available on <a target="_blank" href="https://github.com/Dani3lSun/oracle-blockchain">GitHub</a>...</p>
<p><a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/10/blockchain_demo.gif"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/10/blockchain_demo.gif?w=1024" alt /></a></p>
<p>Hope this blog post is useful for some people, as I do think that this technology is very powerful and enables many use cases to save data in an secure and unmodifiable way, e.g transactional data or bookings etc... Happy coding! :)</p>
]]></content:encoded></item><item><title><![CDATA[APEX Badges for your GitHub projects]]></title><description><![CDATA[As you might already have seen in this tweet: https://twitter.com/Dani3lSun/status/900787959376556032 You are now able to simply add an APEX badge to your APEX GitHub projects. This is done by nothing more than adding some markdown to your Readme.md ...]]></description><link>https://blog.danielhochleitner.de/apex-badges-for-your-github-projects</link><guid isPermaLink="true">https://blog.danielhochleitner.de/apex-badges-for-your-github-projects</guid><dc:creator><![CDATA[Daniel Hochleitner]]></dc:creator><pubDate>Thu, 24 Aug 2017 22:11:46 GMT</pubDate><content:encoded><![CDATA[<p>As you might already have seen in this tweet: https://twitter.com/Dani3lSun/status/900787959376556032 You are now able to simply add an APEX badge to your APEX GitHub projects. This is done by nothing more than adding some markdown to your Readme.md file or any other markdown file in your repository. Example markdown: [code language="text"][![APEX Community](https://cdn.rawgit.com/Dani3lSun/apex-github-badges/78c5adbe/badges/apex-community-badge.svg)](https://github.com/Dani3lSun/apex-github-badges)[/code] You will find more badges and examples on the <a target="_blank" href="https://github.com/Dani3lSun/apex-github-badges">GitHub project page</a>. Contributions and ideas, maybe new badges are highly welcome! Let´s show the open source world how great the APEX community is! :)</p>
]]></content:encoded></item><item><title><![CDATA[Awesome Oracle APEX List on GitHub]]></title><description><![CDATA[Maybe you have already seen my recent tweet about it: https://twitter.com/Dani3lSun/status/899970914690977794 So I´m calling now for community power to help extending that list with great articles, blogs, tips & resources. Goal should be to make this...]]></description><link>https://blog.danielhochleitner.de/awesome-oracle-apex-list-on-github</link><guid isPermaLink="true">https://blog.danielhochleitner.de/awesome-oracle-apex-list-on-github</guid><dc:creator><![CDATA[Daniel Hochleitner]]></dc:creator><pubDate>Tue, 22 Aug 2017 22:11:05 GMT</pubDate><content:encoded><![CDATA[<p>Maybe you have already seen my recent tweet about it: https://twitter.com/Dani3lSun/status/899970914690977794 So I´m calling now for community power to help extending that list with great articles, blogs, tips &amp; resources. Goal should be to make this awesome list even more awesome and a helpful guide / reference for everyone using Oracle APEX! <strong>Here are some steps to contribute to this <a target="_blank" href="https://github.com/Dani3lSun/awesome-orclapex">GitHub project</a>:</strong></p>
<ul>
<li><p>Create a GitHub Account <a target="_blank" href="https://github.com/join?source=header-home">here</a></p>
</li>
<li><p>Fork the main GitHub repository. After that, you will find the forked repository under your GitHub user ( #github-user#/awesome-orclapex )</p>
</li>
</ul>
<p><a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/github_fork.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/github_fork.png?w=300" alt /></a></p>
<ul>
<li>Edit the README.md file and add a APEX blog, link, article or resource. Here´s a <a target="_blank" href="https://github.com/Dani3lSun/awesome-orclapex/blob/master/CONTRIBUTING.md">contribution guide</a>. You can do it either in the GitHub web interface or via git command line tools</li>
</ul>
<p><a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/github_edit.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/github_edit.png?w=300" alt /></a></p>
<ul>
<li>Create a Pull Request to pull your changes to the main repository. I will review the changes and merge it into the main Readme file.</li>
</ul>
<p><a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/github_pull_01.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/github_pull_01.png?w=300" alt /></a><a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/github_pull_02.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/github_pull_02.png?w=300" alt /></a> That´s it! Thanks for your contributions, together we can make this even more awesome! :) Ps: Maybe you´re interested in the complete GitHub awesome stuff, then you will find many awesome projects <a target="_blank" href="https://github.com/sindresorhus/awesome">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Deploy APEX Docker Image on Oracle Container Cloud Service]]></title><description><![CDATA[As I really like Docker and use it pretty much in my daily life as a developer, it became a inherent part in my workflow. I use it for my development instances or for having test servers pretty fast available. Even sharing the images with other devs ...]]></description><link>https://blog.danielhochleitner.de/deploy-apex-docker-image-on-oracle-container-cloud-service</link><guid isPermaLink="true">https://blog.danielhochleitner.de/deploy-apex-docker-image-on-oracle-container-cloud-service</guid><dc:creator><![CDATA[Daniel Hochleitner]]></dc:creator><pubDate>Wed, 16 Aug 2017 22:10:26 GMT</pubDate><content:encoded><![CDATA[<p>As I really like Docker and use it pretty much in my daily life as a developer, it became a inherent part in my workflow. I use it for my development instances or for having test servers pretty fast available. Even sharing the images with other devs or have some demo environment within minutes, Docker sets the bar pretty high. Usually I use my own made Docker Image which contains the following software:</p>
<ul>
<li>Oracle Linux 7.3</li>
<li>Oracle Database 12.2.0.1 Enterprise Edition with non-CDB architecture</li>
<li>Oracle APEX 5.1.2</li>
<li>Oracle ORDS 3.0.11</li>
<li>Oracle SQLcl 17.2.0.184.1230</li>
<li>Apache Tomcat 8.0.45</li>
<li>Java JDK 8u144</li>
<li>OraOpenSource Logger 3.1.1</li>
<li>OraOpenSource OOS Utils 1.0.0</li>
<li>APEX Office Print 3.x (Cloud Package)</li>
</ul>
<p>The build scripts are available on GitHub and open source: <a target="_blank" href="https://github.com/Dani3lSun/docker-db-apex-dev">Oracle Database &amp; APEX Developer Docker Image</a> With this software shipped in one Container I have everything in place to get my work done. (Of course I know that this is usually not the way Docker works, normally you have only one software component in one Docker container. But for me it works quite well, getting all the software I need without having the overhead of a real VM) <strong>Oracle Cloud Services</strong> So in the past I only used Docker on my local machines or servers but I was interested if my image works in the Oracle Cloud too. So I decided to give it a try, and lets have a look what Oracle have in petto. <em>(Anticipation: After getting used to the Oracle Cloud UI, which took only an hour or two, I really liked how the Database Cloud Service and Container Cloud Service worked)</em> First step was to get into the Oracle Cloud Services, and for that purpose Oracle offers a free try period, which enables you to test it either for 30 days or until you consumed services worth $300. Here you get started: <a target="_blank" href="https://cloud.oracle.com/en_US/tryit">https://cloud.oracle.com/en_US/tryit</a> After my registration was successful, Oracle had my cloud account ready within 4 hours. (Which is OK, but of course it could be a bit faster if you have a look at other big cloud companies...). In the welcome mail, I got all the information I needed to get into the Oracle Cloud. Thus we are only interested in the Container Cloud Service in this blog post, I will not write about the other possibilities within the Oracle Cloud. Having that said, I really liked the Database Service, too :) Main focus is to get our own built Docker image running in the Oracle Container Cloud Service so we can use it and can connect to the services like APEX, Database or SSH. <strong>Create an Oracle Container Cloud Service</strong> This was really easy to accomplish, after login I navigated to the Container Service Section and created a new Service. The following screen shots will show the different steps and the create wizard. <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_01.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_01.png?w=300" alt /></a> <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_02.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_02.png?w=300" alt /></a> <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_03.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_03.png?w=300" alt /></a> <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_04.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_04.png?w=300" alt /></a> <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_05.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_05.png?w=300" alt /></a><a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_06.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_06.png?w=300" alt /></a> About 30 minutes later I received an mail from Oracle, that my newly created Container Service is now up and running. So I navigated to the detail page of my Container Service to get some information about it. The most important information is the Public IP address, this is the IP we use to connect to the admin panel of the Container Service: [code language="text" autolinks="false"]https:// e.g https://123.123.123.123[/code] <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_07.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_07.png?w=300" alt /></a> <strong>Oracle Container Cloud Service</strong> Having this information we can login to our Oracle Container Cloud Service entering https + Public IP into our browser. After we have successfully authenticated we will land on a dashboard page of the service. <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_08.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_container_08.png?w=300" alt /></a> For now we are finished within the Oracle Cloud web interface, we successfully got an cloud account, created a Container Service and logged into that service. This procedure was indeed straight forward and not that complicated. It took me about 5-10 minutes to get this done. Really nice! Next is preparing our Docker image so we can use it in the Oracle Container Cloud Service. <strong>Build Docker Image</strong> I am not going into much detail here, because the whole procedure creating a Docker image is described on <a target="_blank" href="https://github.com/Dani3lSun/docker-db-apex-dev">GitHub</a>. Just to see the steps:</p>
<ul>
<li>Clone or download the GitHub repository to your local machine [code language="bash"]git clone https://github.com/Dani3lSun/docker-db-apex-dev.git[/code]</li>
<li>Download missing software components (like Oracle DB or Oracle APEX ...)</li>
<li>Build the Docker image [code language="bash"]docker build -t db-apex-dev-image .[/code]</li>
</ul>
<p><a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/docker_build.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/docker_build.png?w=300" alt /></a> <strong>Upload Docker image to Docker Hub</strong> Thus it is needed that the image is uploaded to some location our Oracle Container Cloud Service has access to, I used <a target="_blank" href="https://hub.docker.com/">Docker Hub</a> to get this done. But of course you can use every other or maybe your own Docker registry server. Important is that the Oracle Cloud server can access your registry server. <em>(What is a Docker registry server? Basically it is a server which stores and distributes Docker images, more details <a target="_blank" href="https://docs.docker.com/registry/">here</a>)</em> The benefit of using Docker Hub is that we only have to create an account there and the Oracle Container Cloud Service has per default access to that registry server, which means we don´t have to configure anything. After creating an account on Docker Hub we have to login on our client using the Docker command line tools [code language="bash"]docker login[/code] Entering username and password of the Docker Hub account and we are done. Next, we upload our Docker image to Docker Hub using again the command line tools: [code language="bash"]docker tag db-apex-dev-image /db-apex-dev-image docker push /db-apex-dev-image[/code] <em>(More details about Docker push can be found <a target="_blank" href="https://docs.docker.com/docker-cloud/builds/push-images/">here</a>)</em> Once the upload process finished, we can go back to our Oracle Container Cloud Service admin panel. (This process can take some time, dependent on your networking bandwidth. The image has about 15-16GB) <em>Note: Make sure that this image is publicly available, this can be configured in the Docker Hub web interface. If you want to have it private than you have to configure your Docker credentials in the Oracle Container Cloud Service admin panel.</em> <strong>Create a new Service</strong> Within the Oracle Container Cloud Service admin panel we can now create a new service, which basically is a <a target="_blank" href="https://docs.docker.com/engine/reference/run/">Docker run</a> command. So we define how our Docker image would be configured to run. (e.g port mappings, temp file size, ...) The most simple and fastest way is to use the <em>Docker Run Command</em> editor and paste in a docker run config like this: [code language="bash"]docker run \ --name=db-apex-dev-container \ -p=2222:22/tcp \ -p=8080:8080/tcp \ -p=1521:1521/tcp \ -v=/dev/shm \ -e="occs:description=Oracle Database and APEX Developer Docker Service" \ "/db-apex-dev-image" \ --tmpfs /dev/shm:rw,nosuid,nodev,exec,size=2g[/code] This defines:</p>
<ul>
<li>HTTP port mapping: 8080 tcp</li>
<li>DB port mapping: 1521 tcp</li>
<li>SSH port mapping: 2222 tcp</li>
<li>Volume: /dev/shm</li>
<li>Volume used as tmpfs with the size of 2GB</li>
</ul>
<p>After we have done that, all the necessary input fields of the create window are automatically filled. Only the service name input have to be set by hand. Here are some screen shots which describe the procedure. <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_01.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_01.png?w=300" alt /></a> <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_02.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_02.png?w=300" alt /></a> <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_03.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_03.png?w=300" alt /></a> <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_04.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_04.png?w=300" alt /></a> Finally we have our Container Service defined and now we will see it up and running. The only thing we have to do now is to press the <em>green Deploy button</em>. Now Oracle fetches our Docker image from Docker Hub and runs it with our configured run parameters. This step takes a few minutes to complete. <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_06.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_06.png?w=300" alt /></a> <strong>Connect to Services</strong> To get the public address of our running services we have to go to the hosts detail page, this IP address is used for all containers/services running in this particular Container Cloud Service. This means all network ports which are defined, can only be applied once. <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_05.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/oc_service_05.png?w=300" alt /></a> Lets say our Public IP address is <em>123.123.123.124</em> then the services would be accessible like: APEX:</p>
<p>http://123.123.123.124:8080/ords</p>
<p>DB:</p>
<p>sqlplus system/oracle@123.123.123.124/db12c</p>
<p>SSH:</p>
<p>ssh root@123.123.123.124 -p 2222</p>
<p><em>Note: These are the default port mappings as we defined in the docker run section above. The users and passwords are defined before building the docker image, read more about it on <a target="_blank" href="https://github.com/Dani3lSun/docker-db-apex-dev">GitHub</a>. For security reasons the DB port should not be available publicly (maybe over SSH tunneling) and Tomcat should run over SSL</em> <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/docker_apex.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/docker_apex.png?w=300" alt /></a> <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/docker_sql.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/docker_sql.png?w=300" alt /></a> <a target="_blank" href="https://blogdanielhochleitner.files.wordpress.com/2017/08/docker_ssh.png"><img src="https://blogdanielhochleitner.files.wordpress.com/2017/08/docker_ssh.png?w=300" alt /></a> That´s it! :) We are done and have a up and running Docker image in the Oracle Cloud packed with all the software components we like...</p>
]]></content:encoded></item></channel></rss>