1steve/cse298300/fall02projs/net... · web viewusually in such cases simply storing data in a flat...
Post on 14-Apr-2018
215 Views
Preview:
TRANSCRIPT
1
1.0 Overview
Microsoft has generated a great deal of interest with its newly developed .NET products. With the .NET initiative, Microsoft aims at providing developers the ability to capture the power of the Internet. .NET is a general purpose platform that allows users to design, develop, deploy and use web based applications in a uniform and efficient manner. According to Microsoft, .NET will transcend device boundaries and fully harness the connectivity of the Internet. The obvious competitor to .NET is Sun Microsystemss J2EE development platform. There are some obvious similarities between the two platforms as well as incredible differences in some areas. Our objective here is to research and analyze a few functionalities and modules of .NET and J2EE. The areas we will specifically discuss include programming language capabilities and interactions, data access abilities, security issues, and middleware connectivity and interoperability.
2.0 Introduction
.NET has an interesting history:
1993: In the beginning, there was COM.
1996: Along came Java
1997: COM continued to evolve: MTS, COM+
1998: XML enters the picture
2000: .NET, the latest evolution of COM
Microsoft .NET initiative brings so many great changes that the software developers, architects and the managers are all experiencing a fundamental tectonic shift. It has created difficulties to adapt to in the short run, but aims at enabling the windows developers to build more powerful, more useful software more efficiently in the long run. .NET brings a language agnostic model, a managed execution environment, JIT compilation, and rich library support. Microsofts .NET can be summarized as a brand applied to a wide range of technologies. These technologies include:
The .NET Framework:
For developers, this framework is the most important part of .NET. Though it can be described as a successor to the Windows DNA (Distributed interNet applications Architecture), the process of windows development is greatly simplified because of the presence of the Common Language Runtime (CLR) and the .NET Framework class library.
The CLR defines a common set of semantics that is used by multiple languages. Visual Studio.NET which is Microsofts primary tool for building .NET framework applications supports four CLR-based languages: Visual Basic.NET, C#, Managed C++ and Jscript.NET. There is also some third party demonstrated support for other languages built on CLR like Perl, Python and COBOL. CLR also provides other common services such as Garbage Collection and standard format for Metadata (the managed code is compiled into MSIL, i.e. Microsoft Intermediate Language), and Assemblies (conceptually similar to JAR files) for organizing compiled codes (DLLs or EXEs).
The .NET Framework class library provides the standard code for the common functions. The .NET framework written in any language can use and access system services in a same way. Among the most important technologies that this framework provides include ASP.NET (key feature for building applications that use web services), ADO.NET (next generation ActiveX Data Objects for accessing data stored in relational DBMSs and other formats), Windows Forms (a standard set of classes for building windows GUI in any .NET Framework programming languages), Enterprise Services (standard classes for accessing COM+ services such as transactions and object pooling).
Web Services:
The .NET Framework is the programming model for developing, deploying, and running XML Web services and applications that all underlies .NET. XML Web services are units of code that allow developers to write programs in different languages and use different platforms to communicate through standard Internet protocols like XML, WSDL (Web Services Description Language to define the interfaces to web services), SOAP (Simple Object Access Protocol to invoke the operations in those interfaces) and UDDI (Universal Description, Discovery and Integration to let clients find compatible servers).
3.0 .NET Languages and Java
J2EE offers only Java as the only language for the platform, while .NET platform offers a number of languages for programmers to write code in, namely, C#, VB.NET, Jscript and Managed C++. To state in their words, it is a Language-Agnostic Model. Though the environments take radically different views considering the languages in general, they coalesce nicely when it comes to C# and Java.
The C# language is an object-oriented language that lets the programmers to quickly build a wide range of applications for the Microsoft .NET platform. The goal of C# and the .NET platform is to shorten development time by freeing the developer from worrying about several low level plumbing issues such as memory management, type safety issues, building low level libraries, array bounds checking, etc. thus allowing developers to actually spend their time and energy working on their application and business logic instead. As a Java developer the previous sentence could be described as "a short description of the Java language and platform" if the words C# and the .NET platform were replaced with words Java and the Java platform.
Background
In June 2000, Microsoft announced a new programming language called C# along with the .NET platform. C# is a strongly typed object-oriented language designed to give the optimum blend of simplicity, expressiveness, and performance. As stated earlier, the .NET platform is centered on a Common Language Runtime (similar to the JVM) and a set of libraries, used, by a wide variety of languages, which are able to work together by all compiling to an intermediate language (IL). The C# language was built with the hindsight of many languages, but most notably Java and C++. It was co-authored by Anders Hejlsberg (who is famous for the design of the Delphi language), and Scott Wiltamuth. The most popular tool for creating C# code is Microsofts Visual Studio.NET, though Microsoft also provides a command-line complier with the .NET Framework called csc.exe and there is also another C# complier developed by the open source world.
3.1 Differences and Similarities (C# vs. Java)
A major part of our effort was concentrated on understanding the C# language, its various features and comparing it to java with fallacies and benefits of each. Java and C# are two big languages, and though programmers from both camps find understanding the other easy, there are some subtle differences between the two. As far as the languages are concerned the differences can be divided into 4 parts informally:
There are some features, which are exactly similar in both languages like Runtime environments, Garbage Collection, Interfaces, Strings and Unextendable classes. While some features differ only in small ways or a syntax like Inheritance, Access Modifiers, Reflection. Some features have some major conceptual differences like Nested Classes, Threads, Operator Overloading, Serialization, and Documentation generation. A few features of C# with no counterparts in Java are Deterministic Object Cleanup, Delegates, Boxing, Pointers and Unsafe Code, Pass by Reference. While, there are certain Java features having no look-alikes in C#, like Extensions, Checked Exceptions, Dynamic Class Loading, Anonymous Inner Classes and above all Cross Platform Portability.
We have tried to look into a few areas out of those cited above, and analyzed how C# shape up in those areas, and compare the approaches of both languages when they both provide those features, and analyze the reasons why certain features dont have their counterparts.
3.1.1 Class hierarchy
Both Java and C# have a single rooted class hierarchy where all the classes in C# are subclasses of System.Object and all Java classes are subclasses of java.lang.Object. In both the languages the methods of the Object classes share some similarities (e.g. System.Object's ToString() to java.lang.Object's toString()) and differences (System.Object does not have analogs to wait(), notify() or notifyAll() in java.lang.Object).
3.1.2 Execution environment
Just like Java is typically compiled to Java byte code which then runs in managed execution environment (the Java Virtual Machine) so also is C# code compiled to an Intermediate Language (IL) which then runs in the Common Language Runtime (CLR). Both platforms support native compilation via Just In Time compilers. However, one of the key differences between C#s IL design and Java byte code is that C# code is never interpreted; it is always natively complied, unlike Java where code is interpreted first and then natively compiled. This was just a design decision made to make the translation of IL into native code easier. It changes which instructions are included, what type information is included, and how it is conveyed. Thus the two ILs are different, the IL in C# is more type-neutral. There's no information in the instructions that specifies the type of the arguments. Rather, that is inferred by what's been pushed on the stack. This approach makes the IL more compact because a JIT compiler has that information anyways, so there's no reason to carry it along in the instructions.
3.1.3 Object creation
In Java objects are created on the heap using the new keyword. Most classes in C# are created on the heap by using the new keyword. Also, just as the JVM manages the destruction of objects, so also does the CLR via a Mark and Compact garbage collection algorithm. Additionally, C# also supports stack-based classes called Value-types. In Java, only the primitives are created on the stack. Thus, objects that are used similarly to primitives in a program hang around and wait for garbage collection thus adding overhead to the program, especially if the objects were used briefly and in a single location. To avoid the problem of allocating heap space for such classes and then having to garbage collect them, C# has a mechanism that allows one to specify that objects of a certain class should be stack based (In fact, C#'s built-in types such as int are actually implemented as structs in the runtime library). Unlike classes, value types are always passed by value and are not garbage collected. And arrays of value types contain the actual value type objects, not references to dynamically allocated objects, which yield a savings of both memory and time.
3.1.4 Namespaces
A C# namespace is a way to group a collection of classes similar to Java's package construct. Though both are conceptually same, they are implemented differently. In Java, the package names dictate the directory structure of source files in an application whereas in C# namespaces do not dictate the physical layout of source files in directories only their logical structure. This complete separation between physical packaging and logical naming gives more flexibility to package things together in physical distribution units without forcing the use of a bunch of directories. In the language itself, there are clearly some differences. In Java, the packaging is also the physical structure, and because of this a Java source file has to be in the right directory and can only contain one public type or one public class. In C#, source files can be given any names, can contribute to multiple namespaces and can take multiple public classes. Further, one can write all of the sources in one big file, or can spread them across smaller files. Conceptually, what happens with C# at compilation is that one gives the compiler all of the source files that make up ones project and then it just goes off and figures out what to do.
3.1.5 Access modifiers
C# access modifier
Java access modifier
Private
Private
Public
public
Internal
protected
protected
N/A
internal protected
N/A
The table below shows the mapping between C# access modifiers to that of Java. C#s protected keyword has the same semantics as the C++ version, different than the semantics of the protected keyword in Java. This means that a protected member can only be accessed by member methods in that class or member methods in derived classes but is inaccessible to any other classes. The internal modifier means that the member can be accessed from other classes in the same assembly as the class. The internal protected modifier means that a member can be accessed from classes that are in the same assembly or from derived classes. The default accessibility of a C# field or method when no access modifier is specified is private while in Java it is protected (except that derived classes from outside the package cannot inherit the field).
3.1.6 Reflection
The ability to discover the methods and fields in a class as well as invoke methods in a class at runtime, typically called reflection, is a feature of both Java and C#. The primary difference between reflection in Java versus reflection in C# is that reflection in C# is done at the assembly level while reflection in Java is done at the class level. Since assemblies are typically stored in DLLs, one needs the DLL containing the targeted class to be available in C# while in Java one needs to be able to load the class file for the targeted class. The examples below which enumerate the methods in a specified class should show the difference between reflection in C# and Java.
C#
using System;
using System.Xml;
using System.Reflection;
using System.IO;
class ReflectionSample {
public static void Main( string[] args){
Assembly assembly=null;
Type type=null;
XmlDocument doc=null;
try{
// Load the requested assembly and get the requested type
assembly=Assembly.LoadFrom("C:\\WINNT\\Microsoft.NET\\Framework\\v1.0.2914\\System.XML.dll");
type = assembly.GetType("System.Xml.XmlDocument", true);
//Unfortunately one cannot dynamically instantiate types via the Type object in C#.
doc = Activator.CreateInstance("System.Xml","System.Xml.XmlDocument").Unwrap() as XmlDocument;
if(doc != null)
Console.WriteLine(doc.GetType() + " was created at runtime");
else
Console.WriteLine("Could not dynamically create object at runtime");
}catch(FileNotFoundException){
Console.WriteLine("Could not load Assembly: system.xml.dll");
return;
}catch(TypeLoadException){
Console.WriteLine("Could not load Type: System.Xml.XmlDocument from assembly: system.xml.dll");
return;
}catch(MissingMethodException){
Console.WriteLine("Cannot find default constructor of " + type);
}catch(MemberAccessException){
Console.WriteLine("Could not create new XmlDocument instance");
}
// Get the methods from the type
MethodInfo[] methods = type.GetMethods();
//print the method signatures and parameters
for(int i=0; i < methods.Length; i++){
Console.WriteLine ("{0}", methods[i]);
ParameterInfo[] parameters = methods[i].GetParameters();
for(int j=0; j < parameters.Length; j++){
Console.WriteLine("Parameter:{0}{1}",parameters[j].ParameterType, parameters[j].Name);
}
}//for (int i...)
}
}
Java
import java.lang.reflect.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
class ReflectionTest {
public static void main(String[] args) {
Class c=null;
Document d;
try{ c=DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument().getClass();
d = (Document) c.newInstance();
System.out.println(d + " was created at runtime from its Class object");
}catch(ParserConfigurationException pce){
System.out.println("No document builder exists that can satisfy the requested configuration");
}catch(InstantiationException ie){
System.out.println("Could not create new Document instance");
}catch(IllegalAccessException iae){
System.out.println("Cannot access default constructor of " + c);
}
// Get the methods from the class
Method[] methods = c.getMethods();
//print the method signatures and parameters
for (int i = 0; i < methods.length; i++) {
System.out.println( methods[i]);
Class[] parameters = methods[i].getParameterTypes();
for (int j = 0; j < parameters.length; j++) {
System.out.println("Parameters: " + parameters[j].getName());
}
}
}
}
The above code samples demonstrate that there is slightly more granularity in the C# Reflection API than the Java Reflection API because C# has a ParameterInfo class which contains metadata about the parameters of a Method while Java uses Class objects for that purpose which lose some information such as the name of the parameter. Sometimes there is a need to obtain the metadata of a specific class encapsulated as an object. This object is the java.lang.Class object in Java and the System.Type object in C#. To retrieve this metadata class from an instance of the target class, the getClass() method is used in Java while the GetType() method is used in C#. If the name of the class is known at compile time then one can avoid creating an instance of the class just to obtain the metadata class by doing the following.
C#
Type t = typeof(ArrayList);
Java
Class c = java.util.Arraylist.class; /* Must append ".class" to fullname of class */
3.1.7 Serialization and documentation
Object Persistence also known, as Serialization is the ability to read and write objects via a stream such as a file or network socket. Object Persistence is useful in situations where the state of an object must be retained across invocations of a program. Usually in such cases simply storing data in a flat file is insufficient and using a Database Management System (DBMS) is overkill. Serialization is also useful as a means of transferring the representation of a class in an automatic and fairly seamless manner. Serializable objects in C# are annotated with the Serializable attribute. The NonSerialized attribute is used to annote members of a C# class that should not be serialized by the runtime. Such fields are usually calculated as temporary values that have no meaning when saved. C# provides two formats for serializing classes; either as XML or in a binary format, the former is more readable for users and applications while the latter is more efficient. One can also define custom ways an object is serialized if the standard ways are insufficient by implementing the ISerializable interface. In Java, serializable objects are those that implement the Serializable interface while the transient keyword is used to mark members of a Java class as ones not to be serialized. By default Java supports serializing objects to a binary format but does provide a way of overriding the standard serialization process. Objects that plan to override default serializations can implement methods with the following signatures
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException;
private void writeObject(java.io.ObjectOutputStream stream) throws IOException
Since the above methods are private there is no interface that can be implemented to indicate that a Java class supports custom serialization using readObject and writeObject. For classes that need publicly accessible methods for custom serialization there exists the java.io.Externalizable interface which specifies the readExternal() and writeExternal() for use in customizing how an object is read and written to a stream.
C#
using System;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
[Serializable]
class SerializeTest{
[NonSerialized]
private int x;
private int y;
public SerializeTest(int a, int b){
x = a;
y = b;
}
public override String ToString(){
return "{x=" + x + ", y=" + y + "}";
}
public static void Main(String[] args){
SerializeTest st = new SerializeTest(66, 61);
Console.WriteLine("Before Binary Write := " + st);
Console.WriteLine("\n Writing SerializeTest object to disk");
Stream output = File.Create("serialized.bin");
BinaryFormatter bwrite = new BinaryFormatter();
bwrite.Serialize(output, st);
output.Close();
Console.WriteLine("\n Reading SerializeTest object from disk\n");
Stream input = File.OpenRead("serialized.bin");
BinaryFormatter bread = new BinaryFormatter();
SerializeTest fromdisk = (SerializeTest)bread.Deserialize(input);
input.Close();
/* x will be 0 because it won't be read from disk since non-serialized */
Console.WriteLine("After Binary Read := " + fromdisk);
st = new SerializeTest(19, 99);
Console.WriteLine("\n\nBefore SOAP(XML) Serialization := " + st);
Console.WriteLine("\n Writing SerializeTest object to disk");
output = File.Create("serialized.xml");
SoapFormatter swrite = new SoapFormatter();
swrite.Serialize(output, st);
output.Close();
Console.WriteLine("\n Reading SerializeTest object from disk\n");
input = File.OpenRead("serialized.xml");
SoapFormatter sread = new SoapFormatter();
fromdisk = (SerializeTest)sread.Deserialize(input);
input.Close();
/* x will be 0 because it won't be read from disk since non-serialized */
Console.WriteLine("After SOAP(XML) Serialization := " + fromdisk);
Console.WriteLine("\n\nPrinting XML Representation of Object");
XmlDocument doc = new XmlDocument();
doc.Load("serialized.xml");
Console.WriteLine(doc.OuterXml);
}
}
Java
import java.io.*;
class SerializeTest implements Serializable{
transient int x;
private int y;
public SerializeTest(int a, int b){
x = a;
y = b;
}
public String toString(){
return "{x=" + x + ", y=" + y + "}";
}
public static void main(String[] args) throws Exception{
SerializeTest st = new SerializeTest(66, 61);
System.out.println("Before Write := " + st);
System.out.println("\n Writing SerializeTest object to disk");
FileOutputStream out = new FileOutputStream("serialized.txt");
ObjectOutputStream so = new ObjectOutputStream(out);
so.writeObject(st);
so.flush();
System.out.println("\n Reading SerializeTest object from disk\n");
FileInputStream in = new FileInputStream("serialized.txt");
ObjectInputStream si = new ObjectInputStream(in);
SerializeTest fromdisk = (SerializeTest)si.readObject();
/* x will be 0 because it won't be read from disk since transient */
System.out.println("After Read := " + fromdisk);
}
}
Both C# and Java provide a mechanism for extracting specially formatted comments from source code and placing them in an alternate document. These comments are typically API specifications and are very useful way to provide API documentation to the users of a library. The generated documentation is also useful to share the specifications for an API between designers, developers and QA.
Javadoc is the tool used by Java to extract API documentation from source code. Javadoc generates HTML documentation from the source code comment, an example of which is the Java 2 Platform, Standard Edition API Documentation25 which was all generated using Javadoc. Javadoc can be used to describe information at the package, class, and member and method level. Descriptions of classes and member variables can be provided with the options to add references to other classes, class members and methods. With Javadoc one can document Description of the method, Exceptions thrown by the method, Parameters the method accepts, Return type of the method, Associated methods and members, Indication as to whether the API has been deprecated or not, Version of the API the method was first added. The deprecated information is also used by the compiler, which issues a warning if a call to a method marked with the deprecated tag is encountered during compilation. Javadoc also provides the following information automatically: Inherited API, List of derived classes, List of implementing classes for interfaces, Serialized form of the class, Alphabetical class listing, Package hierarchy in a tree format. Since Javadoc generates HTML documentation, it is valid to use HTML in Javadoc comments. There is support for linking the generated documentation with other generated documentation available over the web. Such linking is useful when one wants readers of the documentation to be able to read the API documentation from the related sources. Below is an example of how Javadoc comments are used.
Java
/**
* Calculates the square of a number.
* @param num the number to calculate.
* @return the square of the number.
* @exception NumberTooBigException this occurs if the square of the number
* is too big to be stored in an int.
*/
public static int square(int num) throws NumberTooBigException{}
C# uses XML as the format for the documentation. The generated documentation is an XML file that contains the metadata specified by the user with very little additional information generated automatically. The entire C# XML documentation tags have an analogous Javadoc construct while the same cannot be said for the Javadoc tags having C# XML documentation analogs. For instance, the default C# XML documentation does not have analogs to Javadoc's @author, @version, or @deprecated tags although such metadata can be generated by reflecting on the assembly, as Microsoft's documentation build process does. One could also create custom tags that are analogous to the Javadoc tags and more but they would be ignored by standard tools used for handling C# XML documentation including Visual Studio.NET. Also of note is that C#'s XML documentation when generated does not contain metadata about the class such as listings of inherited API, derived classes or implementing interfaces.
The primary benefit of an XML format is that the documentation specification can now be used in many different ways. XSLT stylesheets can then be used to convert the generated documentation to ASCII text, HTML, or Postscript files. Also of note is that the generated documentation can be fed to tools that use it for spec verification or other similar tasks. Below is an example of how C# XML documentation is used.
C#
///Calculates the square of a number.
///The number to calculate.
///The square of the number.
///NumberTooBigException - this occurs if the square of the number is too big to be stored in an int. public static int square(int num){}
3.1.8 Deterministic object cleanup
To provide total control of releasing resources used by classes, C# provides the System.IDisposable interface which contains the Dispose() method that can be called by users of the class to release resources (like database or file handles) on completion of whatever task is at hand. Classes that manage resources such as database or file handles benefit from being disposable. Being disposable provides a deterministic way to release these resources when the class is no longer in use, which is not the case with finalizers in Java or C#. It is typical to call the SupressFinalize method of the GC class in the implementation of the Dispose method since it is likely that finalization by the runtime won't be needed since it will be provided explicitly via the Dispose method. C# also provides that via the using keyword releasing the resources used by classes occur in a more deterministic manner via the Dispose method. If a class is disposable, it is best to make usage of the Dispose() method idempotent (i.e. multiple calls to Dispose() have no ill effects) which can be done by providing a flag that is checked within the Dispose() method to see if the class has already been disposed or not. The example below shows a program where a class keeps a file open up until the Dispose() method is called which indicates that the file no longer needs to be open.
C#
using System;
using System.IO;
public class MyClass : IDisposable {
bool disposed = false;
FileStream f;
StreamWriter sw;
private String name;
private int numShowNameCalls = 0;
MyClass(string name){
f = new FileStream("logfile.txt", FileMode.OpenOrCreate);
sw = new StreamWriter(f);
this.name = name;
Console.WriteLine("Created " + name);
}
~MyClass(){
Dispose(false);
}
public void Dispose(){
if(!disposed){
Dispose(true);
}
}
private void Dispose(bool disposing){
lock(this){ /* prevents multiple threads from disposing simultaneously */
/* disposing variable is used to indicate if this method was called from a
* Dispose() call or during finalization. Since finalization order is not
* deterministic, the StreamWriter may be finalized before this object in
* which case, calling Close() on it would be inappropriate so we try to
* avoid that.
*/
if(disposing){
Console.WriteLine("Finalizing " + name);
sw.Close(); /* close file since object is done with */
GC.SuppressFinalize(this);
disposed = true;
}
}
}
public string ShowName(){
if(disposed)
throw new ObjectDisposedException("MyClass");
numShowNameCalls++;
sw.Write("ShowName() Call #" + numShowNameCalls.ToString() + "\n");
return "I am " + name;
}
public static void Main(string[] args){
using (MyClass mc = new MyClass("A MyClass Object")){
for(int i = 0; i < 10; i++){
Console.WriteLine(mc.ShowName());
} //for
}/* runtime calls Dispose on MyClass object once "using" code block is exited, even if exception thrown */
}//Main
}
The above idiom is practically the same as having C++ style destructors without the worry of having to deal with memory allocation woes, making it the best of both worlds. The non-deterministic nature of finalization has long been a hindrance for Java developers. This cleanup is practically the same as having C++ style destructors without the worry of having to deal with memory allocation woes, making it the best of both worlds. Calling the Dispose() method does not request that the object is garbage collected, although it does speed up collection by eliminating the need for finalization.
3.1.9 Delegates
Delegates are a mechanism for providing callback functions. Delegates are similar to the function pointers in C or functions in C++ and are useful in the same kinds of situation. One use of delegates is passing operations to a generic algorithm based on the types being used in the algorithm. Another use of delegates is as a means to register handlers for a particular event (i.e. the publish-subscribe model). To get the same functionality as C# delegates in Java, one can create interfaces that specify the signature of the callback method such as is done with the Comparable interface although this has the drawback of forcing the method to be an instance method when it most likely should be static. To use delegates, one first declares a delegate that has the return type and accepts the same number of parameters as the methods one will want to invoke as callback functions. Secondly one needs to define a method that accepts an instance of the delegate as a parameter. Once this is done, a method that has the same signature as the delegate (i.e. accepts same parameters and returns the same type) can be created and used to initialize an instance of the delegate, which can then be passed to the method that accepts that delegate as a parameter. Note that the same delegate can refer to static and instance methods, even at the same time, since delegates are multicast. The example below shows the process of creating and using an instance delegates.
C#
using System;
//delegate base
public class HasDelegates
{
// delegate declaration, similar to a function pointer declaration
public delegate bool CallbackFunction(string a, int b);
//method that uses the delegate
public bool execCallback(CallbackFunction doCallback, string x, int y)
{
Console.WriteLine("Executing Callback function...");
if (doCallback == null)
throw ArgumentException("Callback can't be null!");
return doCallback(x, y);
}
}
public class FunctionDelegates
{
public static bool FunctionFoo(string a, int b)
{
Console.WriteLine("Foo: {0} {1}", a, b);
return true;
}
}
public class DelegateTest {
public static void Main(string[] args){
HasDelegates MyDel = new HasDelegates();
//create delegate
HasDelegates.CallbackFunction myCallback =
new HasDelegates.CallbackFunction(FunctionDelegates.FunctionFoo);
//pass delegate to delegate function
MyDel.execCallback(myCallback, "Twenty", 20);
}
} // DelegateTest
In the example above the use of a static delegate shields the client programmers from having to know how to instantiate the delegate object.
3.1.10 Boxing
C# contains some pretty interesting innovations that make component development easier, such as its notions of boxing and unboxing. In situations where value types need to be treated as objects, the .NET runtime automatically converts value types to objects by wrapping them within a heap-allocated reference type in a process called boxing, while unboxing allows the value of an object to be converted to a simple value type.
3.1.11 Pointers and unsafe code
Although core C# is like Java in that there is no access to a pointer type that is analogous to pointer types in C and C++, it is possible to have pointer types if the C# code is executing in an unsafe context, with a lot of runtime checking disabled which means that the program must have full trust (granted earlier) on the machine it is running on. This is necessary particularly in situations like interfacing with the underlying operating system, during interactions with COM objects that take structures that contain pointers, when accessing a memory-mapped device or in situations where performance is critical. The syntax and semantics for writing unsafe code is similar to the syntax and semantics for using pointers in C and C++. To write unsafe code, the unsafe keyword must be used to specify the code block as unsafe and the program must be compiled with the /unsafe compiler switch.
While writing unsafe code in C#, one has the ability to do things that aren't typesafe, like operate with pointers. The code, of course, gets marked unsafe, and will absolutely not execute in an untrusted environment. To get it to execute, one has to grant a trust, and if one doesn't, the code just won't run. In that respect, it's no different than other kinds of native code. The real difference is that it's still running within the managed space. The methods one writes still have descriptive tables that tell you which objects are live, so one doesn't have to go across a marshalling boundary whenever one goes into this code. Otherwise, when one goes out to undescriptive, unmanaged code (like through the Java Native Interface, for example), one has to set a watermark or erect a barrier on the stack. One has to remarshall all the arguments out of the box. Also using objects, extra care has to be taken about which ones one touches because the GC (Garbage Collector) is still running on a different thread. It might move the object if one haven't pinned it down correctly by using some obscure method to lock the object.
C# has taken a different approach. It has integrated this into the language. Since garbage collection may relocate managed (i.e. safe) variables during the execution of a program, the fixed keyword is provided so that the address of a managed variable is pinned during the execution of the parts of the program within the fixed block. Without the fixed keyword there would be little purpose in being able to assign a pointer to the address of a managed variable since the runtime may move the variable from that address as part of the mark & compact garbage collection process.
3.2 C# and Interoperability
Apart from the language differences that C# has with Java we also looked into the issues of interoperability in C#. These we have classified into three separate sections namely, Language Interoperability, Platform Interoperability and Standards Interoperability. Since all the languages on the .NET platform have a striking resemblance, we have given Language Interoperability its own separated section where we compare .NET with Java. A part of our studies indicate that while C# scales better in terms of Language interoperability, and Java in Platform Interoperability, they both fail in terms of Standards Interoperability.
3.2.1 Platform Interoperability
Java runs on any platform that has Java VM installed on it. Java code runs as Java Virtual Machine (VT) byte codes that are either interpreted in the VM or JIT compiled, or can be compiled entirely into native code. J2EE works on any platform that has a compliant set of required platform services (EJB container, JMS service, etc.,). All of the specifications that define the J2EE platform are published and reviewed publicly, and numerous vendors offer compliant products and development environments. But J2EE is a single-language platform. Calls from/to objects in other languages are possible through CORBA, but CORBA support is not a ubiquitous part of the platform
C# presently runs only on the Windows platform. C# is implicitly tied into the IL common language runtime, and is run as just in time (JIT) compiled byte codes or compiled entirely into native code. Though the .NET core works on Windows only but theoretically supports development in many languages (once sub-/supersets of these languages have been defined and IL compilers have been created for them). .NET applications, where the code with references to the base class library, and other libraries, are loaded at runtime into the .NET execution engine, are mostly just-in-time (JIT) compiled by the .NET JIT compiler. This compiles the platform independent intermediate language opcodes to native code used by the processor where the JIT compiler is running. Using intermediate code and JIT compilation means that Microsoft has built into the framework the facility to allow .NET applications to be runnable on any operating system that supports the .NET runtime. Moreover, the remoting architecture of .NET assumes that by default objects are passed by value, that is, when an object is passed to another machine, the actual data in the object is passed and a copy of the object is initialized on the remote machine and run there. If the assembly, i.e. the package that contains the objects code, is not present on the remote machine, that machine will download it. This passing around of assemblies works because the code in each assembly is platform independent.
A major selling point of Java technologies is that applications written in Java are portable across a number of operating systems and platforms. Sun officially supports Linux, Windows and Solaris but other vendors have implemented Java on a large range of platforms including OS/2, AIX and MacOS. Binary compatibility across platforms using similar Java versions is common except for situations involving bugs in various VM implementations.
On the contrary, presently, C# is only available on Windows. Hence some of the .NET libraries, particularly the WinForms library that depend on the minute details of the Windows API dont run on other platforms. Efforts are being made to port it to other platforms, including Linux and FreeBSD. Linux porting is being done as part of the Mono project29 developed by Ximian while the FreeBSD implementation is a Microsoft project codenamed rotor.
A majority of the world's desktop computers, and a significant proportion of the world's mobile devices, will, however, support the .NET runtime because these computers and devices run Windows of one flavor or another. Microsoft is committed to providing .NET for all of its 32-bit operating systems as well as its 64-bit operating systems of the future. This reduces the problems that were experienced going from 16-bit Windows to 32-bit Windows, or developing CE Win32 applications when one is used to developing NT Win32 applications. However, portability between the mobile devices and the computers, both running .NET architecture is still limited. A developer still has to pay attention to facilities of the target device (e.g. the limited screen real estate on Windows CE devices makes MDI (multiple document interfaces) applications unusable), but the .NET namespace for Windowing, System.WinForms, makes developing such applications simple. As a part of the solution to this problem .NET makes componentization a requirement: separating business logic into objects separate to the UI 'presentation' code.
However, Microsoft hasn't ignored platform interoperability. The .NET libraries provide extensive capabilities to write HTML/DHTML solutions. For solutions that can be implemented with a HTML/DHTML client, C#/ .NET is a good choice. For cross-platform projects, which require a more complex client interface, Java is a good choice. .NET is Platform Independent to a limited extend. But the platform independence of .NET is different from that of Java. Java programmers who have developed enterprise applications for open distribution have had to use some code of C/C++. Distribution of the programs is one of the major problems that one faces. Though Java claims, "You write once and Run everywhere", that is true but the hidden part of ease of use is totally ignored. Since Java does not produce PE (Portable Exectuable) files; one has to use the Command-Line JITer (java.exe urclass.class) to start-up the program. So there is the option of either creating ugly batch files or interoperate the start-up code into C/C++ to make PE files. So the option one is left with is to interoperate the start-up code into C/C++. This has 2 impacts; firstly one has to still learn C/C++ and secondly using C/C++ makes your code Platform Dependent!
On the .NET Platform files are compiled into PE files (i.e. Dll's and Exe's). These are the formats that one is already used to using. Using the .NET code is no different than using the normal machine specific code and it doesnt require learning any new language to perform certain operations. This makes the files truly Platform Independent at least on the windows platforms.
3.2.2 Standards interoperability
By standards interoperability we mean all the standards like databases systems, graphics libraries, Internet protocols, and object communication standards like COM and CORBA, that the language can access. C# and Java both have restricted interoperability in this respect. Because of Microsofts business motivations and its own role in defining many of these standards, they support some and provide less support for others which compete with their own - for instance - CORBA, competes with COM and OpenGL competes with DirectX. Similarly, Sun's Java doesn't provide as good support for Microsoft standards as it could.
C# objects, since they are implemented as .NET objects, are automatically exposed as COM objects. C# thus has the ability to expose COM objects as well as to use COM objects. This will allow the huge base of COM code to be integrated with C# projects, since NET is a framework, which can eventually replace COM.
However, Microsoft has submitted C# to ECMA with an objective to present C# to the industry as a possible standard and hence gain support in ECMA for a process that will lead to a commonly designed language, which has a common language infrastructure. By a common infrastructure it means, the core set of class libraries that this specification entails, such that if other companies using other platforms implement it, they could reasonably expect to find those classes available to their programs. When and if ECMA actually arrives at a standard for C# and a common language infrastructure, the result will be available under ECMA's copyright and licensing policies, which are truly open. Any customer will be able to license the ECMA C# standard, subset it, superset it without paying royalties. They'll be able take it and implement it on any platform or any device. That is something fundamentally different from SUN and other competitors who approached the standards bodies, with an intention to monopolize their proprietary languages. Thus the .NET platform is sort of an open platform; any vendor can create a compiler for it. All languages can be ported to .NET, (someday someone might even write a Java(tm) compiler for the .NET). On the contrary in Java, one has to depend on Sun to provide the compilers. This limits the chance of open competition between third-party developers.Just like on the native platform, there are compilers for C/ C++ from many vendors like MS, Borland etc., and the choice of choosing the compiler will be in the hands of the developer as the .NET Platform also invites vendors to develop their own compilers. Since third party vendors can create compilers, they are also free to develop third-party tools, so now the Developer will get a better choice of tools to suit his needs.
4.0 J2EE and .NET database connectivity and support
Database connectivity has been a prime area of competition among the two major developers of integrated development environments. Sun Microsystems and Microsoft both have spent great deal of resources in researching ideal way to let developers connect to databases. Today, almost all applications have database backend. These backend are not necessarily compatible with the platform that the application is developed for. It is important for users to get applications that work across various platforms in a unified and flawless manner. In order to achieve this kind of connectivity, Java came up with JDBC and Microsoft has developed ODBC and ADO/ADO.NET. Both, JDBC and ODBC/ADO has a great and long (in terms of Software years) history. Java does not have one extensive system to do perform all data related tasks. It performs different tasks using different modules. Relational database connectivity is done using JDBC, Offline data access and Persistent objects are provided by EJB Entity Beans and there is no support provided for Hierarchical data access. On the other hand, Microsoft has included all of the above usage in one single API called ADO.NET.
Microsoft introduced ODBC as its first database connectivity API in 1992. ADO was introduced later on as an ActiveX Data Object. Microsoft also includes interfaces such as RDO and DAO. ADO was created as a more abstract layer of OLE-DB API. It included connection interfaces, commands and recordsets. The new .NET framework empowers ADO with connection ability to a data source that is in relational, XML or tabular form. The new ADO API is called ADO.NET. ADO.NET acts upon a disconnected database fashion. Since ADO.NET is just an interface, it needs to have an implementation. There are two implementations available.
SQL Server .NET data provider:
This implementation is specific for .NET and only interacts with SQL Server 7.0 and higher. This implementation provides very efficient data access because of its close tie to the operating system and framework.
OLE DB .NET data provider:
This implementation acts on OLE DB concept and thus contains drivers to connect to almost any database. Often, for unfavorable databases, this implementation shows low performance.
JDBC was introduced by Sun Microsystems in 1996. Java was designed to work on various operating systems and JDBC was also designed to work with many different relational databases. JDBC is an interface that needs to have a specific implementation for each database.
1) JDBC uses existing ODBC drivers for connecting to certain databases by using a JDBC-ODBC Bridge. This allows Java to use ODBCs powerful grip on client-server application, but at the same time defeats the goal of having 100% Java applications.
2) There is a similar implementation of JDBC that only provides a wrapper around a C/C++ driver provided by the database. In other words, this implementation provides a Java class that interacts with the existing C/C++ code and uses all C++ methods. This system provides performance such as that of a C/C++ program and at the same time provides interoperability among various databases. For a 3-tiered implementation, this means that only the second tier server has to be properly configured to interact with the C/C++ driver properly.
3) JDBC also provides a 100% Java implemented implementation. This implementation is easy to use, but lacks high performance of a C/C++ driver.
The later version of JDBC also focuses on disconnected database activities. It provides Rowsets, which work similar to ADO.NETs datasets. It has also made changes in the way it executes SQL query. Instead of querying each SQL statement separately, it can send a block of SQL code to the server, analyze it and return several sets of tuples.
4.1 Differences and Similarities
There are many Differences and Similarities between Database access using J2EE or .NET. Our focus in this project is going to concentrate on three major issues relating to database access. Architecture issues are mostly related to the original design of Java vs. Microsofts ability to tie .NET platform with its operating systems closely. Optimization is something that the SQL code writer has to focus on more than ADO.NET and JDBC users. In promoting .NET, Microsoft has given a lot of focus to its new offline database utility called DataSets. DataSets are smaller version of a relational database that lie in developers local machine and can be easily manipulated. Java does not provide a similar functionality through JDBC. This point is going to be beneficial to Microsoft and going to appeal to a lot of developers.
4.1.1 Architecture
ADO.NET follows the architecture shown below:
Figure 4.1ADO.NET Architecture
ADO.NET has streamlined database connectivity issues by putting a middle tier of XML to interact with Web, Windows Apps or B2B applications. XML layer can in turn easily interact with Datasets and Data Adapter to gather data from the actual database. This architecture removes the requirements of needing to use different implementation for different databases, because XML can interact with any data tier.
JDBC follows the architecture shown below:
Figure 4.2JDBC Architecture
JDBC architecture is a little more complicated than that of ADO.NET. It works on the same fundamental design paradigm. It has an application layer which contains separate ResultSets and interacts with the Data tier through a middle level of Data Management. JDBC architecture does not deal with XML for its tier connections. Because of this reason, a special Connection and DriverManger objects have to be involved in making a connection. DriverManager module chooses which driver to use depending on the underlying database. DriverManager will choose an Oracle Driver if the database that the application wants to connect to is an Oracle database. If ODBC has an existing driver for a particular database, DriverManager uses that driver by passing the query through JDBC-ODBC Bridge. It is important to note that JDBC does not provide the same amount of functionality as ADO.NET provides. In order to achieve similar functionality, we have to also analyze and include a separate architecture for EJB with JDBC.
4.1.2 Optimization
JDBC lets you use prepared statements to optimize the code. Prepared statements are regular SQL statements that accept input parameters. In other words, prepared statements prevent from having to recreate and rerun each SQL statement multiple times. Prepared Statement follows a basic template of code shown below:
// Only the execution of the request changes.
PreparedStatement stmt = cnx. Preparedstatement (Select name, first_name from gurus aged WHERE 0
AND CONTAINS (DamageReport, 'Fire WITHIN Cause' ) > 0
AND . . . /* Join Clauses */ [Walter98]
Oracle is planning to come out with the level XML support that will allow XSL sheets to be formed on demand based on user inputs and the resulting XML. Since XML has become a part of Oracles database management systems, it makes a Visual Basic or VC# programmers life much easier and allows him/her to use .NET as a complete development environment. So far, many Visual Studio developers felt a need to always use SQL Server 2000 for their database backend because of the ease of integration, but with the great XML support provided by .NETs development environment and by Oracle, it is possible to use multiple database backends. The following image shows how interoperability can be used with multiple database systems:
As seen in the above image, an application can work with both SQL and Oracle DBs equally well. An application developed in .NET can interact with SQL DB, retrieve information in XML format, process it and save that information in an Oracle DB by converting it in XML format. Added support for XML in ADO.NETs DataSet object makes it easier to save information from a web page to DataSet to an Oracle or SQL DB.
5.0 Security issues concerning .NET and Java
This section will discuss the security issues of .NET and J2EE. There will be detailed discussions of how each platform supports and maintains secure development and execution environments. The main components of these platforms that keep code, data, and systems safe from inadvertent or malicious errors are code based access control, role based access control, code verification and execution, secure communication, and code and data integrity.
Code based access control is giving permissions to code to access resources based on application or protection domain the code is assigned to, and evidences of the code. Role based access control is giving permissions to a user to access resources based on the users role in the system. Code verification and execution means to check the semantics, analyze bytecode, and to keep code execution within a domain. Secure communication is being able to pass data and messages locally or remotely in a secure manner, as to avoiding data modification or other such hacks. Code and data integrity is making sure code hasnt been modified without authorization by using cryptographic solutions and signed distribution files.
5.1 Differences and Similarities
The differences and similarities of the security aspects outline below will be the focus of this section. Both .NET and J2EE have ways of dealing with these issues, sometimes they are implemented the same way, and sometimes they have totally different architectures. An example being .NET uses the Windows SSPI and IIS for secure communications, Java provides JSSE for a more flexible solution.
5.1.1 Code Based Access Control
Code access means the resources that a piece of code can access. Code access control is the security that allows and prevents a piece of code to access resources. The determination of what a piece of code is allowed to do is decided by the origins and intentions of the code. A main component of the CLRs security ability is code access control. Code access control is broken down into evidence based security, permissions, and a security policy. The CLR reviews evidence of an assembly, determines an identity for it, and then looks up and grants permissions based on the security policy for that assembly identity.
Evidence based security is when the CLR examines the assemblies to determine their origins. At runtime the CLR looks at the metadata of the assemblies and finds out where the code originated, the creator of the assembly, and the URL and zone the assembly came from. A zone is a concept in .NET that represents what domain the assembly is from like the Internet, a LAN, or the local machine. The association of the metadata and its corresponding assembly is also verified by the CLR.
Permissions are the result of code access control in the CLR. A permission assigned to a piece of code is the allowance to execute a certain method or access a certain resource. An assembly will request permissions to execute, and theses requests are answered at runtime by the CLR. The CLR will throw a security exception if an assemblys request is denied. Sometimes a request may not be entirely denied, and the CLR will give the assembly lower level permissions than requested. Because there are so many different permissions that can be requested, they are grouped into sets where the permissions of each set have the same level of security and trust. An assembly originated from the Internet zone may be granted an Internet permission set that pertains to untrusted code. Alike permissions are placed into sets based on similar allowances, code is also grouped based on similar requests and zones. The security policy is responsible for code grouping.
There are three security policies in .NET. There is one for the total enterprise, one for the machine executing the code, and one for the requesting user. Any policy file may limit the permissions of another policy file, but cant entirely restrict all permissions. Each security policy groups code into hierarchal categories based on the identity that the CLR determines from the evidence in the metadata. Once the code is grouped and categorized, the security policy can determine permissions for the assembly. The permission decisions are made by the policy that an administrator sets for assemblies and domains. The .NET configuration tool or the Code Access Security Tool (Caspol.exe) can be used to do this.
Code based security in Java is implemented through the JVM, the class loader, and the Security Manager and Access Controller. The JVM is a secure runtime environment. It manages memory by dynamically allocating different areas for use, isolates executing code, and does array bounds checking to name a few things that make the JVM secure. The class loader is actually a hierarchy architecture that which is many instances of class loader objects that load non-essential classes. The root of the hierarchy is the primordial class loader that loads the base classes. The hierarchy is supposed to prevent unauthorized and untrusted code from replacing any code in the base classes.
The Security Manager and Access Controller examine and implement the security policy. Permissions are determined by the security policy at runtime. As with .NET, permissions are granted by the security policy based on evidence. Java looks for a publisher signature and a location origin. Permissions are also grouped into protection domains and associated with groups of classes in Java much the way they are grouped into permission sets and associated with code groups in .NET. Classes are grouped by their origin the same way that code is categorized in .NET by the assemblys zone. J2EE doesnt automatically do code access control like .NET. By default, the Security Manager isnt used so it is up to the user to supply implementation of the Java API classes that perform resource access control. Java only has two security policy levels, one for the executing machine, and one for the user. Each level can expand or restrict on all the permissions of another level, and there can be multiple policy files at each level.
Although its easier to create custom permissions in Java, .NET provides a lot of standard permission sets, all of which would have to be custom made in Java. The evidence that .NET looks at to establish identity to grant permissions is much more informative than the evidence that Java uses, thus the user must have stronger credentials to be granted permissions on the .NET framework. The togetherness of .NET and Windows allows for the greater permission and evidence sets. J2EE does allow more configurable security policy levels, but without all the permission sets that .NET provides, is like having one talented player but it takes the entire team to win the game.
5.1.2 Role Based Access Control
The policies and permissions of code based access control also apply to role based access control, with the difference that the permissions of a policy are now applied to a user or role. A role is some logical grouping of users like administrators and guests. They all have a different set of privileges to do certain operations. .NET uses role based security to establish an identity, also known as authenticating a user, and to give that identity access to resources, known as authorization. .NET applies the term principal to role membership, and permissions of role based security are managed by the PrincipalPermission object. .NET uses many plug-in authentication modules. The standard modules are Windows authentication, Passport authentication, Form authentication, IIS, and impersonation.
Windows authentication is the Windows OS supporting authentication mechanisms used by applications through the Security Support Provider Interface.
Passport authentication is an authentication service owned and provided by Microsoft. Passport is a centralized service that requires only a single logon for members of sites that use it. Its similar to forms authentication, but already implemented by Microsoft.
Form based authentication is from HTTP and is known as cookies. Unauthenticated requests from clients on the web are redirected back to themselves with an HTML logon form. The form requests user logon credentials that will be sent to the application server once submitted. If the request is authenticated, then ASP.NET issues to the client a key to reacquire the clients identity in the form of a cookie.
IIS server contains built in authentication mechanisms like Basic and Digest authentication and X.509 Certificates with SSL. These mechanisms are used to authenticate identities to applications hosted on the IIS server.
Impersonation authentication is supported by .NET on the OS level. Impersonation allows an application to access another application using a different identity while maintaining responsibility to the original user.
J2EE uses Java Authentication and Authorization Service (JAAS) for role based security. JAAS is an integrated package that implements a Java version of the Pluggable Authentication Module (PAM) framework. Using JAAS, developers are allowed to modify and then plug-in authentication modules. JAAS currently supports authentication methods including Unix, JNDI, and Kerberos. JAAS can only provide limited impersonation authentication because the user identity is different for application and OS levels. Java servlets also support authentication through all the HTTP methods (Basic, Digest, and form).
.NET offers more specific control of role based security by supporting role permission checking declaratively and imperatively. Java servlets provide user access checking declaratively at the servlet level, EJBs provide user access checking declaratively down to the method level, and JAAS provides user access checking imperatively within methods. The different J2EE technologies do the same as .NET, but arent as fine grained as .NET based on the environments that the authentication mechanisms are expected to run on. .NET is expected to run on a Windows platform, and IIS is the only supported server of the .NET framework. This expectancy limits the flexibility of .NET to maintain such fine granularity of role based access control on other platforms. IIS also doesnt support application level authentication because authentication is done at the OS level; again limiting flexibility. Passport authentication and authorization requires that users are members of the Microsoft Passport service to access some .NET applications. When Passport has to be shutdown because of a bug or security breach, there isnt any other alternative for authentication, and Microsoft has a copyright preventing any non-Microsoft vendors from producing one.
5.1.3. Secure Code Verification and Execution
Verifying code and executing it within a safe environment is the best way to prevent system weaknesses from being exposed by an application error, malicious or not. .NET and Java both perform security checks during the code execution process. Stack integrity, bytecode structure, buffer overflows, method parameter types, semantics, and accessibility policies are all checked and verified against the executing code. These checks are implemented differently because of the differences in the executing environments. .NET compiles bytecodes into IL and inserts the code checks at certain locations. Java interprets bytecodes and has a bytecode verifier traverse the bytecodes before it goes to the JIT or JVM.
.NET and Java use the concept of a sandbox for code execution. The sandbox is the analogy of a kid that can only play with the objects in the sandbox unless given permission to use objects outside of the sandbox. The sandboxes of .NET and Java are called the Application Domain and Protection Domain, respectively. The Application Domain in .NET applies static boundaries to its execution environment. An application domain will contain all the loaded classes of an assembly, and multiple assemblies may be loaded into the same application domain. There is no way for an object to directly reference another object in a different application domain and must be done remotely. The Protection Domain in Java applies dynamic boundaries to its sandbox. The source of the code and security policy determines what Protection Domain a class is loaded into. Because of the hierarchal class loader structure, an object can access another object in another domain as long as they were both loaded from the same class loader.
The Protection Domain is more flexible as far as what executing code can do, but this flexibility depends on how sophisticated the developer such that there isnt a security breach. The Application Domain supplies a fixed solution to the executing environment, taking this privilege and burden away from the developer.
Another privilege that is also a burden that developers must deal with is unmanaged code. All the security checks to verify code are done on managed code in a managed environment, the CLR and JVM. .NET and Java allow for unmanaged code to bypass the CLR and JRE, respectively. .NET provides a method to access legacy applications and code outside the CLR, and Java provide the JNI to be used.
5.1.4 Secure Communication
Sensitive data across remote communications to the system need to be secure. Secure communications in .NET and J2EE are done at the application level. Both .NET and J2EE support Secure Sockets Layer (SSL) and Transport Layer Security (TLS). These protocols determine what cryptographic algorithm and session keys to be used. .NET applications can use the Windows SSPI, but only as unmanaged code, which as discussed above is a big potential problem. Microsoft wants its users to use IIS. IIS does support SSL and TLS, but it uses files to transfer messages, which isnt the most efficient way. Java provides Java Secure Sockets Extensions (JSSE) for implementing secure communications. JSSE uses SSL and TLS to create a secure connection using sockets (SSLSocketFactory), and can use this connection for remote method invocations (RMI).
JSSE is more configurable and flexible than any .NET solution for creating secure communications. .NET developers have a choice of either running unmanaged code to use the Windows SSPI or use IIS for creating secure communications, and based on the history of attacks on IIS servers, that isnt to safe either.
5.1.5 Secure Code and Data Protection
Systems must uphold code and data reliability and validity to be secure. Code loaded by the system must supply evidence of its source, version signature, and proof that there hasnt been any unauthorized modification; the same is applicable to data. .NET and Java both provide ways of maintaining code and data integrity.
.NET uses and extends the Windows Crypto API to make cryptographic solutions very configurable. Java provides Java Crypto Extensions (JCE) and the Java Crypto Architecture (JCA) for cryptographic functionality. JCE supports key exchange, uses message authentication algorithms (MAC), and makes it easier for developers to implement different encryption algorithms.
Signed distribution files are necessary to verify code and data sources. Certificate management for signing is provided by both .NET and Java. .NET uses strong-named assemblies that include the assembly name and version information. The assembly is signed with an RSA keypair nullifying the chance of unauthorized modification. Including the version information is a benefit towards avoiding DLL conflicts in execution. Javas JAR files are sealed and all the class files that it contains are individually signed. An unsigned class maybe added to a JAR file, but not to a package within a JAR file so private data and methods of a package cant be accessed. JAR manifest files dont require version information as .NET assemblies do, allowing possible DLL conflicts.
Javas cryptographic capabilities are more flexible than .NETs primarily because .NETs encryption functionality is tied to the Windows API which is a problem if the developer is using another OS. Both .NET and Java handle signed distribution files well. The criticism of code and data protection in both .NET and Java is that it isnt difficult to reverse engineer the crypto algorithms and security protocols because both are based on published portable executable standards, i.e. bytecode. It isnt hard to reverse engineer any bytecode, and third party vendors have solutions to fix this, but it isnt a standard for now.
5.2 Example and code discussion
Role based security allows developers to provide resource access to specific users and groups at a fine grained level. The two most important classes for implementing role based security are Identity objects and Principal objects. An identity object signifies a single user and contains the user name and an authenticating security provider. An example of an identity object could be a user logged into a domain authenticated by Windows, Kerberos, or another security provider. A principal object represents the combination of an identity and the roles that identity can assume; such as an administrator or guest.
The identity object contains a name and an authentication type. The name can be a user name or a Windows account name. The authentication type can be any supported logon protocol like Kerebos or some custom value. There are different types of identity objects to allow greater control of users logging into different domain types. The GenericIdentity object is used for custom logons, and the WindowsIdentity object is specialized for Windows authentication. There are other standard authentication modules that .NET uses, and so there are also FormsIdentity and PassportIdentity objects. Access to the name and authentication type attributes of identity objects are provided through the IIdentity interface. The IIdentity interface is implemented by all Identity classes. An application that grants privileges to users that arent in a Windows user group will use identity objects created with the GenericIdentity class or an other specially written class that implements the IIdentity interface.
A common use for role based security is to check if a Windows user has permission to access a certain resource. The WindowsIdentity class is provided for this situation to contain the information about the authenticated Windows user. The following code example demonstrates how user attributes are obtained.
Imports System
Imports System.Security.Principal
Module WindowsIdentityExample
Public Sub Main()
Dim CurIdentity As WindowsIdentity
CurIdentity = WindowsIdentity.GetCurrent()
Console.WriteLine(Authenticated?: &_
CurIdentity.IsAuthenticated)
Console.WriteLine("Authentication: " & _
CurIdentity.AuthenticationType)
CurIdentity.AuthenticationType)End SubEnd Module
The output of this code when executed is similar to this:
Name:
FACULTY\Lester Lipsky
Authenticated?:True
Authentication:Kerberos
Anonymous?:False
Guest?:
False
System?:False
The principal object represents the abilities that a user has in regard to what role can access what resources. It identifies users with associating roles. The abilities to access resources are granted based on the role(s) within a principal object. Like identity objects, .NET provides GenericPrincipal and WindowsPrincipal objects. The IPrincipal interface is used for accessing properties of principal objects like an associated Identity object, and determining if a user is a member of a certain role. All principal classes implement the IPrincipal interface. For instance, the WindowsPrincipal class implements the IPrincipal interface and extra methods to map Windows NT/2000 group memberships to roles. A principal object is associated to an application domain through a call context object, and there is a call context object for every thread in each process.
Security checks can be done against principal objects imperatively or declaratively, or by direct access. Managed code can determine if a certain principal object is a member of a recognized role, has an accepted identity, or is impersonating an identity by acting in a role by imperative or declarative security checks. Values of principal objects can be accessed directly without a PrincipalPermission object. This is done by reading the values of the current threads principal or using the IsInRole method.
The following code example represents a P/Invoke call into the Win32 API LogonUser to switch the identity of the currently executing thread. The identity switch will allow the thread to call into some .NET managed code. Declarative or imperative Identity role checking will be done to prevent unauthorized callers from executing privileged code. When the impersonation is finished and a privileged method is called, a method RevertToSelf() will be used to switch back to the regular identity before the privileged method call. This code example is courtesy of Peter Bromberg at Eggheadcafe.com.
This first class, Impersonation, takes care of the calls to the APIs. This class has one major function: to accept a machine name, an "authorized user" name to check against, and a new username and password to impersonate assuming the passed-in username is correct. In the Test Harness that accompanies the application, the authorized Machine\UserName or DOMAIN\UserName will be set for it to check against in the line, "If mWI1.Name strCurrentUserName Then Return 1". Since this function returns a Windows Identity token, we'll just send back a "1" to show that the call failed on the authorized user check so we can handle it in the calling code. For convenience, the ImpersonationContext object has been stored in the AppDomain (application domain) cache by the token integer converted to a string so that it can be accessed at any time.
Imports SystemImports System.Runtime.InteropServicesImports System.Security.PrincipalImports System.Security.PermissionsImports PABPublic Class Impersonation _Public Shared Function LogonUser(ByVal lpszUsername As String, ByVal lpszDomain As String, ByVal lpszPassword As String, _ByVal dwLogonType As Integer, ByVal dwLogonProvider As Integer, ByRef phToken As Integer) As BooleanEnd Function _Public Shared Function GetLastError() As IntegerEnd FunctionPublic Function ImpersonateNewUser(ByVal strMachineName As String, ByVal strCurrentUserName As String, ByVal strNewUserName As String, ByVal strNewPassword As String) As Integer
' if a DOMAIN account, strMachineName should be the DOMAIN'The Windows NT user token.Dim token1 As Integer'Get the user token for the specified user, machine, and password using the unmanaged LogonUser method.'The parameters for LogonUser are the user name, computer name, password,'Logon type (LOGON32_LOGON_NETWORK_CLEARTEXT), Logon provider (LOGON32_PROVIDER_DEFAULT),'and user token.Dim loggedOn As Boolean = LogonUser(strNewUserName, strMachineName, strNewPassword, 3, 0, token1)Debug.WriteLine("LogonUser called")Debug.WriteLine("LogonUser Success? " + loggedOn.ToString())Debug.WriteLine("NT Token Value: " + token1.ToString())'First lets get the credentials of the actual user that's already in context:Debug.WriteLine("Before impersonation:")Dim mWI1 As WindowsIdentity = WindowsIdentity.GetCurrent()Debug.WriteLine(mWI1.Name)Debug.WriteLine(mWI1.Token)If mWI1.Name strCurrentUserName Then Return 1' Now set our IntPtr (token) to the new user whose token we want to impersonate:Dim token2 As IntPtr = New IntPtr(token1)Debug.WriteLine("New identity created:")' now instantiate the new WindowsIdentity object:Dim mWI2 As WindowsIdentity = New WindowsIdentity(token2)Debug.WriteLine(mWI2.Name)Debug.WriteLine(mWI2.Token)'Now go ahead and Impersonate the new user.Dim mWIC As WindowsImpersonationContext = mWI2.Impersonate()Dim sCurrentImp As String = mWI2.Token.ToString()System.AppDomain.CurrentDomain.SetData(sCurrentImp, mWIC)Debug.WriteLine("After impersonation:")Return mWI2.Token.ToInt32()End Function
Public Function Revert(ByVal sToken As String) As IntegerDim mWI3 As WindowsImpersonationContext = System.AppDomain.CurrentDomain.GetData(sToken)mWI3.Undo()Debug.WriteLine("After impersonation is reverted:")Dim mWIR As WindowsIdentitymWIR = WindowsIdentity.GetCurrent()Debug.WriteLine(mWIR.Name)Debug.WriteLine(mWIR.Token)Dim retval As Integer = mWIR.Token.ToInt32()Return retvalmWIR = NothingEnd FunctionEnd Class
The next class will act as privileged code. A function called Hello will return the name of the Identity that successfully accessed it. The Identity will be checked declaratively using the attribute. The code to imperatively check the Identity is commented out and looks like:
'If Not (MyPrincipal.IsInRole("PETER\DBUser")) Then' Throw New System.Security.SecurityException("Unauthorized User")'End If'If Not (MyPrincipal.Identity.Name.Equals("PETER\tester")) Then' Throw New System.Security.SecurityException("Unauthorized User")'End If
The Principal Policy of the current application domain is set to Windows Principal too.
Imports System.Security.PrincipalImports System.Security.PermissionsImports System.ThreadingPublic Class Testme _Public Function Hello(ByVal strText As String)Dim mWI1 As WindowsIdentity = WindowsIdentity.GetCurrent()Debug.Write(mWI1.Name)Return strText + " was run under permitted user:" & mWI1.Name & vbCrLfEnd FunctionPublic Sub New()'Get the current principal and put it into a principal object.AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal)Dim MyPrincipal As WindowsPrincipal = CType(Thread.CurrentPrincipal, WindowsPrincipal)' NOTE: uncomment following lines and Comment out the ' _' before the "Hello" function to do checks imperatively'If Not (MyPrincipal.IsInRole("PETER\DBUser")) Then' Throw New System.Security.SecurityException("Unauthorized User")'End If'If Not (MyPrincipal.Identity.Name.Equals("PETER\tester")) Then' Throw New System.Security.SecurityException("Unauthorized User")'End IfEnd SubEnd Class
This class is the test harness that will put everything together and generate a form to test out the other classes.
Imports System.Security.PrincipalImports System.Security.PermissionsImports System.ThreadingPublic Class Form1Inherits System.Windows.Forms.FormDim retval2 As IntegerDim retval As IntegerPrivate Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.ClickDim mWI2 As WindowsIdentity = WindowsIdentity.GetCurrent()' show original identity:TextBox1.Text += "Original Identity: " & mWI2.Name & vbCrLf' Impersonate the required user:Dim ip As PAB.Impersonation = New PAB.Impersonation()retval=ip.ImpersonateNewUser(System.Environment.MachineName, TextBox2.Text, TextBox3.Text, TextBox4.Text)If retval = 1 ThenMessageBox.Show("UH-OH, Wrong user running app!")Exit SubEnd If'Show stored impersonation TokenTextBox1.Text += "Impersonation Token: " & retval.ToString()' Make the privileged call...Dim tD As Testme = New Testme()TextBox1.Text += vbCrLf & tD.Hello("Security")tD = Nothing' Revert to original userDim retval2 As Integer = ip.Revert(retval.ToString())TextBox1.Text += vbCrLf & "Reverted: " & retval2.ToString() & vbCrLf' Show current identityTextBox1.Text += "Final Identity: " & mWI2.Nameip = NothingEnd Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.ClickTextBox1.Clear()Dim mWI1 As WindowsIdentity = WindowsIdentity.GetCurrent()TextBox1.Text += "Current Identity: " & mWI1.NameDim tD As Testme = New Testme()TextBox1.Text += vbCrLf & tD.Hello("Security")tD = NothingmWI1 = WindowsIdentity.GetCurrent()TextBox1.Text += "Identity: " & mWI1.NameEnd SubEnd Class
The user information in the example below is native to the machine the code was ran on.
Clicking the NO Security button will cause the code to attemps to instantiate the TestMe class and call its Hello method. This method call will fail, because the security checks will return a Security Exception:
By filling in an Authorized user, a DBUser and password, and click the Security button, then the code calls the impersonation API: ip.ImpersonateNewUser(System.Environment.MachineName, TextBox2.Text, TextBox3.Text, TextBox4.Text) Since the call to the Hello Method is now being run under the required user / role identity, the call will succeed:
6.0 Interoperability
In this section we will talk about the interoperability of .NET architecture as a whole. The couple of areas, which we have researched, are interoperability of languages and middleware interoperability.
6.1 Interoperability of languages
By Interoperability of languages we mean level and ease of integration with other languages. Both the Java Virtual Machine and the Common Language Runtime allow you to write code in many different languages, so long as they compile to byte code or IL code respectively. However, the .NET platform has done much more than just allows other languages to be compiled to IL code.
One of the advantages of .NET Platform is Language Independence. Since all the code on the .NET ultimately gets converted to IL, one can incorporate components written in any language supported by the CLR. An example of this is that the Framework classes (System Namespaces) that come with the .NET SDK have been written in C#, but you can use these classes from any language like VB.NET, Managed C++ without ever writing any extra code! NET allows multiple languages to freely share and extend each others libraries to a great extent. For instance, an Eiffel or Visual Basic programmer could import a C# class, override a virtual method of that class, and the C# object would now use the Visual Basic method (polymorphism). VB.NET has been massively upgraded (at the expense of compatibility with VB6) to have modern object oriented features. Languages written for .NET will generally plug into the Visual Studio.NET environment and use the same RAD frameworks if needed, thus overcoming the secondary effect of using another language. C# provides P/Invoke, which is a much simpler (no-dlls) way to interact with C code than Java's JNI. This feature is very similar to J/Direct, which is a feature of Microsoft Visual J++. It does not matter if you use VB.NET, C#, Managed C++, COBOL.NET, Perl.NET or any other language on the .NET they all produce the same code. So now on the .NET all languages have the same efficiency and power. It is just the developers choice to choose the language whose syntax he is most comfortable with.
Cross language interoperability is the ability to access constructs written in one programming language from another. There are a number of ways cross language interoperability works in Java. First of all, there is the Java Native Interface (JNI), which is a mechanism that allows Java programs call native methods written in C, C++ or assembly language. The C, C++ or assembly methods have to be written in a specific way to be called from Java. Native methods can use JNI to access Java features such as calling Java language methods, instantiating and modifying Java classes, throwing and catching exceptions, performing runtime type checking, and loading Java classes dynamically. Java also has the ability to interact with distributed objects that use the common object request broker architecture (CORBA) via Java IDL. CORBA is a technology that allows developers to make procedure calls on objects in a location and language agnostic manner. CORBA has a language-agnostic interface definition language (IDL), which has various mappings for languages that support CORBA. Java IDL supports the mappings from Java objects to CORBA IDL objects. Various ORBs support CORBA language bindings for a number of languages including C, C++, Java, Python, Lisp, Perl, and Scheme. The most seamless way to do cross language interoperability in Java is when the language is compiled directly to Java byte code. This means that objects in that language are available to Java programs and Java objects are available to programs written in the target language. A good example of this is the Jython scripting language, which is a version of the Python programming language that is integrated with the Java platform. There are a number of projects in various degrees of completion that are aimed at providing a similar degree of cross language interoperability within the confines of the Java Virtual Machine. A list of languages retargeted for the Java Virtual Machine is available on the web page of Dr. Robert Tolksdorf37. Currently, Sun Microsystems (creators of the Java language and platform) seems to be uninterested in this level of cross language interoperability and has decided to leave this to independent developers and researchers.
With seamless cross language interoperability, objects can inherit implementation from other types, instantiate and invoke methods defined on other types, and otherwise interact with objects regardless of the language the types are originally implemented in. Also tools such as class browsers, debuggers, and profilers only need to understand one format (be it Java byte codes or .NET instruction language) but can support a multitude of languages as long as they target the appropriate runtime. Also error handling across languages via exceptions is possible.
C# and the .NET runtime were created with seamless cross-language interoperability as a design goal. A language targeting the .NET common language runtime (CLR) is able to interact with other languages that conform to the common type system and when compiled include certain metad
top related