Friday 19 October 2007

JDeveloper 10.1.3.3 JSP Compiler Errors in JSF faces

I have run into the following error while modifying JSF pages in JDeveloper 10.1.3.3 on openSUSE. While modifying the page contents using the JDeveloper editor, the page stops working when accessed from the browser and you get an Internal Server Error with a JspCompileException but nothing else. The page was still accessible from the internal JDeveloper Design editor and would complie correctly from inside JDeveloper.

A remedy that sometimes worked was to remove a few components from the page put them back and then hope it would work.

Eventually I came up with the following simple trick. Just remove all contents from ~/jdevhome/mywork/WorkSpace/ViewController/classes/.jsps and rebuild your project.

Thursday 18 October 2007

Delphi: How to install BDE on a client machine

Suppose you have a Delphi application that access data using the Borland Database Engine (BDE) and you wish to deploy it on a user's client computer. If you compile the project without runtime packages and no BDE support is required, then merely copying the EXE file on the target computer is usually enough.

Now installing BDE on a client computer is performed as follows :

  1. Locate the file bdeinst.cab located in C:\Program Files\Common Files\Borland Shared\BDE.
  2. Open it using winZip or something equivalant.
  3. The .cab file contains only one file BDEINST.DLL. Extract this to the users client computer.
  4. Use the command regsvr32.exe bdeinst.dll from the command prompt to perform the installation.
  5. Run your application

BDE un-installation can be performed by folloowing these steps :

  1. Delete the folder containing BDE data
  2. Delete the BDEADMIN.CPL file in the Wiindows\System folder, in order to get rid of the control file applet
  3. Use regedit in order to delete the key HKEY_CURRENT_USER\Software\Borland\BDEAdmin
  4. Next locate HKEY_LOCAL_MACHINE\Software\Borland and delete the subkeys BLW32, Borland Shared and Database engine.
  5. Reboot your machine

Note : Having said all that I would like to give you an extract from the file bdedeploy.txt located at the BDE installation directory.

2. BORLAND-CERTIFIED INSTALLATION PROGRAMS
===========================================================
Borland products which include redistribution rights
include an Borland-certified install program, such as
InstallShield Express, to ensure proper installation and
uninstallation of your applications so that they co-exist
well with other applications which use the Borland Database
Engine (BDE) including those created with Visual dBASE,
Paradox, Delphi, and C++Builder.

Borland has also provided BDE and SQL Links installation
information to other install vendors such as Sax Software,
WISE Software, Great Lakes Business Systems (GLBS) makers
of the WISE install tool and Eschalon Development so that
their products can also be ensured to be fully compatible
with the BDE.

From time to time, Borland Software Corporation may, 
at its discretion, certify additional installation programs 
for use as the Borland Certified Install Program for this 
product.
  
Also check the Borland-sponsored announcement newsgroups:

  news:borland.public.announce
  news:borland.public.bde

3. BDE DEPLOYMENT (ALL DATABASE APPLICATIONS)
===========================================================
3.1 Deploying the BDE
---------------------
A Borland-certified installation program provides all
needed functionality and steps for redistributing the
Borland Database Engine (BDE), including:

  * Selecting files to redistribute
  * Determining final directory locations
  * Comparing versions of BDE files
  * Creation of BDE aliases
  
Follow the instructions of the particular installation
program used for specific steps to create an installation
program that includes the BDE.

Wednesday 17 October 2007

JSF How to create and use dependent list boxes

Back in 2006, Frank Nimphius has written two excellent articles about using AJAX style combo (or list) boxes in a web page that filter one another based on values of a master detail relationship. I have to admit that thanks to him, my users have one less reason to grumble :-)

The first one is entitled ADF Faces: How-to build dependent lists boxes with ADF and ADF Faces and the second one ADF Faces: How-to build dependent lists boxes with ADF and ADF Faces Part - II

Thank you Frank.

ABAP: How to tell if user input is a number

ABAP casts between types by just sequentially copying (as many as the size of the target type) bytes from one memory area to an other and then checks if the new contents of the target memory area can be perceived as the appropriate type. As a general rule strings and character objects are always copied left while numbers get copied right justified)

This little code fragment will get a number out of a string and convert it to practically any numeric format.

        DATA :
           temp_str(10) type c value '1234,34'.
           my_number TYPE f.

*       If the value of temp_str is comming from user input of from any other
*       user or ABAP dictionary type then it is also wise to execute something like
        CONDENSE temp_str.

*       You may want to execute this if you --like me -- your 
*       decimal symbol is a comma
        REPLACE ',' WITH '.' INTO temp_str.

        CATCH SYSTEM-EXCEPTIONS conversion_errors = 4.
          my_number = temp_str.
        ENDCATCH.
        IF sy-subrc <> 0.
          MESSAGE e888(sabapdocu) WITH temp_str ' is not a number'.
        ENDIF.

Friday 12 October 2007

ABAP Obtaining an object's characteristics from the classification system

The easiest way to get a list of all the characteristics of an object is to use the BAPI_OBJCL_GETCLASSES BAPI. The function requires that you supply an object key, the database table where the object is stored, the class type of the obect to look for -- e.g. '001' for material or '023' for batch -- and finally whether or not to return the entire list of characteristics.

The return value consists of a table containing all the classes the object is currently assigned and if requested three additional tables with values of the object characteristics, one for string valued, one for numeric and one for currency.

Information about objects, db tables and corresponding class types is stored in the inob table. So one way of getting all this would be to write code like :

  DATA : 
    wa_inob TYPE inob, 
    my_object LIKE inob-objek.

  CLEAR wa_inob.
  CONCATENATE my_matnr my_batch INTO my_object.
  SELECT SINGLE * 
    FROM inob 
    INTO wa_inob
    WHERE objek = my_object.

So calling the BAPI would be as straight forward as ....

  DATA :
    class_list TYPE STANDARD TABLE of bapi1003_alloc_list.
    valueschar TYPE STANDARD TABLE OF bapi1003_alloc_values_char, 
    valuescurr TYPE STANDARD TABLE OF bapi1003_alloc_values_curr, 
    valuesnum  TYPE STANDARD TABLE OF bapi1003_alloc_values_num,
    return     TYPE STANDARD TABLE OF bapiret2.

  CALL FUNCTION 'BAPI_OBJCL_GETCLASSES'
    EXPORTING
      objectkey_imp         = wa_inob-objek
      objecttable_imp       = wa_inob-obtab
      classtype_imp         = wa_inob-klart
      read_valuations       = 'X'
      keydate               = sy-datum
*     language              = 'E'
    TABLES
      alloclist             = class_list
      allocvalueschar       = valueschar
      allocvaluescurr       = valuescurr
      allocvaluesnum        = valuesnum
      return                = return.

Determining if an object has indeed been assigned classification information is performed by checking the return table value. Correct classification means that the return table contains one line with field "TYPE" set to 'S', "ID" set to 'CL' and NUMBER set to 741. (When no allocations are found number is 740). So before trying to read any characteristic values is safer to test like this.

     FIELD-SYMBOLS
        <ret> TYPE bapiret2.

     READ TABLE return INDEX 1 ASSIGNING <ret>.

*   Check that object allocations exist
    IF  sy-subrc = 0 AND
        <ret>-id = 'CL' AND
        <ret>-number = 741.

      ....

Now getting a specific value from let's say the string valued characteristics can be accomplished by code similar to following, which gets the value of a characteristic named QUALITY:

  FIELD-SYMBOLS :
    <fs> TYPE bapi1003_alloc_values_char.

  READ TABLE valueschar WITH KEY charact = 'QUALITY' ASSIGNING <fs>.

  if <fs> IS ASSIGNED.
*   The language independent value of the characteristic is 
    my_quality_id = <fs>-value_neutral.
*   The language dependent i.e. the one that will appear in the
*   result table of CL30N is 
    my_quality_descr = <fs>-value_char.
  ENDIF.

NOTE: The following approach is generic and is supposed to work fine on all systems. In our system however (Release 4.6C) the inob table does not have an index on inob-objek, so the SELECT SINGLE statement is executed by performing sequential reads. One way to overcome this would be to manually create the index. The other, as far as I am concerned, is to know beforehand the type of objects and classes that your program deals with, and therefore hard code the database table to 'MARA' or MCH1' and the class type to '001' and '023' for materials and batches respectively.

Friday 5 October 2007

HTML Code for searching your site with Google.

I had seen this done in many sites and to be honest I always thought of it as a fancy trick for showing off HTML skills. Lately while using Steve Muench's Blog I realized that this little HTML form turns out to be of ultimate use. Then I said ok let's do it and ended up looking at HTML code from various sites....Finally I ended up with the following solution, that you can copy at use as is, Just replacing my site with yours.


<form action="http://www.google.com/search" method="get">
    <table border="0" align="center">
        <tr>
            <td>
                <input maxlength="255" value="" name="q" size="31" type="text"/>
                <input value="Google Search" type="submit"/>
            </td>
        </tr>
        <tr>
            <td>
                <input value="" name="sitesearch" type="radio"/>
                The Web
                <input checked value="abakalidis.blogspot.com" name="sitesearch" type="radio"/>
                This Blog
            </td>
        </tr>
    </table>
</form>

Thank you Kate for the messy HTML color syntax.:-)

Wednesday 3 October 2007

Java: How to tell if an LDAP user is member of a group

Here is a Java class that I use in order to determine if a user of an LDAP server is a member of a group. The class uses the Mozilla LDAP SDK available for download from Mozilla

In many JSF applications this class lies in the heart of my UserInfo managed beans allowing or f orbiting access to various parts of the application.

Things you need to fill before using it are :

  • server name or IP address
  • Server port
  • A user name and a password that will allow your code to execute queries on the LDAP Server
  • The base DN to start the search at

The usual place to find these values is the LDAP server itself. If you server is an Oracle IAS 10.2 infrastructure then check the file $ORACLE_HOME/install/portlist.ini.


package ab.util.ldap;

import java.util.Enumeration;

import netscape.ldap.LDAPAttribute;
import netscape.ldap.LDAPConnection;
import netscape.ldap.LDAPEntry;
import netscape.ldap.LDAPException;
import netscape.ldap.LDAPReferralException;
import netscape.ldap.LDAPSearchResults;

public class MyLDAP 
{
    public static final String ldapHost = "ldap.shelman.int";
    public static final int ldapPort = 3060;
    private static final String authid = "cn=orcladmin";
    private static final String authpw = "my_password";
    private static final String base = "cn=groups,dc=shelman,dc=int";

    /**
     * isGroupMember -- determine if user belongs to an LDAP group
     *
     * #param group name of group to examine
     * #param user name user to search for emembership+-
     */
    public static boolean isGroupMember(String group, String user) 
    {
        String member = "cn=" + user.toLowerCase();
        String filter = "(cn=" + group + ")";
        String[] attrs = { "uniquemember" };
        boolean result = false;

        LDAPConnection ld = new LDAPConnection();
        // System.out.println("Attempting to connect :"  + today.toString());
        try {
            // connect to server and authenticate
            ld.connect(ldapHost, ldapPort, authid, authpw);
            // issue the search request
            LDAPSearchResults res =
                ld.search(base, ld.SCOPE_SUB, filter, attrs, false);
            // loop on results until complete
            while (res.hasMoreElements()) {
                try {
                    LDAPEntry entry = res.next();
                    LDAPAttribute atr = entry.getAttribute(attrs[0]);
                    Enumeration enumVals = atr.getStringValues();

                    while (enumVals != null && enumVals.hasMoreElements()) {
                        String val = (String)enumVals.nextElement();
                        // convert to lower case
                        val = val.toLowerCase();

                        if (val.indexOf(member) >= 0) {
                            result = true;
                            break;
                        }
                    }

                } catch (LDAPReferralException ex) {
                    // ignore referal exceptions
                    continue;
                } catch (LDAPException ex) {
                    System.out.println(ex.toString());
                    continue;
                }
            }
        } catch (Exception ex) {
            System.out.println("Error communicating with LDAP Server " +
                               ldapHost + " Port " + ldapPort);
            System.out.println(ex.toString());
        }

        // finished searching. try to disconnect
        if (ld != null && ld.isConnected()) {
            try {
                ld.disconnect();
            } catch (LDAPException ex) {
                System.out.println(ex.toString());
            }
        }

        // return whatever found
        return result;
    }
}

The package ldapjdk-4.17-38 is included in the distribution of all openSUSE systems. For openSUSE 10.2 the package is available for download from here.