Jaimon's Blog

Check digit calculator for barcodes and credit cards

I couldn’t find any online page where you can calculate the check digit for a barcode with an arbitrary length of digits. There are pages where you can do this for barcodes with specific length, like EAN13 or GS1 barcodes etc.

Since it was easy to create one from scratch, I thought I’ll do one and post it.

You can find it here in action

HTH

Jaimon

April 17, 2013 Posted by | Uncategorized | , | Leave a comment

An Excel Addin to work with Unicode CSV files

Excel treat .csv files as text files and will replace all Unicode characters with “?” when saved normally. If you want to preserve the Unicode characters, you would need to save the file as “Unicode text (*.txt)”, which is a Tab delimited file. Since I couldn’t find any existing solution to preserve Unicode characters in CSV format, I thought I’ll give it a go in creating an Excel Addin to do just that.

Installing the Addin

If you want to try this on your Excel 2007 or 2010, please download http://blog.jaimon.co.uk/UnicodeCSV/setupfiles.zip and extract the files to a folder before running setup.exe.


****Update****

Some of the readers have experienced issues with this installation file on some machines. For a successful installation,

  • Please make sure to install this as Local admin user
  • If setup.exe doesn’t work for some reason try running the UnicodeCSVAddin.vsto file
(Many thanks to our reader Sean for pointing this out)

Once the Addin is installed, it will look out for any CSV file being opened. If you’re opening an existing Unicode CSV file, this Addin will override the current saving method (either by pressing Ctrl-S, or clicking on any of the Save buttons/options from the Ribbon or menu) and will always save as a Unicode file.

But if you are creating a new file and want to save that as a Unicode csv file, then choose the Save or SaveAs option from the newly created Ribbon item “Unicode CSV”.

When clicking on Save from “Unicode CSV” Ribbon, it will make sure the file extension is .csv, if not it will prompt you to select another file name. Once a file is saved this way, then you don’t need to use the “Unicode CSV” Ribbon item any more, just save as you do normally.

How does it work?

As Excel can handle Unicode text (*.txt) files perfectly well, I’m relying on that to make this Addin work. On save, this Addin will be calling Excel’s SaveAs method to create a temporary Unicode Text file. It will then overwrite the original file by replacing all Tabs with comma and reload it. As it is a CSV file, Excel doesn’t preserve any column width adjustments or last active cell. Currently this Addin will reset the last active cell, and I think it should be pretty straightforward to reset column widths as well, if you wish to do so.

The code behind (for the geeks!)

This was done as an Excel 2007 application level Addin project in Visual Studio 2010 targeting .Net framework 4.0, so it can be installed on both 2007 & 2010 versions of MS Office. Feel free to fork the project from https://github.com/jaimonmathew/Unicode-CSV-Addin as a Visual Studio 2010 solution, if you want to make any modification or create an MSI installer etc. (Please read the license and notice files before using the code)

–ThisAddin.cs

using System;
using Excel = Microsoft.Office.Interop.Excel;
using System.Windows.Forms;
using Office = Microsoft.Office.Core;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections.Generic;
using System.IO;

namespace UnicodeCSVAddin
{
    public partial class ThisAddIn
    {
        private Excel.Application app;
        private List<string> unicodeFiles; //a list of opened Unicode CSV files. We populate this list on WorkBookOpen event to avoid checking for CSV files on every Save event.
        private bool sFlag = false;

        //Unicode file byte order marks.
        private const string UTF_16BE_BOM = "FEFF";
        private const string UTF_16LE_BOM = "FFFE";
        private const string UTF_8_BOM = "EFBBBF";

        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            app = this.Application;
            unicodeFiles = new List<string>();
            app.WorkbookOpen += new Excel.AppEvents_WorkbookOpenEventHandler(app_WorkbookOpen);
            app.WorkbookBeforeClose += new Excel.AppEvents_WorkbookBeforeCloseEventHandler(app_WorkbookBeforeClose);
            app.WorkbookBeforeSave += new Excel.AppEvents_WorkbookBeforeSaveEventHandler(app_WorkbookBeforeSave);
        }

        void ThisAddIn_Shutdown(object sender, EventArgs e)
        {
            app = null;
            unicodeFiles = null;
        }

        void app_WorkbookBeforeSave(Excel.Workbook Wb, bool SaveAsUI, ref bool Cancel)
        {
            //Override Save behaviour for Unicode CSV files.
            if (!SaveAsUI && !sFlag && unicodeFiles.Contains(Wb.FullName))
            {
                Cancel = true;
                SaveAsUnicodeCSV(false, false);
            }
            sFlag = false;
        }

        //This is required to show our custom Ribbon
        protected override Office.IRibbonExtensibility CreateRibbonExtensibilityObject()
        {
            return new Ribbon1();
        }

        void app_WorkbookBeforeClose(Excel.Workbook Wb, ref bool Cancel)
        {
            unicodeFiles.Remove(Wb.FullName);
            app.StatusBar = "Ready";
        }

        void app_WorkbookOpen(Excel.Workbook Wb)
        {
            //Check to see if the opened document is a Unicode CSV files, so we can override Excel's Save method
            if (Wb.FullName.ToLower().EndsWith(".csv") && isFileUnicode(Wb.FullName))
            {
                if (!unicodeFiles.Contains(Wb.FullName))
                {
                    unicodeFiles.Add(Wb.FullName);
                }
                app.StatusBar = Wb.Name + " has been opened as a Unicode CSV file";
            }
            else
            {
                app.StatusBar = "Ready";
            }
        }

        /// <summary>
        /// This method check whether Excel is in Cell Editing mode or not
        /// There are few ways to check this (eg. check to see if a standard menu item is disabled etc.)
        /// I know in cell editing mode app.DisplayAlerts throws an Exception, so here I'm relying on that behaviour
        /// </summary>
        /// <returns>true if Excel is in cell editing mode</returns>
        private bool isInCellEditingMode()
        {
            bool flag = false;
            try
            {
                app.DisplayAlerts = false; //This will throw an Exception if Excel is in Cell Editing Mode
            }
            catch (Exception)
            {
                flag = true;
            }
            return flag;
        }
        /// <summary>
        /// This will create a temporary file in Unicode text (*.txt) format, overwrite the current loaded file by replaing all tabs with a comma and reload the file.
        /// </summary>
        /// <param name="force">To force save the current file as a Unicode CSV.
        /// When called from the Ribbon items Save/SaveAs, <i>force</i> will be true
        /// If this parameter is true and the file name extention is not .csv, then a SaveAs dialog will be displayed to choose a .csv file</param>
        /// <param name="newFile">To show a SaveAs dialog box to select a new file name
        /// This will be set to true when called from the Ribbon item SaveAs</param>
        public void SaveAsUnicodeCSV(bool force, bool newFile)
        {
            app.StatusBar = "";
            bool currDispAlert = app.DisplayAlerts;
            bool flag = true;
            int i;
            string filename = app.ActiveWorkbook.FullName;

            if (force) //then make sure a csv file name is selected.
            {
                if (newFile || !filename.ToLower().EndsWith(".csv"))
                {
                    Office.FileDialog d = app.get_FileDialog(Office.MsoFileDialogType.msoFileDialogSaveAs);
                    i = app.ActiveWorkbook.Name.LastIndexOf(".");
                    if (i >= 0)
                    {
                        d.InitialFileName = app.ActiveWorkbook.Name.Substring(0, i);
                    }
                    else
                    {
                        d.InitialFileName = app.ActiveWorkbook.Name;
                    }
                    d.AllowMultiSelect = false;
                    Office.FileDialogFilters f = d.Filters;
                    for (i = 1; i <= f.Count; i++)
                    {
                        if ("*.csv".Equals(f.Item(i).Extensions))
                        {
                            d.FilterIndex = i;
                            break;
                        }
                    }
                    if (d.Show() == 0) //User cancelled the dialog
                    {
                        flag = false;
                    }
                    else
                    {
                        filename = d.SelectedItems.Item(1);
                    }
                }
                if (flag && !filename.ToLower().EndsWith(".csv"))
                {
                    MessageBox.Show("Please select a CSV file name first");
                    flag = false;
                }
            }

            if (flag && filename.ToLower().EndsWith(".csv") && (force || unicodeFiles.Contains(filename)))
            {
                if (isInCellEditingMode())
                {
                    MessageBox.Show("Please finish editing before saving");
                }
                else
                {
                    try
                    {
                        //Getting current selection to restore the current cell selection
                        Excel.Range rng = (Excel.Range)app.ActiveCell;
                        int row = rng.Row;
                        int col = rng.Column;

                        string tempFile = System.IO.Path.GetTempFileName();

                        try
                        {
                            sFlag = true; //This is to prevent this method getting called again from app_WorkbookBeforeSave event caused by the next SaveAs call
                            app.ActiveWorkbook.SaveAs(tempFile, Excel.XlFileFormat.xlUnicodeText);
                            app.ActiveWorkbook.Close();

                            if (new FileInfo(tempFile).Length <= (1024 * 1024)) //If its less than 1MB, load the whole data to memory for character replacement
                            {
                                File.WriteAllText(filename, File.ReadAllText(tempFile, UnicodeEncoding.UTF8).Replace("\t", ","), UnicodeEncoding.UTF8);
                            }
                            else //otherwise read chunks for data (in 10KB chunks) into memory
                            {
                                using (StreamReader sr = new StreamReader(tempFile, true))
                                using (StreamWriter sw = new StreamWriter(filename, false, sr.CurrentEncoding))
                                {
                                    char[] buffer = new char[10 * 1024]; //10KB Chunks
                                    while (!sr.EndOfStream)
                                    {
                                        int cnt = sr.ReadBlock(buffer, 0, buffer.Length);
                                        for (i = 0; i < cnt; i++)
                                        {
                                            if (buffer[i] == '\t')
                                            {
                                                buffer[i] = ',';
                                            }
                                        }
                                        sw.Write(buffer, 0, cnt);
                                    }
                                }
                            }
                        }
                        finally
                        {
                            File.Delete(tempFile);
                        }

                        app.Workbooks.Open(filename, Type.Missing, Type.Missing, Excel.XlFileFormat.xlCSV, Type.Missing, Type.Missing, Type.Missing, Type.Missing, ",");
                        Excel.Worksheet ws = app.ActiveWorkbook.ActiveSheet;
                        ws.Cells[row, col].Select();
                        app.StatusBar = "File has been saved as a Unicode CSV";
                        if (!unicodeFiles.Contains(filename))
                        {
                            unicodeFiles.Add(filename);
                        }
                        app.ActiveWorkbook.Saved = true;
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show("Error occured while trying to save this file as Unicode CSV: " + e.Message);
                    }
                    finally
                    {
                        app.DisplayAlerts = currDispAlert;
                    }
                }
            }
        }

        /// <summary>
        /// This method will try and read the first few bytes to see if it contains a Unicode BOM
        /// </summary>
        /// <param name="filename">File to check for including full path</param>
        /// <returns>true if its a Unicode file</returns>
        private bool isFileUnicode(string filename)
        {
            bool ret = false;
            try
            {
                byte[] buff = new byte[3];
                using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    fs.Read(buff, 0, 3);
                }

                string hx = "";
                foreach (byte letter in buff)
                {
                    hx += string.Format("{0:X2}", Convert.ToInt32(letter));
                    //Checking to see the first bytes matches with any of the defined Unicode BOM
                    //We only check for UTF8 and UTF16 here.
                    ret = UTF_16BE_BOM.Equals(hx) || UTF_16LE_BOM.Equals(hx) || UTF_8_BOM.Equals(hx);
                    if (ret)
                    {
                        break;
                    }
                }
            }
            catch (IOException)
            {
                //ignore any exception
            }
            return ret;
        }

        #region VSTO generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }

        #endregion
    }
}

The crux of the application is in ThisAddin.cs file, and I’ve put some comments wherever I can in the code to explain the functionality. But here is a brief info on how the code works. (As it was my first attempt at using VSTO (Visual Studio Tools for Office) and the fact that I’m a Java/Oracle developer for my day job, I’m sure the .net experts here might find a better way of coding in many places).

On the ThisAddin_Startup method I’m adding three event handlers,

  1. Application_WorkbookOpen – to check for whether the new file being opened is a Unicode CSV file. The code looks at the BOM (Byte Order Mark) bytes to figure out whether it’s a Unicode file or not. At the moment it will check for UTF8, UTF16-LE and UTF16-BE preambles. If it’s a Unicode CSV file, then it will add the file name to a global list
  2. Application_WorkbookBeforeClose – to remove file name from the global Unicode CSV file list
  3. Application_WorkbookBeforeSave – This will call the method SaveAsUnicodeCSV to override Excel’s default Save behaviour if the filename is in the global Unicode CSV file list. Once some basic checks are passed, SaveAsUnicodeCSV method will save the current file as a Unicode Text file to a temporary file, and close it. We now need to replace all the Tabs in that file with a comma. If the file is small enough (<= 1MB), I’m using File.ReadAllText and File.WriteAllText to read and write in one go. Otherwise I’m using a StreamReader and StreamWriter to read/write data in 10KB chunks. (BTW, these are numbers I plucked out of thin air, so feel free to change this to suite your use case). Once the new file has been created, we will then open the new csv into current workbook.

Ribbon1.xml has the entries to create new Ribbon items.

Jaimon

August 23, 2011 Posted by | Uncategorized | , , , , , | 106 Comments

SimpleImageInfo – A Java class to get image size without loading the whole data


SimpleImageInfo is a Java class to find out an image’s size and mime type without reading the whole image data. It doesn’t have any dependency on AWT or any other libraries. Currently it supports the following image types

  • JPG
  • GIF
  • PNG
  • BMP
  • TIFF

Because it only read the first few bytes, in most cases it only takes less than a millisecond to return the values.

Usage

Getting image data from a byte array

 

SimpleImageInfo imageInfo = new SimpleImageInfo(bytes);
System.out.println("MIME type : " + imageInfo.getMimeType() + " width : " + imageInfo.getWidth() + " height : " + imageInfo.getHeight());

Getting image data from a file

 

SimpleImageInfo imageInfo = new SimpleImageInfo(file);
System.out.println(imageInfo);


Download the source code

Please click here to download the source code.

HTH

Jaimon

January 29, 2011 Posted by | Uncategorized | , , | 21 Comments

Making Scrollable Tables with fixed headers – Updated

This post is an update to my previous post on making scrollable tables with fixed headers. I’ve had a few requests for adding additional functionalities, and I thought instead of cluttering the old post, I’ll do a new one for the updated script.

What’s new?

  • Multiple tables on a single page
  • Multiple header freezing
  • Column freezing
To see it in action, please click here.

Implementation

  • Download the file fxHeader_0.6.min.js and copy to your folder (An uncompressed fxHeader_0.6.js is also available)
  • Include the above file in your HTML page
      <script src="fxHeader_0.6.min.js" type="text/javascript"></script>
  • Just before body end tag (</body>), add these lines,
<script type=”text/javascript”>
fxheaderInit(‘dataTable1’,200,2,2);
fxheaderInit(‘dataTable2’,200,2,0);
fxheader();
</script>
Call fxheaderInit for every table you want to add scrolling. fxheader() is only required to call once.
Attributes for fxheaderInit function
  • Table id
  • Height (in pixels)
  • No. of rows to freeze
  • No. of columns to freeze

Width will be taken from the table’s width attribute, so make sure the table you’re passing in has got a width attribute. If width is specified in percentage, it will be calculated in regards to width of the document body, and not of the containing element (ie parentNode). Since width will be changed on window resize, fxheader() function will be attached to window.onresize, if any of the table’s width is specified in percentage. If your existing code has reliance on window.onresize event, please amend the fxHeader.js file.

If your table is generated by a framework like JSF or ASP.net, you’ll need to avoid any possible ID collision as discussed on my previous post.

Cheers,

Jaimon

March 19, 2010 Posted by | Uncategorized | , , , , , | 148 Comments

Migrating 10g JSF Web project with ADF Faces components in jspx pages to 11g Facelets pages using Rich components

Introduction

As of this writing (Feb 2010), there are no tools available to convert your 10g project into an 11g project using Rich components, let alone something that can convert my jspx pages into Facelets. JDeveloper 11g FAQ confirms that, you need to do manual migration if you want to use Rich components. As Oracle points out, you’ll need to do some code refactoring to make use of new functionalities available with JSF 1.2 and Rich components (and Facelets).

This post discusses a few points encountered during my by hand migration process I’ve done with a couple of 10g projects. If you are migrating to use Trinidad components instead of Rich components, then this paper would be more useful than my post.

At the end of this post, you can download a sample 11g Rich Component project with Facelets pages to get you going.

Why Facelets?

There are a number of good articles on this subject, so there is no point repeating any of that here. (See reference section for some useful links). For me, the main reasons for the conversion was the fact that JSP is deprecated from JSF 2.0, and Facelets is the new standard view technology, so at some stage, you’ll need to convert to Facelets. And also the Facelets pages render a bit faster.

Some other benefits like templating may not sound great to an ADF user as Oracle already had templating support from JDeveloper 10g.

Facelets support in JDeveloper

Although the usage of Facelets was possible with 10g as discussed here, there was little IDE support like code completion available to you. Although the support improved in 11g, it wasn’t perfect as John Stegeman explains here. But it is a lot better now with Patch 1 release on November 2009 (Version 11.1.1.2.0 onwards). So please update your JDeveloper 11g if you plan to use Facelets at all.

Skinning

In 11g, you can extend an existing skin rather than creating everything from scratch. The fusion desktop skin that comes with Patch 1 release looked great to us, and decided to use it instead of custom skinning. We changed a few styling details by adding some CSS styles and by overriding existing styles. You may find these posts on skinning useful

If you want to change your skin at run time, please see this forum post.

Cleaning up

It is always a good practice to clean up your project before doing the migration. One of the biggest clean up I did was to remove a lot of unnecessary bindings. Anyone remember this screen?
JDeveloper’s auto binding feature may be suitable for certain applications, but for many projects it only resulted in a binding hell. In 11g, this feature is not selected by default. It was surprising to see we only required a couple of bindings with next to nothing Java code changes (other than removing binding references) compared to hundreds of bindings we had. We could have removed our last few bindings with some source code changes, although we decided to keep the changes to a minimum. I prefer my Java code to be decoupled with JSF library as much as possible. It will make life easier with unit tests as well. And I certainly won’t be the only one to say decoupling is a good idea. Please see Cay Horstmann discusses about decoupling with JSF API here.  (Although he talks in a JSF 2 context, the principles are the same regardless of any version number)
Code re-factoring on view pages – Converting Jspx to Facelets

Changing a jspx page to a Facelets page is very straight forward as this Myfaces wiki explains. But as you might want to use Facelets functionalities like templating, custom tag libraries etc, this would be a perfect time to think and document your overall site design. You can also take out all those nasty f:verbatim tags and include HTML tags directly on your page. So if you ever used some thing like this in your old page,

 

<f:verbatim><hr/></f:verbatim>
outputText value=”#{backing_bean.msg}” />

 

This can now be changed to

 

<hr/>
#{backing_bean.msg}

 

 
Templating

Easy templating is one of the selling points of Facelets. There are good tutorials available on the net on this topic including this one from Jacob Hookom, the creator of Facelets. In our sample project, I’ve included a template file (template.xhtml) with a page header, content area and page footer. You can include any common functionality in your template file. If one or two pages in your application require things differently, you have the option to override them in those specific pages only.

Once the template is created you could start converting each jspx pages. Use the new gallery and select Facelets page (Please make sure “All Technologies” tab is selected)

Then you may start with this code and copy/paste the main content from jspx page, excluding anything that is already in template file.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
template="template.xhtml">
<ui:define name="pageTitle">Facelets Test Page</ui:define>
<ui:define name="content">
<!--
Copy/Paste main content from your jspx page here
-->
</ui:define>
</ui:composition>

You can override any named UI component you’ve defined on your template file.

While a lot of simple rich tags like outputText, forEach, panelBox etc. won’t need any modifications at all, tags like table or treeTable will require some attention. (Please see af:table issues section as well). It will be worthwhile to have a look at the online rich client demo at http://jdevadf.oracle.com/adf-richclient-demo/faces/components/index.jspx. Please see this link if you want to deploy the online demo on your own server. With the introduction of new components like Calendar, Carousel etc. it is very tempting to do a page redesign. Another enhancement we liked was the inline dialog boxes and pop-up windows. (You can see it in action on the sample project)

You can find the list of new features in 11g at http://www.oracle.com/technology/products/jdev/collateral/papers/11/newfeatures/index.html

If you have used JavaScript in your pages, you’ll find this post by Frank Nimphius very useful. One thing you’ll notice straight away is, there are no on event attributes (e.g. onclick) available in tags like af:commandButton. You’ll be required to use af:clientListener tag instead. Please see Frank’s blog for examples.

af:table issues

Oracle has done quite of lot of work with af:table component. It is more intelligent now, so it will only fetch the data as you scroll along, if you wish so, something similar to Yahoo Mail’s inbox. It is quite a useful feature if you have a big table, but at least in our case, the user experience wasn’t that great, especially when you scroll with your mouse scroll wheel. You’ll see quite a lot of “Fetching Data” messages. Again a change you’ll notice here is the lack of separate pagination controls. So even if you set rows=”15” on af:table, it is not going to put pagination controls when you have a table with more than 15 items. It isn’t entirely bug free too, as discussed here. I’ve provided a dirty fix on the forum, but as I mentioned there, it is something Oracle needs fixing.

Code reuse and Facelets custom tag libraries (Please see reference section for online material on this subject)

If you are displaying similar data in more than one page, you could make use of Facelets custom tag libraries to create a common component and reuse everywhere else. When working with ADF Faces, there are a few catches though. One of them is the usage of partialTriggers in tag libraries. See this example here,

On test.xhtml, we are including the custom tag library with this line

 

<j:sdetails value=”#{test.msg}” partialTriggers=”cmdButtonId” />

 

On the tag library file sdetails.xhtml, if we include the partialTriggers directly as shown below, it will result in a run time java.lang.IllegalArgumentException: Cannot convert x of type class java.lang.String to class [Ljava.lang.String; as ADF Faces expect the attribute value of partialTriggers as a String array.

 

<af:outputText value=”#{value}” partialTriggers=”#{partialTriggers}” />

 

To pass in an array, we could define a function in our test.tablig.xml file as,

<function>
<function-name>getStringArray</function-name>
<function-class>uk.co.jaimon.ft.Test</function-class>
java.lang.String[] getStringArray(java.lang.String)
</function>


And declare this static method in Test.java file as,

public static String[] getStringArray(String string) {
if(string == null || "".equals(string)) {
return new String[0];
}else {
return string.split(" ");
}
}

Now on the taglib file, we can use the following line to make it work,

<af:outputText value="#{value}" partialTriggers="#{j:getStringArray(partialTriggers)}" />

You can see the full implementation on the sample project.

Navigation specified in faces-config.xml doesn’t work for pop-ups when used with Rich Components!

That was a surprise, and took a while to figure out Oracle has moved all their extension work on navigation to task flow files.

Let’s say you had a navigation defined in faces-config.xml as,

<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>dialog:nameSearch</from-outcome>
<to-view-id>/nameSearch.jspx</to-view-id>
</navigation-case>
</navigation-rule>

To make this work under 11g, first you need to add an ADF task flow page. From new gallery, select ADF Task Flow off Web Tier/JSF category and click OK.

On the following page, uncheck the box “Create as Bounded Task Flow” as shown here,

And modify the content with the following lines,

<?xml version="1.0" encoding="windows-1252" ?>
<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
<view id="nameSearch">
<page>/nameSearch.xhtml</page>
</view>
<control-flow-rule>
<from-activity-id>*</from-activity-id>
<control-flow-case>
<from-outcome>dialog:nameSearch</from-outcome>
<to-activity-id>nameSearch</to-activity-id>
</control-flow-case>
</control-flow-rule>
</adfc-config>

Again, the sample project includes a working implementation.

Code re-factoring on Java code

This could be a simple task or a bigger one depending on how many bindings you’ve used in your project, and how much re-factoring you want to do. What I did was to replace the word Core with Rich using Search/Replace in Files option. So basically, your CoreTable becomes RichTable, CoreInputText becomes RichInputText etc. And in imports, replace all oracle.adf.view.faces.component.core. with oracle.adf.view.rich.component.rich.

JDeveloper 11g/WebLogic is only certified to work with JDK 1.6. Make use of the new functionalities available with JDK 1.5 and 1.6 like Generics, new for loops, auto boxing etc, if you aren’t already using JDK5 features in your 10g projects. Please see the release notes for JDK 1.5 and JDK 1.6 for more details.

Although OC4J R3 (10.1.3) was a J2EE 1.4 Server, it also supported EJB 3.0 and JPA, which were part of JEE5. If you are using EJB beans, then you might be better off using the migration wizard in JDeveloper 11g. Once migrated, you can convert your jspx to Facelets pages and change backing beans accordingly. You’ll be required to modify web.xml for Facelets support and take out the JSP library as well.  Some of these post might be useful in EJB migration

Migrating data sources

This Oracle document explains about various data sources migration options. What I did was to create IDE connections and then just drag the connection to different applications via Database Navigator. This will result in creation of connections.xml in .adf/META-INF folder on your application folder. Once you add the connection, JDeveloper will create necessary JDBC module when you run or debug your application with the integrated WebLogic server.

Obtaining data sources from your Java code.

JNDI lookup code also needs some modification if you’ve used code similar to,

DataSource ds = (DataSource) ic.lookup("jdbc/preProdDS");

You’ll need to change it to the following line to make it work with WebLogic server,

DataSource ds = (DataSource) ic.lookup("java:comp/env/jdbc/preProdDS");

Just a side note, here the connection name you set up in JDeveloper will be just “preProd” and not “preProdDS”.

IE6 support

Well, the simple answer is, there is none. If you open your application on IE6, a JavaScript alert will pop-up displaying the list of browsers Oracle support.

In a way, it is a good thing, so we can finally get those IE6 users to upgrade. But that can also be the problem, getting them to upgrade before they can use your 11g application. If you desperately needs IE6 support, then you should stick with Trinidad components with 11g and not Rich components.

Rendering issues on IE8

Although IE8 is officially supported with the patch 1 release as mentioned here, there are still some rendering issues with certain components (as of February 2010). One of them is the treeTable component. If you’re on IE8, open test.jspx from my sample application and click on Tree Table Issue button.

As you can see, when you scroll, some texts are getting displayed outside the scrolling area. This doesn’t happen in any other browsers I’ve tested. Hopefully Oracle will fix it in their next release.

Sample Application

 You can download an 11g sample application from here. Run index.jsp or test.xhtml to start the application. It show cases some of the functionalities we’ve discussed on this post, including usage of Facelets templates, tag library, inline pop-ups, ADF-C page flow etc.

Have a happy migration!

Jaimon Mathew

References:

February 6, 2010 Posted by | Uncategorized | , , , , , , , , | Leave a comment

Monitoring Log4J messages on a browser via server push

Introduction

Most of the Java projects I’ve worked on have used Log4J as the logging framework. We normally change the severity level to ERROR on lo4j.xml before deploying our web application to a production system. But when something goes wrong, you may want to enable debugging for a short time and see the messages, and then turn the level back. There are a couple of ways you can change the severity level on the fly. You can either use a jsp file as explained here or a JMX solution as explained here or here

But we also wanted to take peek at the log data in real time so when we see an error being generated we can change the level back to ERROR and look at server’s log files for debugging. Something like doing a tail –f logfile.log from the server. In some places, developers are not given SSH access to server, and needs to contact the DBA for any log files. Even if the developer has access to it, SSH access may be limited to internal traffic, and if you want to fix an issue from home, you’re out of luck. A browser based solution would be ideal in these cases. So here we are discussing such a solution, which should be very easy to implement in your own project.

Implementation

  • Place the liveLogger.jar on your WEB-INF/lib folder
  • Add the following to your web.xml (before the closing web-app tag)

<context-param>
<param-name>LIVE_LOGGER_MAX_CLIENTS</param-name>
<param-value>10</param-value>
</context-param>
<servlet>
<servlet-name>LiveLogger</servlet-name>
<servlet-class>uk.co.jaimon.LiveLogger</servlet-class>
</servlet>
<servlet>
<servlet-name>LiveLogFeeder</servlet-name>
<servlet-class>uk.co.jaimon.LiveLogFeeder</servlet-class>
</servlet>
<servlet>
<servlet-name>LogSelector</servlet-name>
<servlet-class>uk.co.jaimon.LogSelector</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LiveLogger</servlet-name>
<url-pattern>/logger/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>LiveLogFeeder</servlet-name>
<url-pattern>/liveLogFeeder</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>LogSelector</servlet-name>
<url-pattern>/livelog</url-pattern>
</servlet-mapping>

Monitoring the logs

Once you ran your application, point the browser to, http://<<your server>>/<<your context>>/livelog to get started. I’m assuming that you’ve a valid log4j.properties or log4j.xml file in your classpath. This utility itself uses Log4J for logging with the logger name LiveLogger. Here is a sample screen shot of what you should be seeing on the first page. This is where you can select a logger to change its severity or start streaming log data.

Once you select the logger, you’ll see log data being displayed on the left hand pane as shown below. You can also use this page to change the severity level of your selected logger. Once done, change the level back and stop the monitoring by clicking on Stop button.

How does it work?

Once we select the logger, livelog.html file opens an invisible iframe connecting to LiveLogFeeder servlet to enable a comet style server push. Different styles of comet programming are well documented, so I’m not going to repeat it here. I’m using the streaming technique here. The feeder servlet adds a custom appender to our selected logger, which will keep collecting log messages on to a queue. The servlet then pushes these messages in batch to a client queue with some pause (500 milliseconds by default), so your browser doesn’t freeze up. A JavaScript function on the client side process these queues in batch and puts each message on top of existing once, so the last one gets displayed on top.

Issues with streaming a lot of data to a browser

If there is an influx of messages, then it would severely affect the performance of your application. So I’ve limited the number of messages we can receive on the queue to a 1000.

Your browser will also struggle if we keep inserting a large number of items to the list. So there is a 1000 items restriction here as well. JavaScript function which places the message will also remove any old messages.

Since we are adding an appender, it is important we remove it as soon the client stops or the connection gets lost. We’re achieving this in two ways. On the client side, we’ve added a window.onbeforeunload JavaScript event to stop the monitoring when the user closes the tab or window, or going in to another website. On the server side, most JEE servers will throw an IOException when we try to write to a closed connection. So we can remove the appender and get out of our servlet once we get an exception. OC4J behaves slightly differently though, where it takes another minute or so for it to generate an IOException, when the connection gets lost. I’ve also added a bit of code to write some characters, if we haven’t written to the stream in the last minute to make sure we generate an IOException on closed connections.

Making changes to the configuration parameters

At the moment, the only thing that is configurable via web.xml is the maximum number of clients allowed. There are a few other parameters, which I haven’t bothered to get it from web.xml. These are listed here, and I believe all of them are self explanatory.

  • maxNoOfMessagesToWriteOnce (LiveLogFeeder.java)
  • intervalBetweenWrites (LiveLogFeeder.java)
  • maxMessageQLength (LMAppender.java)
  • MAX_LOG_ITEMS_TO_DISPLAY (livelog.js)

My Netbeans project files can be downloaded from here, if you want to make any changes and create the jar file. There is also an Ant target “createjar” which generate the jar file. You’ll need to change the location of the jar file though, as it is hard coded at the moment. If you are using any other IDE, create a new project and copy/paste contents of src and web folders to relevent locations.

Making improvements

There is scope for improvement in a number of places.

On the server side, we could use a separate thread to write messages to the client to reduce some of the overheads. But as you already know, creating threads from Servlet is a no-go area. Two JSRs (236 and 237) were created as possible solutions, unfortunately none of them made it to JEE6. While JSR 236 remains inactive, last year JSR237 was withdrawn. There is a commonj project, which can be utilized as a possible solution.

On the client side, inserting and removing elements via JavaScript takes the most time. I’m sure there are better ways to implement this than using insertBefore and removeChild methods in a loop.

I would appreciate, if you could let me know of any improvements you make to this project.

Files to download

Compiled jar file : liveLogger.jar

Source code (As a Netbeans project) : LiveLoggerV01.zip

February 2, 2010 Posted by | Uncategorized | , , , , , , | 1 Comment

Managing Vimeo video player via Javascript using Moogaloop API

Vimeo Moogaloop JavaScript Integration

Vimeo is a great web site for sharing videos. It doesn’t have any 10 minute video length restriction as in YouTube. It also has some developer tools to assist in integrating vimeo with your own site. My interest was in the Moogaloop API, and using JavaScript to load a list of files and control the player.

The Moogaloop JavaScript example you can download from vimeo wasn’t working for us (As of Dec 2009).  As it turns out, we weren’t the only people having this problem as this forum post shows. In that forum, one user posted a working example at this blog. His example was working for the first time, but not able to reload another movie. The issue was that the DOM element for the container was getting replaced the first time a movie loads, so the next call to embedSWF will fail due to non-existence of a named element. My minor fix was to check for element existence before calling embedSWF and if not found, create it with a document.createElement call.

In this post we are going to extend on that, and showing a sample where we get a list of movies for a particular user and display it in a list with thumbnail images. Clicking on each link will load the movie in the player area, and will start playing (if auto play is enabled).

To  demonstrate Moogaloop’s event system, I’m displaying a “Please wait” message at the top when somebody clicks on Play, and when we are past the first second of playing, we are  hiding this message. Please see Moogaloop’s API documentation for more details. Although I’ve added two event listeners to show the capabilities, onProgress event should only be used with some caution, as it fires a lot of events every second. If you are using it, make sure the code doesn’t run for more than a few milliseconds.

I’ve also included some wrapper methods to call Moogaloop’s API like play, stop, pause and seek at the end of the JavaScript file. One thing to remember is seek will only work with already buffered data. For example, if the movie is just loaded, and there is only 5 seconds worth data in the buffer, then seekTo(10) won’t work at all.

Using it with other JavaScript libraries

If you are including this script with any other JavaScript code, make sure the global variables I’ve used here don’t create a variable name conflict. Normally I tend to use private JavaScript variables as much as possible. But this post is not intended to be used as a library, but as a starting point to get Vimeo integrated, I haven’t isolated the code as I’ve done in my other blogs here and here.

See it in action

For a live demo, please click here.

Download code

You can download all the required  files as a zip from here.

Cheers,

Jaimon

February 2, 2010 Posted by | Uncategorized | , , , , | 5 Comments

Making Scrollable Tables with fixed headers

********** Please click here for an update script with additional functionalities like multiple header freezing, column freezing and multiple tables ***********

Although it is a common requirement, it’s not always straight forward to make scrollable tables with fixed headers. Here, we are going to try a few JavaScript solutions that could be suitable for most cases. I need to emphasize on that, because you might need to make a few tweaks to make it work in your environment, especially tables generated by web  frameworks.

Implementation

To implement these JavaScript solutions, your tables should have an ID and width. Width can be a fixed value or a percentage. Since the width can be specified in percentage, it should also work when window get resized. It has been designed to introduce this functionality with minimal changes to your existing pages. So for the JavaScript functions to work, you only need to add the following lines to your HTML pages,

<script type="text/javascript" src="fxheader1.js"></script>
<script type="text/javascript">
fxheaderInit('dataTable',300);
</script>

Make sure you add the second JavaScript call after the table element. Replace dataTable with the ID of your table, and 300 with the height you require. Instead of specifying the height as a fixed value, there are ways to stretch the table to fill the page based on other components on the page. Last solution make use of such a method.

Browser compatibility

It’s been tested on the following browsers all running on Windows XP.

  • IE6
  • IE7
  • IE8
  • Firefox 2.0
  • Firefox 3.5
  • Google Chrome

The big table

Here is an example of a table we are going to introduce the scrolling functionality. Well, it is not really big, just big enough to test our functionality 🙂

http://jaimonblog.appspot.com/datascroller/bigTable.html

Solution 1: Simple CSS only scrolling without fixing headers

With the help of a bit of CSS, we can implement a simple scroller. Enclose the table element within a div container with an overflow value of auto and a fixed height. Something like

<div style=’overflow:auto;height:300px;’>
<table …. />
</div>

You can see the demo at http://jaimonblog.appspot.com/datascroller/simpleScroller.html

Solution 2: Scrolling with fixed headers using table cloning

You can see a working demo at http://jaimonblog.appspot.com/datascroller/fixedHeaderFullClone.html

Download fxheader1.js and include it in your project as explained on the implementation section

How does it work?

  • First we put a scrollable div container around the table element as in our first solution.
    • create a div element
    • clone the table element using cloneNode(true)
    • added the cloned table to this div
    • replace the table element with this div
  • Create a container on top of the table to place the headers.
  • Attach scrollHeader function to the table container div’s onscroll event, so that we can align the heading when the table is scrolled horizontally.
  • We then clone the table and place it in the header container. Container height is set, so that only the header row is visible.
  • We then set a negative margin top on the original table to hide the actual header.
  • Attach fxheader function to window onresize event, so it works when window get resized.

Please note that, there are more than one way to do add a div element around our table element using JavaScript. You could speed up the process by adding these divs manually on the page as in Solution 1, because cloneNode function can be expensive. Make sure to use the correct IDs, so that rest of the code can find these. Please see the function addScrollerDivs in fxheader1.js for more details.

Solution 3: Scrolling with fixed headers using fast table cloning

You can see a working demo at  http://jaimonblog.appspot.com/datascroller/fixedHeaderFastClone.html

Download fxheader2.js and include it in your project as explained on the implementation section to get started.

Solution 2 will give you very accurate column widths at the expense of doing a full table cloning. If you have a table with lots of rows, this can take a while. If your rows have elements with unique IDs, you will have to change it on the cloned node to avoid  ID collision. Although it will work, it will lead to a sluggish user experience on pages with really large tables.

In solution 3, we are doing a fast clone without any child elements. Then we add the first row to it and set individual column width separately based on the offsetWidth property of each cell. You’ll have to offset cell margin/padding when setting the width. In our example, I deduct 3 from offsetWidth and that works fine for our table in all browsers I’ve tested.

Everything else is similar to solution 2.

Solution 4: Adding scrolling to table generated by ADF Faces 10g with fixed header (With and without pagination support) and automatic height stretching

Although in theory it is similar to solution 3, I had to make a few changes to make it work for a table generated by ADF Faces. The problem is ADF Faces creates three HTML tables for each af:table element. First and third tables are created for pagination support. It doesn’t create an ID for the actual data table itself, so we need to access it as a childNode of the container element. If pagination is there, we need to place this above our fixed header. Since we have the scrolling functionality, I thought the second pagination controls below the data table is not necessary, and I’m removing it from the page.

When cloning table data here, we have to update ID field of each element to avoid ID collision. We also have to set the correct selected index value for pagination selector combo box. Another functionality I’ve implemented here is the ability to auto stretch the table content to fit the page. You should pass in the ID of your last container component to make it work. Please see adjustHeight function in fxheader.js for more details.

As with the second and third solutions, you could add the following line to your JSPX page to get this working.

<afh:script source="fxheader.js" />
<afh:script text="fxheaderInit('testTable','footerContent');" partialTriggers="testTable" />

 

Where testTable is the ID of your af:table element and footerContent is the ID of your last container component for stretching. Partial Triggers are required here if used with pagination.

You can download a fully functioning JDeveloper project from http://jaimonblog.appspot.com/datascroller/ADF10gScroller.zip

Please note that it is created with  JDeveloper version 10.1.3.3. If you are using 11g, then fixed header functionality is already part of it. (Although there are a few issues with it at the time of writing this. One such issue is discussed here http://forums.oracle.com/forums/message.jspa?messageID=4032492#4032492)

As I’m not able to provide a demo link, you can see some screen shots of this solution here,

 

With Pagination

Without Pagination

February 2, 2010 Posted by | Uncategorized | , , , , | 84 Comments

C# FTP Client Library

As this is my first blog, I’m going to write an update for a C# library I wrote in early 2001 on csharphelp.com site. Although it was the first open source FTP client library for .Net, there is a lot changed since then. But I still get emails from people asking about bugs, license etc. And I always send the same reply (when I get the time to do that).

From framework version 2.0, Microsoft has added WebClient class and you could do a lot of networking stuff with it. It is much easier to do FTP downloads/uploads with this new class. There are a lot of articles on the net on how to use FTP with WebClient class, so I’m not going to reinvent the wheel here. Some sites I’ve got through Googling is listed here.

So if anybody is still using my old library, please update your code to use WebClient class.

Cheers..

JM

September 2, 2009 Posted by | Uncategorized | , , , , , | 1 Comment