Porting by Rewriting the Code in Java


General Steps to Follow when Converting C# to Java

Despite their syntactical similarities, building applications in C# and Java involve radically different processes, and the process of hand conversion is a complex one. Some best practices to consider in doing such a translation are as follows. (You will be translating the scenario using these best practices in the rest of this section.)

  1. Examine the data types that you are using and make sure you have analogous data types in Java. When you don't have them, you'll need to figure out which data type best matches the problem that you are trying to solve. Remember that this can have a knock on effect throughout your code. If you have any custom data classes that you use in your application, you should consider changing them early on in the process too.

  2. Examine the class libraries that you are using. Their namespaces are imported at the top of every module, so you can get a good grip on the family of class libraries that your application will use. You should find the equivalent namespace or namespaces in Java that support the same functionality and then look, reference by reference, for a class within each library that is analogous to the one that is used in the original. You should then plan for how you intend to translate from one to the other. Sometimes it makes sense to 'wrap' the properties, methods and events in the Java class with ones analogous to the .NET class so that you can minimize the changes to your application logic. You would then import your "wrapped" classes instead of the "base" Java ones.

  3. Examine the dependencies that your application relies on. If you do not have access to the source code of these dependencies, you will need to find equivalent ones that your Java applications can use, or you will need to find a technology bridge that allows you to use these dependencies within your Java application should they exist. This includes access to databases. So if for example, your entire system is moving to Linux and your .NET application uses SQL Server, you will have to find an alternative database system and understand which parts of your code will only work with SQL Server and replace them with code that will work with your new database. The process of abstracting this code behind helper classes will assist in this.

  4. Your application has probably grown and evolved over time into what it is now. As such, it might have a lot of spaghetti code and patched functions to meet late requirements, which aren't optimal for its current task. It might include kludges for quick fixes such as goto statements (though not in C#). You should evaluate your code against the requirements that it meets and see what can be streamlined and improved before translating any unnecessary parts.

  5. Understand the nonfunctional requirements such as security and reliability. When translating, you may need to interface to different systems for these. For example, there is no concept of EJB in .NET, but for you to meet your reliability requirements it may be better to throw out a lot of .NET code that gives you the object pooling that EJB gives you by implementing your code as an EJB.

Translating the Data Classes

The previous sections described Item and ItemStatus classes, which were pretty simple, straightforward classes used to represent the Item as ordered and as shipped for the input and output to the Web service, respectively. On the surface, as they are pretty simple one might think that the C# code would probably work in Java without any change. But that would take another look. The .NET way of accessing properties uses get and set accessors. These keywords don't exist in Java, and to get and set a property, there is a private member variable that stores the property value and public functions that are used to get and set that value. Listing 16-6 provides an example of the translation of the Item class into Java.

Listing 16-6. Java Item Class

public class Item {   private String _productNumber;   private int _Quantity;   public String getProductNumber()   {     return _productNumber;   }   public void setProductNumber(String strIn)   {     _productNumber = strIn;   }   public int getQuantity()   {     return _Quantity;   }   public void setQuantity(int nQuantity)   {     _Quantity = nQuantity;   } } 

The changes necessary to translate the ItemStatus classes are very similar.

When developing Web services, a top-down approach is recommended, and XSD files are the starting point for the generation of data structures. This example has already shown the .NET classes to represent these data structures and how to port these classes by translating their source code. However, if one prefers to take an XSD based approach, he or she can use ant and JAXB to generate what he/she needs. Listing 16-7 provides a snippet of an ant script that achieves this.

Listing 16-7. ant Script to Create Classes

<!-- Create stub classes from XSD--> <target name="createJavaClassesFromSchema"> <echo message="--- creating java file of given schema-"/>    <mkdir dir="${xsdclasses}"/>    <exec executable="${wsdp.home}/jaxb/bin/xjc.bat">    <arg value="-d"/>    <arg value="${xsdclasses}"/>    <arg value="${schema.dir}/PurchaseOrder.xsd"/>    <arg value="${schema.dir}/ShippingNotice.xsd"/>    </exec> </target> 

Translating the Web Service

The front door into a Web service in .NET is the ASMX file. When this file is accessed over HTTP, the .NET Framework looks for the underlying assembly, and if it doesn't exist, it compiles the source code and deploys the assembly to the correct location. It then acts as a proxy to the underlying class, specified in the Class= parameter.

<%@ WebService Language="c#" Codebehind="WarehouseService.asmx.cs"  %> 


In Java, Web services operate differently. However they still need a proxy developed. In this case the proxy is an interface that extends java.rmi.remote and is implemented by the underlying class. The code for that defines this interface to the WarehouseCallback service, as in Listing 16-8.

Listing 16-8. WarehouseCallback Interface

import java.rmi.RemoteException; import java.rmi.Remote; public interface WarehouseCallback extends Remote { public ItemStatus[] ShipGoods(Item[] ItemList) throws RemoteException; } 

It is clear that there is no direct analogue, so when porting to Java an interface like this must be developed to each of your Web service classes, and the Web methods (in this case ShipGoods) must be prototyped in this interface.

Using Java Web Services Annotations

The JAX-RPC 2.0 early access [JAXRPC1] offers Web service annotations that are very similar to the C# annotations in the Java community. Along with JAX-RPC 2.0 Web services, the metadata that is submitted as part of the JSR181 will be available in Java EE5.0 [JSR181]. Listing 16-9 gives a brief example of how Java Web services can use these annotations:

Listing 16-9. JAX-RPC 2.0 Annotations

import javax.jws.WebService; import javax.jws.WebMethod; @WebService public class WarehouseCallbackService {  @WebMethod  public String getPOStatus(String poID)  {   // execute business logic here return "Status";  } } 

Translating the Underlying Web Service Class

Defining the Web Service Class

In C# the Web service class is one that derives from the System.Web.Services.WebService class. In Java there is no equivalent, so it's best to design a class to implement the interface class and ServiceLifeCycle, which enables the JAX-RPC runtime to manage the lifecycle of the objects built from this class and connect the application to external resources such as the database. The class can then be defined with

public class WarehouseCallbackImpl implements WarehouseCallback, ServiceLifecycle { … } 


Importing the Required Libraries

The ShipGoods Webmethod was developed for the C#-based Web service connected to an SQL Server database to check the quantities of goods that were available to determine if they could be shipped or not. To do this, the following libraries had to be imported. In C# this is done with the using statement as shown here:

using System.Data; using System.Data.SqlClient; 


When accessing SQL Server from Java, the JDBC drivers for SQL Server first need to be obtained, which are available for download from Microsoft [MSDN1], and the JAR files containing the necessary classes placed into the Classpath. Remember also that these JARs need to be installed on the application server to get the application to run correctly. To use the SQL Server functionality in Java, import the libraries to the project as shown in Listing 16-10:

Listing 16-10. Java Class Library Imports

import com.microsoft.jdbc.*; import com.microsoft.jdbcx.*; import com.microsoft.util.*; import java.sql.*; 

Connecting to the Database

The C# code uses an object of type SqlConnection to manage the connection to the database. It is initialized with a connection string as shown here:

string connectionString = "Data Source=(local);uid=sa;pwd=welcome;database=Warehouse"; SqlConnection con = new SqlConnection(connectionString); 


The Java equivalent is quite similar. When using Java, the class that is used as the driver for SQL Server using the Class.forName command first must be registered. A Connection object (which is analogous to the .NET SqlConnection object) is then initialized by passing this connectionstring to the DriverManager object.

Listing 16-11. JDBC Connection Setup

Class.forName        ("com.microsoft.jdbc.sqlserver.SQLServerDriver"); String connectionString =        "jdbc:microsoft:sqlserver://NYCMLMORO- DEV2:1433;User=sa;Password=welcome;DatabaseName=Warehouse"; Connection conn =        DriverManager.getConnection(connectionString); 

Getting Data from the Database

In C# to execute a query against an SQL server database, a string is used that contains the desired SQL, and an SqlCommand object is created, passing this query and the current connection to its constructor. To read the results of the query, use an SqlDataReader object, to which is passed the results of the ExecuteReader() method of this command. The next step is then to iterate through the rows of results using the SqlDataReader.

SqlCommand com = new SqlCommand(sql,con); SqlDataReader r = com.ExecuteReader(); 


Java is similar, but instead of an SqlCommand, a Statement object is used, which is quite comparable. The Statement is created on a specific connection. You use the Statement object to execute SQL and return a ResultSet using code like the following:

Statement com = conn.createStatement(); ResultSet r = com.executeQuery(sql); 


Iteration through the resultset is done in a similar way to how it's done with SqlDataReader in C#. Do take note, however, that if porting a database, then the business logic residing within the database also needs to be ported. For example, if porting from a Microsoft SQL Server to another database, the database would need to be rearchitected on the new platform, moving all views, relationships, diagrams, stored procedures, and other necessary artifacts. This may not be a trivial task.

Putting It All Together

The full implementation class for the Web service in Java is shown in Listing 16-12. This can be compared to the C# listing earlier in this chapter. While it isn't drastically different and not too difficult to derive if one has a good understanding of both languages, it is certainly not a trivial task to derive this from the original C# code.

Listing 16-12. Java Web Service Source

import java.rmi.RemoteException; import com.microsoft.jdbc.*; import com.microsoft.jdbcx.*; import com.microsoft.util.*; import java.sql.*; public class WarehouseCallbackImpl implements WarehouseCallback, ServiceLifeCycle {   public ItemStatus[] ShipGoods(Item[] ItemList)        throws RemoteException   {     ItemStatus[] collRtn = new ItemStatus[ItemList.length];     try     {       Class.forName           ("com.microsoft.jdbc.sqlserver.SQLServerDriver");       String connectionString = "jdbc:microsoft:sqlserver:";       connectionString+="//SQLSERVER:1433;User=sa; ";       connectionString+="Password=welcome; ";       connectionString+="DatabaseName=Warehouse";       Connection conn =              DriverManager.getConnection(connectionString);       String sql="";       for(int lp=0;lp<ItemList.length;lp++)       {        collRtn[lp] = new ItemStatus();        collRtn[lp].setProductNumber                     (ItemList[lp].getProductNumber());        sql="Select QuantityOnHand from WarehouseStock where";        sql+= " ProductID='" +              ItemList[lp].getProductNumber() + "'";        Statement com = conn.createStatement();        ResultSet r = com.executeQuery(sql);        if(r.next())        {          int nQuantityonHand = r.getInt("QuantityOnHand");          if(nQuantityonHand>ItemList[lp].getQuantity())            collRtn[lp].setStatus(true);          else            collRtn[lp].setStatus(false);        }        else        {          collRtn[lp].setStatus(false);        }       }     }     catch(Exception e)     {       e.printStackTrace();     }     finally{       try {         com.close();         r.close();       } catch (Exception exe){}     }   return collRtn;   }   // Need to be overridden   public void init(Object context)   {   }   public void destroy()   {   } } 

Deploying the Web Service

The next logical step in porting a .NET to Java EE is deploying the application under a designated environment.

In Listing 16-13 are definitions of ant tasks for deploying and undeploying applications under Tomcat:

Listing 16-13. ant Script for Tomcat Deployment

<!-- Tomcat tasks, for deploy and undeploy targets" -->  <taskdef name="deploy"       classname="org.apache.catalina.ant.DeployTask">    <classpath ref/>  </taskdef>  <taskdef name="undeploy"       classname="org.apache.catalina.ant.UndeployTask">    <classpath ref/>  </taskdef> 

The tasks in Listing 16-14 are used to deploy and undeploy a Web application under Tomcat:

Listing 16-14. ant Script for Tomcat Undeployment

<target name="deploy" depends="process-war"   description="Deploys the processed WAR file">   <deploy url="${tomcat.manager.url}"       username="${tomcat.username}"       password="${tomcat.password}"       path="${tomcat.path}"       war="file:${buildhome}/WarehouseService.war"/>  </target>  <target name="undeploy"    description="Undeploys a Web application">    <undeploy url="${tomcat.manager.url}"       username="${tomcat.username}"       password="${tomcat.password}"       path="${tomcat.path}"/>  </target> 

Tomcat doesn't provide a test harness for a Web service, but many other application servers do. Figure 16-5 shows the Web service from this section running on an Oracle Application Server.

Figure 16-5. Warehouse service running on Java EE


Consuming the Web Service

When the C# Web service was consumed, Visual Studio.NET was used to create a proxy class that could then be used within a client application, be it an ASP.NET page or a Windows Form to consume the Web service. Most Java IDEs offer the same functionalityto create a Java Proxy class to the Web service based on its WSDL. Alternatively the WSDL2Java tool [Axis1] from Apache Axis can be used to create this from the command line. This proxy class can then become the basis for communications with the Web service on a servlet, a JSP, or an applet. The .NET client is then ported to the appropriate Java Client (Windows Form to Applet or Application, Web Form to JSP, and so on).

Next Steps

While porting of applications from .NET to Java EE has many advantages, it is a lot of work to go through the source code and translate it line by line to the appropriate Java equivalents. Despite the similarities between the two languages on the surface, this example has demonstrated that a relatively simple set of routines such as the ones used here to query a database and populate an array based on the results of that query still require extensive translation. More complicated routines and methods likely require more extensive changesor indeed complete rewrites. In addition, when coming from a .NET environment, where there is no concept of Java EE, there is no direct translation to allow a build of EJBs that are managed by the container, helping to maximize investment in that container. To take advantage of these, the system might need to be rearchitected, negatively impacting the amount of source code that requires a straight translation. In addition deployment is more of a challenge and requires a thorough understanding if the goal is to do any porting of C# code to Java. Ultimately, it's important to weigh all these factors to adequately scope out the porting effort. While the work to do so is considerable, the advantages that the platform offers are generally worth the investment.




Java EE and. Net Interoperability(c) Integration Strategies, Patterns, and Best Practices
Java EE and .NET Interoperability: Integration Strategies, Patterns, and Best Practices
ISBN: 0131472232
EAN: 2147483647
Year: N/A
Pages: 170

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net