Analytics


Google

Thursday, December 25, 2008

Shell Scripting on Unix

Ideally to learn how to do scripting, we need to have a unix environment to try out the scripts and this can be easily done by setting up Linux or Open Solaris on your computer. However, what happens if you don't have a spare computer to do that?

You could use a live CD. Problem with that is you need to reconfigure it each time which is troublesome. Alternatively, you can setup Cygwin. There are two version - Cygwin which is purely command prompt and Cygwin/X which provides a X Windows environment.

Given that you can then use the various tutorials on Internet to learn scripts like bash:

Linux Config org
Bash Programming How To
Advanced Bash Tutorial

Alternatively, you can use perl, php or even java to automate your unix scripts.

Monday, December 22, 2008

Free Charting Libraries

When writing program, we have an option to either write everything ourselves or to use/leverage on utilities/libraries that are already written. This is especially true when it comes to charting.

When using asp classic, you can use the mschart control that comes with MS Office. However, that requires that you install MS Office on your webserver. Also, I found the library quite difficult to work with.

The following are some popular opensource/freeware libraries you can use:

For Java, you can use jfreechart.
For .Net, you can use zedgraph. I saw a good article on this on this web page, which is in fact how I found this library.

Alternatively, you can use paid software, our previous software was chartfx and then we started working on Netchart.

Netchart is good in that it can be used for different platforms because it uses webservices and provides libraries for java, asp etc. Only thing it is requires a server to serve the webservices.

Some additional libraries that I found are:

Open Flash Chart
Fusion Chart Free A good sample code on how to use Fusion Chart Free using .net is found here.
Some additional java script charting components are found here.

Friday, December 19, 2008

Can't find table in stored function, procedure or package in Oracle

This is a strange problem:

We have rights to select a table and can select in normal sql statement. However, when we write a stored procedure or function (with or without a package), we encounter an error that says table not found.

The one solution we found was to grant that account select any table rights. Once granted, we can compile and use the stored procedure/package. However, the problem with doing that is the account can also select any table in any schema in that instance.

I found the following solution thanks to a support from the community forum:

Note:388774.1


Applies to:
Oracle Server - Enterprise Edition - Version: 9.2.0.1 to 10.2.0.3
This problem can occur on any platform.
Symptoms
When SYSTEM user grants select on a table to another user with SQL*Plus , it works fine: SYSTEM user has the GRANT ANY OBJECT PRIVILEGE system privilege .

SQL> CONNECT system/xxx
Connected.
SQL> grant SELECT, INSERT, UPDATE, DELETE on SCOTT.TEST TO X;
Grant succeeded.
1. Under 10g, when this statement is in a stored procedure, it returns "ORA-1031 - insufficient privilege".
2. Under 9i, it returns "ORA-00942: table or view does not exist "

SQL> CREATE OR REPLACE PROCEDURE system.def_grant_test
IS
cursor_handle INTEGER;
BEGIN
cursor_handle := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cursor_handle, 'GRANT SELECT, INSERT, UPDATE, DELETE on SCOTT.TEST TO X', dbms_sql.native );
DBMS_SQL.CLOSE_CURSOR(cursor_handle);
END;
/


Procedure created.
SQL 9i> exec system.def_grant_test
BEGIN system.def_grant_test; END;
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at "SYS.DBMS_SYS_SQL", line 826
ORA-06512: at "SYS.DBMS_SQL", line 39
ORA-06512: at "SYSTEM.DEF_GRANT_TEST", line 6
ORA-06512: at line 1
SQL 10g> exec system.def_grant_test
BEGIN system.def_grant_test; END;

*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SYS.DBMS_SYS_SQL", line 906
ORA-06512: at "SYS.DBMS_SQL", line 39
ORA-06512: at "SYSTEM.DEF_GRANT_TEST", line 6
ORA-06512: at line 1
Cause
Generally an invokers's called procedure executes using the caller's privileges and all unqualified objects are resolved in the schema of the caller.
The privileges of the invoker are checked during runtime only for
DML statements ('insert', 'update','delete', 'update')
dynamic sql statements ('execute immediate' and 'open for using')
cursor statements ('open' and 'open for update')
DDL statements ('lock table')
parsed SQL statements using dbms_sql.parse
Solution
To implement the solution, please execute the following steps:

1. Create the procedure defined as an invoker's right procedure , with AUTHID CURRENT_USER clause:

SQL> CREATE OR REPLACE PROCEDURE system.inv_grant_test authid current_user
IS
cursor_handle INTEGER;
BEGIN
cursor_handle := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cursor_handle, 'GRANT SELECT, INSERT, UPDATE, DELETE on SCOTT.TEST TO X', dbms_sql.native );
DBMS_SQL.CLOSE_CURSOR(cursor_handle);
END;
/


Procedure created.
SQL> exec system.inv_grant_test
PL/SQL procedure successfully completed.
SQL> select * from dba_tab_privs where table_name='TEST' and grantee='X';

GRANTEE OWNER TABLE_NAME GRANTOR PRIVILEGE GRA HIE

-------- -------- ---------- -------- ---------- --- ---
X SCOTT TEST SCOTT INSERT NO NO
X SCOTT TEST SCOTT DELETE NO NO
X SCOTT TEST SCOTT SELECT NO NO
X SCOTT TEST SCOTT UPDATE NO NO

Wednesday, December 17, 2008

Java Enterprise Edition (JEE) servers

Two of the most renown JEE servers are JBoss (now under RedHat) and GlassFish (from Sun).

However, if your company is primarily using Weblogic at the corp level and you want to have a development environment, you can download it here, it comes with a free developer license (which allows up to 5 ip addresses) to perform your test and development.

Maybe it is due to my lack of knowledge of the server. I was unable to successfully create my own project using the easy start. The project was created using the wizard but failed to start. Also, I noted that the weblogic server (10.3 still uses Java 1.4 instead of Java 1.5 or Java 1.6) as used by the JBoss and Glassfish. Also, there was no easy way to start the server by itself.

I tried JBoss, starting and stopping is very simple. I have yet to try out Glassfish.

JBoss 4.2.x is JEE 5 able but not certified. JBoss 5.0 is JEE 5 certified. Glassfish and Weblogic also support JEE 5. All three servers are supported by NetBeans.

Tuesday, December 16, 2008

m.www.yahoo.com

I recently received a complaint from a colleague that she is not able to get to the yahoo website:

http://www.yahoo.com

instead she was redirected to the following website:

http://m.www.yahoo.com

The first reaction from our helpdesk was that there could be a possible spyware on her computer. Mostly because others who type the following address were redirected to the following page:

http://www.yahoo.com/?nm=1

A quick search in Google quickly revealed that she was directed to a new interface which yahoo is testing. The frustrating part is the users are picked at random and are not given a choice to revert back.

The fix is to clear the cookie and the cache in her browser. Also her feedback was that she did not really like the new interface, she preferred the old one because:
  1. To her it was prettier with the pictures that changes according to season. Like right now there is the Christmas greeting.
  2. Everything is with one click.
I read comments from other sites and a lot of them did not like it either, even though Yahoo was trying to emulate Google's cleaner interface. I guess that is a lesson to be learned. One your user base is used to an interface, you should not just change it; otherwise you risk angering your loyal customers!!!

Sunday, December 14, 2008

Compiling .Net programs with Nant

Nant is the .Net Version of Ant. It is capable of doing a lot of things as indicaed in the task reference here. However, for the purpose of this article, I am going to focus how to use it to either compile an .net assembly either to .exe or .dll.

In my previous article, I provided an example how we can write a program without using Visual Studio. Assuming you have written your program, you could use the command line vbc (for Visual Basic compiler) and csc (C# compiler) to compile. However, it is tedious if the program references a lot of libraries or there are multiple source file.

To compile using Nant, first step is to download NANT. The home page is found here. Download and unzip the zip file. Assuming you downloaded Nant 8.5, you can unzip everything to c:\nant-0.85. When unzipping, just say use the folder in the zip file then unzip it to c:\. It will automatically create a folder tree c:\nant-0.85\...

Next, create a build file:

Here is an example for compiling .exe:

<?xml version="1.0"?>
<project name="FileLimAlert" default="build" basedir=".">
<property name="debug" value="true" unless="${property::exists('debug')}" />
<property name="verbose" value="true" />
<target name="clean" description="remove all generated files">
<delete file="FileLimAlert.exe" failonerror="false" />
<delete file="FileLimAlert.pdb" failonerror="false" />
</target>
<target name="build" description="compiles the source code">
<vbc target="exe" output="FileLimAlert.exe" debug="${debug}">
<sources>
<include name="FileLimAlert.vb" />
</sources>
<references>
<include name="System.dll" />
<include name="System.Web.dll" />
<include name="System.Web.Services.dll" />
<include name="System.XML.dll" />
</references>
</vbc>
</target>
</project>


Here is a sample to compile code behind (into .dll) - assuming this is called newLdaplogin.build.xml:

<?xml version="1.0"?>
<project name="newLDAPLogin" default="build" basedir=".">
<property name="debug" value="true" unless="${property::exists('debug')}" />
<property name="verbose" value="true" />
<target name="clean" description="remove all generated files">
<delete file="bin\newLDAPLogin.dll" failonerror="false" />
<delete file="bin\newLDAPLogin.pdb" failonerror="false" />
</target>
<target name="build" description="compiles the source code">
<vbc target="library" output="bin\newLDAPLogin.dll" debug="${debug}">
<sources>
<include name="newLDAPLogin.aspx.vb" />
</sources>
<references>
<include name="System.dll" />
<include name="System.Web.dll" />
<include name="System.Data.dll" />
</references>
</vbc>
</target>
</project>


Note
  1. The target build will instruct nant what to do. In the above example to use VBC to compile either.
  2. In the first example above, the vbc target is to exe and the second one is to library.
  3. The references section within the target are for the libraries your program is referencing.
  4. The sources section within the target are for all the source code.
  5. As shown above, each section can include multiple includes, so you could have multiple source code compiling into either an exe or a dll.
  6. Clean basically instruct nant what are the files to delete.
To compile, just type (if you are building for Framework 1.1):

c:\nant-0.85\bin\nant -buildfile:newLdaplogin.build.xml -t:net-1.1

The output will look something like this:

NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006)
Copyright (C) 2001-2006 Gerry Shaw
http://nant.sourceforge.net

Buildfile: file:///C:/project/vbUtils/clsldap.build.xml
Target framework: Microsoft .NET Framework 1.1
Target(s) specified: build


build:

[vbc] Compiling 1 files to 'C:\project\vbUtils\bin\
newLDAPLogin'.

BUILD SUCCEEDED

Total time: 3.7 seconds.


If you want to build for framework 2.0 then change -t:net-1.1 to -t:net-2.0.

The build will detect is a recompilation is necessary and will not compile if the assembly is already up to date. Each version of nant will have a list of framework that is supported.

The latest version 0.86-Beta 1 supports the following framework:

FrameworkTargetRuntime
.NET Framework 1.0**
.NET Framework 1.1**
.NET Framework 2.0**
.NET Framework 3.5**
.NET Compact Framework 1.0*
.NET Compact Framework 2.0*
Mono 1.0 Profile**
Mono 2.0 Profile**
Mono 3.5 Profile**
Moonlight 2.0*
Shared Source CLI 1.0*
Silverlight 2.0*

You can check the version supported by looking into the nant.exe.config file.

Friday, December 5, 2008

Removing standby or logout from Windows XP shutdown menu

Obtained the following from here - Have not verified:


HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ACPI\Parameters
Click in the right pane and create a new REG_DWORD value.
Give it the name Attributes
Set the value to 00000070

After reboot, Standby is unavailable.

********************************

to remove logoff :-

Start the Registry Editor (go to Start, Run and type Regedt32). Find the key

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\Explorer]
create a new dword called NoLogOff and give it a value of 1

the above can be done on the hklu part of the registry if it is just for a particular user.

Monday, December 1, 2008

Oracle Hints

Every so often, when you have an application which is working fine and suddenly the applications slows down for no apparent reason. I have seen this on many occasions and found that majority of the time is somehow Oracle decided the fastest way to perform my sql query is to do a full table scan on some huge tables because the database has many extends.

In those situations, I use hints to "advice" Oracle to use the index for example:

select /*+ index(e iempmaster) */ empno, title, extension from empmaster e where empno = 'XXX123';

Note the the hints starts with /*+, it needs a space immediately after that prior to the hint. Index is not the only options there are many other hints such as hash etc. You can find reference to the rest in the following sites:

http://www.psoug.org/reference/hints.html
http://www.dba-oracle.com/t_sql_hints_tuning.htm

To test if your hint is working or not, perform an explain plan without the hint and run the explain plan with the hint. You should see the difference. You can perform explain plan from the sqlplus (dos prompt) or use SQL Developer, there is a button for execute Explain plan. However, to do that you need to have a table called plan_table with the following structure:

Column Name Data type
STATEMENT_ID VARCHAR2(30 BYTE)
TIMESTAMP DATE
REMARKS VARCHAR2(80 BYTE)
OPERATION VARCHAR2(30 BYTE)
OPTIONS VARCHAR2(30 BYTE)
OBJECT_NODE VARCHAR2(128 BYTE)
OBJECT_OWNER VARCHAR2(30 BYTE)
OBJECT_NAME VARCHAR2(30 BYTE)
OBJECT_INSTANCE NUMBER
OBJECT_TYPE VARCHAR2(30 BYTE)
OPTIMIZER VARCHAR2(255 BYTE)
SEARCH_COLUMNS NUMBER
ID NUMBER
PARENT_ID NUMBER
POSITION NUMBER
COST NUMBER
CARDINALITY NUMBER
BYTES NUMBER
OTHER_TAG VARCHAR2(255 BYTE)
PARTITION_START VARCHAR2(255 BYTE)
PARTITION_STOP VARCHAR2(255 BYTE)
PARTITION_ID NUMBER
OTHER LONG
DISTRIBUTION VARCHAR2(30 BYTE)

Monday, November 24, 2008

Accessing LDAP using Java

The easiest way to access LDAP in Java is to use JNDI (Java Naming and Directory Interface).

A good example on how to do that can be obtained from Sun website. Snippet is as follows:


In the LDAP, authentication information is supplied in the "bind" operation. In LDAP v2, a client initiates a connection with the LDAP server by sending the server a "bind" operation that contains the authentication information.

In the LDAP v3, this operation serves the same purpose, but it is optional. A client that sends an LDAP request without doing a "bind" is treated as an anonymous client (see the Anonymous Authentication section for details). In the LDAP v3, the "bind" operation may be sent at any time, possibly more than once, during the connection. A client can send a "bind" request in the middle of a connection to change its identity. If the request is successful, then all outstanding requests that use the old identity on the connection are discarded and the connection is associated with the new identity.

The authentication information supplied in the "bind" operation depends on the authentication mechanism that the client chooses. See the next section for a discussion of the authentication mechanism.

Authenticating to the LDAP by Using the JNDI
In the JNDI, authentication information is specified in environment properties. When you create an initial context by using the InitialDirContext(in the API reference documentation) class (or its superclass or subclass), you supply a set of environment properties, some of which might contain authentication information. You can use the following environment properties to specify the authentication information.

* Context.SECURITY_AUTHENTICATION(in the API reference documentation) ("java.naming.security.authentication").
Specifies the authentication mechanism to use. For the Sun LDAP service provider, this can be one of the following strings: "none", "simple", sasl_mech, where sasl_mech is a space-separated list of SASL mechanism names. See the next section for a description of these strings.
* Context.SECURITY_PRINCIPAL(in the API reference documentation) ("java.naming.security.principal").
Specifies the name of the user/program doing the authentication and depends on the value of the Context.SECURITY_AUTHENTICATION property. See the next few sections in this lesson for details and examples.
* Context.SECURITY_CREDENTIALS(in the API reference documentation) ("java.naming.security.credentials").
Specifies the credentials of the user/program doing the authentication and depends on the value of the Context.SECURITY_AUTHENTICATION property. See the next few sections in this lesson for details and examples.

When the initial context is created, the underlying LDAP service provider extracts the authentication information from these environment properties and uses the LDAP "bind" operation to pass them to the server.

The following example shows how, by using a simple clear-text password, a client authenticates to an LDAP server.

// Set up the environment for creating the initial context
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");

// Authenticate as S. User and password "mysecret"
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=S. User, ou=NewHires, o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, "mysecret");

// Create the initial context
DirContext ctx = new InitialDirContext(env);

// ... do something useful with ctx

Using Different Authentication Information for a Context
If you want to use different authentication information for an existing context, then you can use Context.addToEnvironment()(in the API reference documentation) and Context.removeFromEnvironment()(in the API reference documentation) to update the environment properties that contain the authentication information. Subsequent invocations of methods on the context will use the new authentication information to communicate with the server.

The following example shows how the authentication information of a context is changed to "none" after the context has been created.

// Authenticate as S. User and the password "mysecret"
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=S. User, ou=NewHires, o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, "mysecret");

// Create the initial context
DirContext ctx = new InitialDirContext(env);

// ... do something useful with ctx

// Change to using no authentication
ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "none");

// ... do something useful with ctx

Authentication Failures
Authentication can fail for a number of reasons. For example, if you supply incorrect authentication information, such as an incorrect password or principal name, then an AuthenticationException(in the API reference documentation) is thrown.

Here's an example that is a variation of the previous example. This time, an incorrect password causes the authentication to fail.

// Authenticate as S. User and give an incorrect password
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=S. User, ou=NewHires, o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, "notmysecret");

This produces the following output.

javax.naming.AuthenticationException: [LDAP: Invalid Credentials]
at java.lang.Throwable.(Compiled Code)
at java.lang.Exception.(Compiled Code)
...

Because different servers support different authentication mechanisms, you might request an authentication mechanism that the server does not support. In this case, an AuthenticationNotSupportedException(in the API reference documentation) will be thrown.

Here's an example that is a variation of the previous example. This time, an unsupported authentication mechanism ("custom") causes the authentication to fail.

// Authenticate as S. User and the password "mysecret"
env.put(Context.SECURITY_AUTHENTICATION, "custom");
env.put(Context.SECURITY_PRINCIPAL, "cn=S. User, ou=NewHires, o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, "mysecret");

This produces the following output.

javax.naming.AuthenticationNotSupportedException: Unsupported value for java.naming.security.authentication property.
at java.lang.Throwable.(Compiled Code)
at java.lang.Exception.(Compiled Code)
at javax.naming.NamingException.(Compiled Code)
...

Wednesday, November 19, 2008

Understanding J2EE Programming

I have bought many books on J2ee programming and I found a lot of times the concept is very difficult to grasp - especially for a person who is from .Net programming background.

However, I found so far the best explanation to help understand the concept is from theserverside web site. The provide some complete books on the subject in PDF format for free download. One of them is Mastering EJB.

If you are interested in the subject, this is an excellent place to start. However, the book may be a little outdated but I believe most of the concepts on the stateful and stateless beans are still explained nicely in the book. There is also a more uptodate version of it (again free download of the complete pdf) - Mastering Enterprise JavaBean 3.

One of the problems I have with J2EE, is they keep adding other framework like Struts, JavaFaces, Hibernate etc. The above book does not cover those.

Saturday, November 15, 2008

Rich experience programming on the Web

I read a pretty good review on the launch of Java 1.6u10 release. In one of the comments, there is a reference to Interactive Pulp which provides samples of how to write very nice applets which rivals Flash - link is here.

Also, I see that Microsoft is coming with similar competition to Flash called SilverLight. The installation is only 4 Mbytes.

Monday, November 10, 2008

Too Many Open Cursors

Occasionally, you may find your program encountering "Too Many Open Cursor" when using Oracle.

You may be able to resolve this by:
  1. Closing any open resultset.
  2. Dispose any database adapter after use.
  3. Disposing your database command object after use (either oleDBCommand or OracleCommand) whether or not it is a prepared statement or not.

What sometimes happen is after you create a database command within your subroutine and then you perform a query a cursor is opened. We sometimes assume that when we exit the routine, the GC (garbage collector) will automatically close the open cursors and dispose of the objects that are no longer used. However, I found out that it does not always happen. In an environment where you perform a lot of query or update, the system may not know that it can reuse the existing open cursor and will open new cursors during each iteration or call and sooner or later will hit the limit.

This applies whether you are using .Net framework or Java.

I have found this has helped me avoid the "Too Many Open Cursors" issue in both environment.

Saturday, November 8, 2008

Compiling invalid Oracle Object

If you use a lot of packages, stored functions and procedures, they may sometimes become invalid if an object it depends on is changed. We have encountered in a few occasions, our application will hang as a result. To address this, we are using a script (shown below) and schedule it to run once an hour to recompile any invalid objects it encounters.

This is a script is obtained from another blog but was modified so that it will continue to compile the rest even if it encounters error (in case, there are procedures that are in the schema that is still being edited and will not compile correctly, so other invalid object (but compilable) will still get compiled ).
rem -----------------------------------------------------------------------
rem Filename:   cmpall2.sql
rem Purpose:    Compile all invalid database objects
rem             Run this script after each database upgrade or whenever
rem             required.
rem Notes:      If the dependencies between objects are complicated, you can
rem             end up re-compiling it many times, as recompiling some
rem             invalidates others. This script uses dyamic SQL, and
rem             recompile objects based on reverse dependency order.
rem Date:       24-Sep-1998
rem Author:     Fiona Lindsay <f_lindsay@hotmail.com>
rem -----------------------------------------------------------------------

set serveroutput on size 1000000

declare
   sql_statement varchar2(200);
   cursor_id     number;
   ret_val       number;
begin

   dbms_output.put_line(chr(0));
   dbms_output.put_line('Re-compilation of Invalid Objects');
   dbms_output.put_line('---------------------------------');
   dbms_output.put_line(chr(0));

   for invalid in (select object_type, object_name
                   from   user_objects o
                   where  o.status       = 'INVALID'
                     and  o.object_type in ('PACKAGE', 'PACKAGE BODY',
                                            'FUNCTION',
                                            'PROCEDURE', 'TRIGGER',
                                            'VIEW')
                   order  by o.object_type) loop

      begin
       if invalid.object_type = 'PACKAGE BODY' then
          sql_statement := 'alter package '||invalid.object_name||
                           ' compile body';
       else
          sql_statement := 'alter '||invalid.object_type||' '||
                           invalid.object_name||' compile';
       end if;
 
       /* now parse and execute the alter table statement */
       cursor_id := dbms_sql.open_cursor;
       dbms_sql.parse(cursor_id, sql_statement, dbms_sql.native);
       ret_val := dbms_sql.execute(cursor_id);
       dbms_sql.close_cursor(cursor_id);
 
       dbms_output.put_line(rpad(initcap(invalid.object_type)||' '||
                                invalid.object_name, 32)||' : compiled');
      EXCEPTION
          When others then
             dbms_output.put_line('Error in '|| invalid.object_name || ' - Continuing');
      end;
   end loop;

end;
/
EXIT

To use the above script, just copy into a text file and save it as comp_obj.sql. You can then use a batch file to run it as follows:

sqlplus scott/tiger@orcl @comp_obj >> comp_obj.log

Wednesday, November 5, 2008

Review Google Chrome Browser

I realize that there are many reviews of Chrome already but I am approaching this as a programmer blogger.

Things I like about Chrome:
  • It is fast.
  • In some cases where I get timeout with either IE or Firefox, the page still gets rendered in Chrome.
  • The incognito page is often handy when you want to do research and don't want to leave any trail on the PC of where you have been.
  • You can open a set of book marks in a new window (similar functionality available in firefox).
Things I don't like about Chrome:

  • When you perform compose in Blogger, it encloses the subsequent paragraph between <div> </div> tag. Both IE and Firefox does not add these extra tags.
  • It does not have an option to undo close tab which you may have accidentally closed (this is available in FireFox - haven't noticed it in IE7 - not sure if it is there or not).
  • If you want to have properly aligned table, you can enter your information into Excel and then paste in into the compose screen in Firefox and the data will align correctly. See examples below.
  • Assuming you have your photos in Flickr, you can switch to view source and search for <img src="http://farm When you do that in Firefox, it will highlight and select the text for you. In Chrome, the text is selected but not highlighted. (I do this because I upload my images to flickr and then display them in my blogs).
Pasting from Excel into Blogger compose screen:

This is how the excel looks like:


Using Firefox:

Date Mileage time
1-Jan-08 10 1 hr
2-Jan-08 5 0.5 hr



Using IE:

Date
Mileage
time
1-Jan-08
10
1 hr
2-Jan-08
5
0.5 hr

Using Chrome:


Date Mileage time 1-Jan-08 10 1 hr 2-Jan-08 5 0.5 hr

Friday, October 31, 2008

Consuming Webservice using MSSoap

Even though majority of the programmers have started using vb.net, there may be cases where you have a legacy system that still needs to consume webservice. This is how to do it with MSSoap.

First thing to be aware of is you may encounter issue if the webservice returns XML, so have it return a string instead. This is easily done by using a wrapper function. For example, if you have a webmethod say getLotData, just create a getLotDataStr as follows:

<WebMethod()> _
Public Function getLotData(ByVal pLotID As String) As XmlDocument
....
End Function

...

<WebMethod()>
Public Function getLotDataStr(ByVal pLotID As String) As String
Dim tmpStr As String = ""
Dim xDoc As XmlDocument

xDoc = getLotData(pLotID)
tmpStr = xDoc.OuterXml

Return tmpStr
End Function


To consume the webservice, in vbscript or VB, you then use the MSSoap client as follows:

Dim wsClient As New MSSOAPLib30.SoapClient30
Dim urlStr as String = "http://www.blogger.com/wservice.asmx?WSDL"
Dim sReply, lotID As String
Dim xDoc As DOMDocument30

lotID = ...
wsClient.MSSoapInit urlStr
sReply = wsClient.getLotDataStr(lotID)

xDoc.loadXML sReply

...


Once the information is loaded to the XmlDocument, you can then access the information by using the getElementsByTagName. You can also access the inner text by access the Text Property.

E.g.

Dim lotNode As IXMLDOMNode
Dim processStep As String

Set lotNode = xDoc.getElementByTagName("processID")
ProcessStep = lotNode.Text


'If it is an attribute then access it by the Attributes as in
ProcessStep = lotNode.Attributes.getNameItem("Name").Text


You can also use the DomDocument30 data type to create the parameters if the Webservice requires a XML as a parameter.

Wednesday, October 29, 2008

Creating properly aligned content in Blogger

The easiest way to create a properly aligned information in Blogger is:
  1. Open excel and place the data in table as shown below.
  2. Copy and then paste into the compose screen (this only works if you are using FireFox) and there you have it, your data is aligned correct (as shown below).

This is how the excel looks like:


Using Firefox:

Date Mileage time
1-Jan-08 10 1 hr
2-Jan-08 5 0.5 hr

Saturday, October 25, 2008

Dynamically loading UserControl in ASP.Net

I gathered information from the following sources to come up with the following:

4 Guys from Rolla
ASP.Net
CodeProject

First the UserControl:

This is how it looks like in VS Designer:


The code is as follows:

<%@ Control Language="vb" AutoEventWireup="false" Codebehind="ucAckFrm.ascx.vb" Inherits="testWeb.ucAckFrm" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %>
<asp:panel id="PaneCtrl" runat="server">
<asp:Label id="lblCtrlName" runat="server"></asp:Label>
<BR>
<asp:Panel id="paneDisp" runat="server">
<asp:Label id="lblMsg" runat="server"></asp:Label><BR><BR>
<asp:Button id="btnAck" runat="server" Text="Acknowledge"></asp:Button>
<asp:Button id="btnReject" runat="server" Text="Reject"></asp:Button>
<asp:TextBox id="txtReason" runat="server"></asp:TextBox><BR>
<asp:Label id="lblErrMsg" runat="server" ForeColor="Red"></asp:Label>
<HR>
</asp:Panel>
</asp:panel>


The code behind is as follows:

Public Class ucAckFrm
Inherits System.Web.UI.UserControl

#Region " Web Form Designer Generated Code "

'This call is required by the Web Form Designer.
Private Sub InitializeComponent()

End Sub
Protected WithEvents lblMsg As System.Web.UI.WebControls.Label
Protected WithEvents btnAck As System.Web.UI.WebControls.Button
Protected WithEvents btnReject As System.Web.UI.WebControls.Button
Protected WithEvents txtReason As System.Web.UI.WebControls.TextBox
Protected WithEvents lblCtrlName As System.Web.UI.WebControls.Label
Protected WithEvents lblErrMsg As System.Web.UI.WebControls.Label
Protected WithEvents PaneCtrl As System.Web.UI.WebControls.Panel
Protected WithEvents paneDisp As System.Web.UI.WebControls.Panel

'NOTE: The following placeholder declaration is required by the Web Form Designer.
'Do not delete or move it.
Private designerPlaceholderDeclaration As System.Object

Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
'CODEGEN: This method call is required by the Web Form Designer
'Do not modify it using the code editor.
InitializeComponent()
End Sub

#End Region

Public Event AckClick As EventHandler
Public Event RjctClick As EventHandler

Public Property RjctReason() As String
Get
Return txtReason.Text
End Get
Set(ByVal Value As String)
txtReason.Text = Value
End Set
End Property

Public Sub setMainPane(ByVal pVisible As Boolean)
PaneCtrl.Visible = pVisible
End Sub

Public Sub setDispPane(ByVal pVisible As Boolean)
paneDisp.Visible = pVisible
End Sub

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
lblCtrlName.Text = Me.ID
End Sub

Private Sub btnAck_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAck.Click
RaiseEvent AckClick(Me, e)
End Sub


Private Sub btnReject_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnReject.Click
Dim tmpStr As String

tmpStr = Trim(txtReason.Text)

If tmpStr.Length > 0 Then
RaiseEvent RjctClick(Me, e)
Else
lblErrMsg.Text = "Reject Reason cannot be blank"
End If
End Sub
End Class


The main aspx page:


The code:

<%@ Register TagPrefix="uc1" TagName="ucAckFrm" Src="ucAckFrm.ascx" %>
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="TestPage.aspx.vb" Inherits="testWeb.WebForm1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<title>WebForm1</title>
<meta content="Microsoft Visual Studio .NET 7.1" name="GENERATOR">
<meta content="Visual Basic .NET 7.1" name="CODE_LANGUAGE">
<meta content="JavaScript" name="vs_defaultClientScript">
<meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema">
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<asp:label id="lblMsg" runat="server"></asp:label><br>
<asp:textbox id="txtMsg" runat="server"></asp:textbox><br>
<br>
<asp:button id="btnSubmit" runat="server" Text="Submit"></asp:button><br>
<asp:Label id="lblErrMsg" runat="server" ForeColor="Red"></asp:Label><br>
<hr>
<br>
<uc1:ucAckFrm id="DefAck" runat="server"></uc1:ucAckFrm><br>
<asp:PlaceHolder id="pHolder" runat="server"></asp:PlaceHolder></form>
</body>
</HTML>


The code behind:

Public Class WebForm1
Inherits System.Web.UI.Page

#Region " Web Form Designer Generated Code "

'This call is required by the Web Form Designer.
Private Sub InitializeComponent()

End Sub
Protected WithEvents lblMsg As System.Web.UI.WebControls.Label
Protected WithEvents txtMsg As System.Web.UI.WebControls.TextBox
Protected WithEvents btnSubmit As System.Web.UI.WebControls.Button
Protected WithEvents defAck As ucAckFrm
Protected WithEvents pHolder As System.Web.UI.WebControls.PlaceHolder
Protected WithEvents lblErrMsg As System.Web.UI.WebControls.Label

'NOTE: The following placeholder declaration is required by the Web Form Designer.
'Do not delete or move it.
Private designerPlaceholderDeclaration As System.Object

Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
'CODEGEN: This method call is required by the Web Form Designer
'Do not modify it using the code editor.
InitializeComponent()
End Sub

#End Region

Dim vStr As String
Dim ackArray As New ArrayList

Private Sub PageInit2(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
Dim ctrlCnt, tmpStr As String
Dim loopCnt, ackCnt As Integer
Dim vAckCtrl As ucAckFrm
Dim ctrl As Control

ctrlCnt = Request.Item("opt")
vStr = ctrlCnt

If IsNumeric(vStr) Then
ackCnt = CInt(vStr)
If ackCnt > 0 Then
For loopCnt = 0 To (ackCnt - 1)
ctrl = LoadControl("ucAckFrm.ascx")
tmpStr = "Ack_" & loopCnt
ctrl.ID = tmpStr
vAckCtrl = CType(ctrl, ucAckFrm)
AddHandler vAckCtrl.AckClick, AddressOf AckHandlerAck
AddHandler vAckCtrl.RjctClick, AddressOf AckHandlerRjct

ackArray.Add(vAckCtrl)
pHolder.Controls.Add(ctrl)
Next
End If
End If

AddHandler defAck.AckClick, AddressOf AckHandlerAck
AddHandler defAck.RjctClick, AddressOf AckHandlerRjct

End Sub


Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
If IsPostBack = False Then
Try
lblMsg.Text += "ctrlCnt value is " & vStr
Catch ex As Exception
lblErrMsg.Text += "<br>" & ex.Message
End Try

End If

End Sub


Private Sub AckHandlerAck(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim idName As String

idName = UCase(CType(sender, Control).ID)

lblMsg.Text += "<br>Acknowledge by " & idName
End Sub

Private Sub AckHandlerRjct(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim idName, reason As String
Dim handledCtrl As ucAckFrm

idName = UCase(CType(sender, Control).ID)
handledCtrl = CType(sender, ucAckFrm)
reason = handledCtrl.RjctReason

lblMsg.Text += "<br>Rejected by " & idName & " due to " & reason
End Sub
End Class


In this example, to simplify testing, I use the url parameter opt to determine how many times I want to load the usercontrol. In the following example, I loaded it twice - i.e. ?opt=2


Key points I learn from the articles are:
  • Init will be called each time the page is refreshed.
  • View State is loaded after the init phase before the page load phase.
  • To load the control, we actually use the .ascx file using Page.LoadControl
  • After loading the control, we need to use CType to change back to the class defined in the codebehind for the usercontrol so we can access the properties, events etc for the user control.
Other Notes on this example:
  • In the example above, I assigned the ID for each of the controls that are dynamically loaded, this is actually for a project I am working on where the requestor will specify how many persons will be informed of a request made. Each person receiving the request will then have an option to either acknowledge the request or reject it. In that situation, I will then name each of the controls with the role for each receipient.
  • I added the mainPane and dispPane to provide an option if to either hide the whole control or to display on the header (but not used in the example above).
  • I use a try catch and throw to practice Error handling I wrote in my previous article.

Wednesday, October 22, 2008

Consuming WebService in Lotus NOTES

I found it difficult to find reference on how to consume webservice in Lotus NOTES. That is why I have this as a bookmark to help me get back to those locations:

Consuming webservice in LN 6


Another good one that shows a simple client side method is found here (snippet as follows):
Dim Client As Variant
Set Client = CreateObject("MSSOAP.SoapClient")
'Initialize connection to the Web Service
Call Client.mssoapinit
("http://localhost/testWS.nsf/Simple?wsdl")
'Call our simple GetEmailAddress
function provided by Web service
Dim result As String
result = Client.getJoke()
'output result to message box
Messagebox result, 48, "Get Joke"


There is also an open source project which produces a java stub to consume the webservice called stubby, however, still not sure whether is meets my counterparts requirements.

In LN 8, it is much simpler to consume the webservice, just have to create a new script - either JavaScript, LotusScript or Java Library and there is a button at the bottom labeled WSDL, just click it and import WSDL to create the required reference to consume the webservice.

There is also another reference on how to write do some of the coding in java - found here. Another reference talking about webservice in LN 8.5 is found here.

Monday, October 20, 2008

Lotus NOTES programming

If you are curious about programming in Lotus NOTES, IBM provides an excellent resource here. The item I found most useful is the reference entitle Lotus Script Language Guide. Unfortunately, there is only one version of the reference book available (i.e. version 7.x).

When I first started on Lotus NOTES (4.x), they only provided formula for programming. Since version 5.x, the introduced JavaScript and LotusScript. If you are from MS environment, you will probably be more comfortable with LotusScript, since the syntax is identical to VBScript used for macros of all the MSOffice products.

If you prefer, you can either program in Java or JavaScript which is also available within Lotus NOTES. So just as you can program in Oracle using PL/SQL or using Java. The preference is yours.

Wednesday, October 15, 2008

Oracle 10G migration Issues - not group by

When we migrated from Oracle 9i to 10G, we encountered a few issues. One of the strangest one is as follows.

The following query works fine in 9i but when we execute in 10G, we encounter the following error:

ORA-00979: not a GROUP BY expression

The SQL statement (changes have been made on the column names and table names to protect the innocent code) is as follows:

SELECT TO_CHAR(Time_Date, 'yyyymmdd') Time_Date ,
Def_Code ,
StepName ,
ScrapReason ,
StartQty ,
ScrapQty ,
BonusQty ,
ROUND(DECODE(StartQty,0,0,(ScrapQty-BonusQty)/StartQty*100), 2) DefectPercentage,
0 Normalised
FROM
(SELECT Time_Date,
Def_Code ,
StepName ,
ScrapReason ,
ScrapQty ,
BonusQty ,
(SELECT SUM(YLDPACKAGE.GET_OPSTEPSTARTQTY(StepName,StartQty,ScrapQty,BonusQty))
FROM yld_summary
WHERE Time_Date = d.Time_Date
AND RType = 'ASSEMBLY'
AND (Owner LIKE 'SPECIALBUILD%'
AND (Category = 'PRODUCTION'
OR Category = 'BETA' ) )
AND (PRODUCTNAME(WafID,Owner) = 'VISTA')
AND YLDPACKAGE.GET_STEPGRP(StepName) = d.StepName
) StartQty
FROM
(SELECT Time_Date ,
Def_Code ,
YLDPACKAGE.GET_STEPGRP(StepName) StepName ,
REPLACE(ScrapReason, '_RI','') ScrapReason,
SUM(ScrapQty) ScrapQty ,
SUM(BonusQty) BonusQty
FROM yld_dtl
WHERE Time_Date BETWEEN to_date('20081007', 'yyyymmdd') AND to_date('20081014', 'yyyymmdd')
AND RType = 'ASSEMBLY'
AND (Owner LIKE 'SPECIALBUILD%'
AND (Category = 'PRODUCTION'
OR Category = 'BETA' ) )
AND (PRODUCTNAME(WafID,Owner) = 'VISTA')
AND YLDPACKAGE.GET_STEPGRP(StepName) = 'LAPPING'
AND ScrapReason = 'Scratches'
GROUP BY Time_Date ,
Def_Code ,
YLDPACKAGE.GET_STEPGRP(StepName),
REPLACE(ScrapReason, '_RI','')
) d
) order by time_date


The work around is to just add another column in the group by (highlighted below in red):

SELECT TO_CHAR(Time_Date, 'yyyymmdd') Time_Date ,
Def_Code ,
StepName ,
ScrapReason ,
StartQty ,
ScrapQty ,
BonusQty ,
ROUND(DECODE(StartQty,0,0,(ScrapQty-BonusQty)/StartQty*100), 2) DefectPercentage,
0 Normalised
FROM
(SELECT Time_Date,
Def_Code ,
StepName ,
ScrapReason ,
ScrapQty ,
BonusQty ,
(SELECT SUM(YLDPACKAGE.GET_OPSTEPSTARTQTY(StepName,StartQty,ScrapQty,BonusQty))
FROM yld_summary
WHERE Time_Date = d.Time_Date
AND RType = 'ASSEMBLY'
AND (Owner LIKE 'SPECIALBUILD%'
AND (Category = 'PRODUCTION'
OR Category = 'BETA' ) )
AND (PRODUCTNAME(WafID,Owner) = 'VISTA')
AND YLDPACKAGE.GET_STEPGRP(StepName) = d.StepName
) StartQty
FROM
(SELECT Time_Date ,
Def_Code ,
YLDPACKAGE.GET_STEPGRP(StepName) StepName ,
REPLACE(ScrapReason, '_RI','') ScrapReason,
SUM(ScrapQty) ScrapQty ,
SUM(BonusQty) BonusQty
FROM yld_dtl
WHERE Time_Date BETWEEN to_date('20081007', 'yyyymmdd') AND to_date('20081014', 'yyyymmdd')
AND RType = 'ASSEMBLY'
AND (Owner LIKE 'SPECIALBUILD%'
AND (Category = 'PRODUCTION'
OR Category = 'BETA' ) )
AND (PRODUCTNAME(WafID,Owner) = 'VISTA')
AND YLDPACKAGE.GET_STEPGRP(StepName) = 'LAPPING'
AND ScrapReason = 'Scratches'
GROUP BY Time_Date ,
Def_Code ,
YLDPACKAGE.GET_STEPGRP(StepName),
StepName ,
REPLACE(ScrapReason, '_RI','')
) d
) order by time_date



Nov 23, 2008

Found the above code did not product correct output, had to rewrite it as follows:

 SELECT TO_CHAR(Time_Date, 'yyyymmdd') Time_Date                                   ,
Def_Code ,
StepName ,
ScrapReason ,
StartQty ,
ScrapQty ,
BonusQty ,
ROUND(DECODE(StartQty,0,0,(ScrapQty-BonusQty)/StartQty*100), 2) DefectPercentage,
0 Normalised
FROM
(SELECT Time_Date,
Def_Code ,
StepName ,
ScrapReason ,
ScrapQty ,
BonusQty ,
(SELECT SUM(YLDPACKAGE.GET_OPSTEPSTARTQTY(StepName,StartQty,ScrapQty,BonusQty)),
YLDPACKAGE.GET_STEPGRP(StepName) step
FROM yld_summary
WHERE Time_Date = d.Time_Date
AND RType = 'ASSEMBLY'
AND (Owner LIKE 'SPECIALBUILD%'
AND (Category = 'PRODUCTION'
OR Category = 'BETA' ) )
AND (PRODUCTNAME(WafID,Owner) = 'VISTA')
group by YLDPACKAGE.GET_STEPGRP(StepName)) l,
(SELECT Time_Date ,
Def_Code ,
YLDPACKAGE.GET_STEPGRP(StepName) StepName ,
REPLACE(ScrapReason, '_RI','') ScrapReason,
SUM(ScrapQty) ScrapQty ,
SUM(BonusQty) BonusQty
FROM yld_dtl
WHERE Time_Date BETWEEN to_date('20081007', 'yyyymmdd') AND to_date('20081014', 'yyyymmdd')
AND RType = 'ASSEMBLY'
AND (Owner LIKE 'SPECIALBUILD%'
AND (Category = 'PRODUCTION'
OR Category = 'BETA' ) )
AND (PRODUCTNAME(WafID,Owner) = 'VISTA')
AND YLDPACKAGE.GET_STEPGRP(StepName) = 'LAPPING'
AND ScrapReason = 'Scratches'
GROUP BY Time_Date ,
Def_Code ,
YLDPACKAGE.GET_STEPGRP(StepName),
StepName ,
REPLACE(ScrapReason, '_RI','')
) d where l.step = d.stepname
) order by time_date

Sunday, October 12, 2008

Installing Framework 1.1 in Vista (if error)

Found the article from here.
Install Microsoft .NET Framework 1.1 on Windows Vista - Fix RegSvcs.exe Fails Error

Windows Vista comes packaged with Microsoft .NET Framework 2.0 and 3.0, but not version 1.1, which may still required by some applications such as QuickBooks 7, GFI Report Packs, VS.NET 2003, InstallShield 11.5, and many other developed software. However, when trying to install Microsoft .NET Framework 1.1 (dotnetfx.exe) on Windows Vista, one or more error messages similar to one of the following text will appear, and stop or prevent setup installation of .NET Framework 1.1 from continuing successfully.

RegSvcs.exe - Common Language Runtime Debugging Services

Application has generated an exception that could not be handled.
Process id=0xe50 (3664), Thread id=0xd44 (3396).

Click OK to terminate the application.
Click CANCEL to debug the application.

In Event Viewer of Vista, the following log or similar can be seen:

Source: MsiInstaller
Event ID: 1030
Description:
Product: Microsoft .NET Framework 1.1. The application tried to install a more recent version of the protected Windows file C:\Windows\Microsoft.NET\Framework\sbs_wminet_utils.dll. You may need to update your operating system for this application to work correctly. (Package Version: 1.0.0.0, Operating System Protected Version: 1.0.0.0).

Source: MsiInstaller
Event ID: 1030
Description:
Product: Microsoft .NET Framework 1.1. The application tried to install a more recent version of the protected Windows file C:\Windows\Microsoft.NET\Framework\sbs_system.enterpriseservices.dll. You may need to update your operating system for this application to work correctly. (Package Version: 1.0.0.0, Operating System Protected Version: 1.0.0.0).

The error pops up in the end of .NET Framework 1.1 setup when it’s almost finished, and is registering “System.EnterpriseServices.dll” when the error occurs, time remaining for the installatin process shows 0 seconds.

Why exactly the error occurs when installing Microsoft .NET Framework 1.1 in Windows Vista is not known, as the issue happens randomly. It may affect both 32-bit and 64-bit Vista system, but not all system will have the issue. But one thing is for sure. Microsoft .NET Framework 1.1 SP1 (Service Pack 1) has resolved the issue. However, Microsoft does not provide download to standalone .NET Framework 1.1 installer that integrated with SP1.

Thus, the workaround to fix the Microsoft.NET Framework 1.1. installation error on Vista is to slipstream .NET Framework 1.1 SP1 into .NET Framework 1.1 installer, so that both the .NET Framework and the Service Pack can be installed at the same time.

To slipstream .NET 1.1 SP1 into .NET Framework setup package to have a integrated .NET Framework 1.1 SP1 installer, follow steps in this trick:


1. Create a new folder named DotNet in C:\ drive (C:\DotNet is used in this guide, you can change to any folder you prefer, but ensure that you use correct path in the following steps).
2. Download Microsoft .NET Framework 1.1 Redistributable Package (dotnetfx.exe). Make sure the setup file is saved as dotnetfx.exe.
3. Download Microsoft .NET Framework 1.1 Service Pack 1 (NDP1.1sp1-KB867460-X86.exe). Make sure that the file is renamed and saved as dotnetfxsp1.exe, so that the rest of the steps can be followed easily.
4. Move both installation files into the same directory (i.e. C:\DotNet), if you’re not saving them together.
5. Open command prompt as Administrator.
6. Change to the directory where the two installation setup files for .NET 1.1 are saved (i.e. C:\DotNet).
7. Run the following commands one by one, press Enter after each one.
* dotnetfx.exe /c:"msiexec.exe /a netfx.msi TARGETDIR=C:\DotNet"

If you’re not using C:\DotNet directory, change the target to your path accordingly.

Install Microsoft .NET Framework 1.1 on Windows Vista
Then click on “Yes” when prompted to answer “would you like to install Microsoft .NET Framework 1.1. Package?”

Wait for the uncompression, extraction and setup to complete with the display of the following dialog which says installation complete.
Install Microsoft .NET Framework 1.1 on Windows Vista
* dotnetfxsp1.exe /Xp:C:\DotNet\netfxsp.msp

No status message for this step.
* msiexec.exe /a c:\DotNet\netfx.msi /p c:\DotNet\netfxsp.msp

Install Microsoft .NET Framework 1.1 on Windows Vista
Wait for Windows Installer for Microsoft .NET Framework 1.1 to finish and disappear automatically.
8. Install Microsoft .Net Framework 1.1 with slipstreamed/integrated Service Pack 1 by running netfx.msi created in the working folder.

Wednesday, October 8, 2008

Microsoft is offering three VS 2008 PDF for free download but...

Just found the following in my email today from Microsoft - just click the following link:


After signing up, it allows you to download the three books (from Microsoft Press) in PDF format:

Programming Microsoft LINQ
Introducing Microsoft Silverlight 2
Programming ASP.NET 3.5

I was expecting the whole book but only selected chapters of each book is provided : (

However, Microsoft does provide some other pdf books for free such as:

Composite Application Guidance for WPF (but requires simple registration - just log into hotmail)

Tuesday, October 7, 2008

Decompiler

Recently one of my team member was in the midst of migrating to a new database and was performing some testing and encountered some problem. I told him to go back to the code and make the changes and testing. However, when he did that, he found that the person who previously wrote the code (and left the company) failed to checkin the source code into our SourceSafe repository.

As such, I went to search for decompilers. I came across many articles but the following are the more helpful ones:

Decompiling .Net Assemblies
Decompilers

For .Net, it appears that Reflector is the free solution recommended.

There were a couple of sharewares that did the samething - Spice, Dis# and Salamander.

For Java, you have Jode and Jad. Hmm, where do this people come up with these names :)

Wednesday, October 1, 2008

Connecting to RAC using SQLDeveloper

The following are two definition in tnsnames.ora pointing to the same RAC database. The first one uses name (racdb1), the second (racdb2) uses actual IP address:

racdb1 =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = db1.blogspot.COM)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = db2.blogspot.COM)(PORT = 1521))
(LOAD_BALANCE = yes)
)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = ORCL.BLOGSPOT.COM)
(FAILOVER_MODE =
(TYPE = SELECT)
(METHOD = BASIC)
(RETRIES = 180)
(DELAY = 5)
)
)
)


racdb2 =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.155.1.1)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.155.1.2)(PORT = 1521))
(LOAD_BALANCE = yes)
)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = ORCL.BLOGSPOT.COM)
(FAILOVER_MODE =
(TYPE = SELECT)
(METHOD = BASIC)
(RETRIES = 180)
(DELAY = 5)
)
)
)

If you use SQL Developer to connect to racdb1 using the TNS, you will encounter the following error:

Status: Failure - Test failed: (null).

It works fine if you connect to racdb2. Apparently, it is not able to perform a nslookup to obtain the IP address when using TNS. It works fine if you use advance and use thin client to connect as in:

Strangely, if you use Advanced and put the following string, it works:

jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS = (PROTOCOL = TCP)(HOST = db1.blogspot.COM)(PORT = 1521))(ADDRESS = (PROTOCOL = TCP)(HOST = db2.blogspot.COM)(PORT = 1521))(LOAD_BALANCE = yes))(CONNECT_DATA=(SERVER = DEDICATED)(SERVICE_NAME = ORCL.BLOGSPOT.COM)(FAILOVER_MODE=(TYPE = SELECT)(METHOD = BASIC)(RETRIES = 180)(DELAY = 5))))

Friday, September 26, 2008

AJAX in Framework 3.5

AJAX is now built into Framework 3.5. Using it is quite straight forward:


First add
<asp:ScriptManager runat="server">
</asp:ScriptManager>
within the form tag:

The form tag is normally within the <body> </body> tags:
As in:
<head>
...
</head>
<body>
...
<form id="form1" runat="server">
....
</form>
</body>


Next use put your controls within the Update Panel. All controls within the Update Panel are automatically AJAX. However, make sure to put it within the ContentTemplate!!


<asp:UpdatePanel ID="uPane1" runat="server">
<ContentTemplate>
...
</ContentTemplate>
</asp:UpdatePanel>