Wednesday, June 1, 2016

Debugging 101: A case study

Recently I've spent most of my free time participating the in Oracle APEX Discussion Forum on OTN.  It's a way to give back to the Oracle community, but also to learn new things and hone my skills.  So if you ever get a reply from Pmon, that's me :)

Today I was trying to help a user that wanted to implement a slider to set a numeric value in a field.  They were using javascript samples from a couple of blog posts and had cobbled together a sample that wasn't working.  After a quick search I found that there was already a plugin someone had created.  Slider was an APEX plugin made for a minimum APEX version of 4.x.  I posted the link to the plugin as the solution and thought I had come up with another easy win.  A few hours later, the user updated the thread to let me know that the plugin wasn't working and the control wasn't displaying on the screen.  Truth be told, I had never worked with APEX plugins in my life.  I took a quick look at the link I sent and compared it with the user's example and could see that there were noticeable differences.  I updated the thread with my observations and told the user if all else fails, contact the developer.

One thing that I've found is that while trying to help people in the forums I become unintentionally emotionally invested in each issue that I'm helping people with.  I feel like I have a responsibility to carry them forth toward a solution even when my initial effort fails.  This is exactly how I felt in this situation.  What gnawed at me was that the developer of the plugin had a working example deployed to apex.oracle.com which meant that it could work in version 5.

I decided to try to resolve the issue knowing that it would be a good opportunity for me to learn about plugins and how they worked.  One thing that I was confident in was my debugging skills so I decided to share in this post a step by step case study of how I resolved this issue.

Triage:

The first step in debugging any issue is trying to figure out how severe the problem really is and what is going on.  Looking at the user's sample:


Anyone could plainly see that the text box was displaying, but the slider control was missing.  This is usually the indication of some type of error.  It could be a missing image file or some type of rendering issue.  To see if there were any script errors occurring I decided to use f12 tools:

If you haven't used it before, just hit f12 on your browser and prepare to be amazed.  Each browser has a different implementation, but most of them have about the same features that are essential to debugging errors.  In the console window there was a javascript error that occurred during the rendering of the page.

Interpreting your errors

If I have one essential piece of advice for anyone learning how to code and debug any language it is this....learn how to understand error messages!  Error messages can be cryptic, but they really are your best source for information to what's going on.  Unfortunately there really isn't a shortcut in learning how to interpret error messages.  The best way to learn what error messages are trying to say to you is to see a lot of them.  Luckily we make a lot of mistakes.  So code a lot, make a lot of mistakes, and learn.

With this particular message, it's complaining about e.widget.extend not being a function.  In my experience, if this works in an earlier version and doesn't work in a newer version, it usually means that either the method has been deprecated and no longer exists, or it's implementation has changed and needs to be used in another way.

Search efficiently

To figure this out I started my search like I always do.  Try to come up with a well formed specific google search.  Google searching is also an essential skill in debugging because let's face it, there's a world of information out there if we only knew how to find it.  I usually start with a search that is somewhat general and has enough details to get me in the ballpark of where I want to go.  I use it as a feeler to find specific search terms that will lead me directly to the answers I want.

This is what I came up with...

oracle apex e.widget.extend is not a function

The first hit that I navigated to was a blog by Tobias Arnhold:


He mentioned the exact error that was occurring and referenced that it was occurring in the Early Adopter's version of APEX 5.  It wasn't quite what I was looking for because there was no resolution, but it gave me enough information to refine my search.  Here was my next attempt:

oracle apex 5 e.widget.extend is not a function

It was a subtle change, but just adding 5 after apex for the search made all the difference.  The next link I checked was another blog entry from Mr. Arnhold:



Part II of his original post and this time he provided a resolution.  The issue was arising because of the reference to an older jquery library.  The resolution?  Remove it.


After reviewing the plugin code I found that the javascript libraries were being added via the PL/SQL of the plugin.  I commented out the section the referred to the offending library jquery.ui.1.8.  This would take care of the javascript error, but there were still missing html elements.

Compare with a working example

This is another essential skill in debugging.  When things don't work for you, strip your code down to the bare essentials and compare it to a working example of what you want to achieve.  In this case, I had the demo application that worked like a champ https://apex.oracle.com/pls/apex/f?p=84616:99903.  I compared it with the example provided by the user and found a couple of differences:



It might be hard to see in the images, but I immediately noticed that the div tag for the slider control was missing a class attribute.  It was also missing an html anchor as a child to the div tag.  To test the changes that I was going to make to the page I used the f12 tools to directly add the class attribute to the div tag and add the anchor as a child element to the div.  After doing these two things, I could immediately see that I would get the desired results.

To resolve this issue, I edited the plugin source and changed the original:

to..

by taking the html directly out from the page and adding it to the PL/SQL code.  Feeling pretty good about myself I decided to run the application again.  This time the slider control displayed, but I was still getting a javascript error:


A different error! Progress!  With my new error I just followed the same debugging process that I used with the first error:

  • Triage
  • Interpret your error(s)
  • Search Efficiently
  • Compare with a working example

This error stumped me a bit.  I checked all the javascript source of the working example and it was the same as the broken sample.  I knew that this error was essentially same as the first, so for some reason the method .slider was missing in our sample, but not in the demo application.  I determined that this was not an APEX specific error so I did a search:

javascript .slider is not a function

The first result that came up was a stackoverflow thread that had this answer:


Once I saw this answer in the thread I realized what had happened.  The javascript library that I had removed was just out-dated, but it was still essential.  So, I used the script reference provided to download a new version of the library.  I saved it to a file and uploaded it to the plugin:


then I added a reference to the file in the PL/SQL of the plugin:



When I ran the page again, the slider worked and there were no error messages:

Success!  As with any success, there were many failures along the way, but I did learn a lot during the process.

I hope that this example of how to debug an issue helps someone out there and remember to use these debugging steps/skills:
  • Triage
  • Interpret your error(s)
  • Search Efficiently
  • Compare with a working example
Happy debugging!


Update:

One thing that's cool about the internet is that it shrinks the world and it brings us closer to each other.  Because of this you never know who you might interact with.  Today, I got advice from Patrick Wolf, member of the APEX development team.  How cool is that?  Here's what he said:

"instead of loading the full jQueryUI library again (APEX already loads a number of jQueryUI components out of the box), I would suggest to use

apex_javascript.add_3rd_party_library_file (
p_library => apex_javascript.c_library_jquery_ui,
p_file_name => 'jquery.ui.slider' )
in the plug-in. This has also the advantage that the plug-in automatically picks up the most recent jQueryUI version which ships with APEX, otherwise you might run into the same issues with the next version of APEX when we update jQueryUI to a newer release."

Two things of note in his comment.  Firstly, always try to keep your source code to a minimum.   It helps with page loading and performance.  Secondly, when you use external libraries and make customizations it's very common to have issues when patches and upgrades are released.  Be prepared to support them for the life of the application.  

2 comments:

  1. Hi Paul,

    instead of loading the full jQueryUI library again (APEX already loads a number of jQueryUI components out of the box), I would suggest to use

    apex_javascript.add_3rd_party_library_file (
    p_library => apex_javascript.c_library_jquery_ui,
    p_file_name => 'jquery.ui.slider' )

    in the plug-in. This has also the advantage that the plug-in automatically picks up the most recent jQueryUI version which ships with APEX, otherwise you might run into the same issues with the next version of APEX when we update jQueryUI to a newer release.

    Regards
    Patrick

    ReplyDelete
    Replies
    1. Thanks for the tip Patrick. I've still got a lot to learn about jQuery, but I'm enjoying the journey.

      Delete