EJB 3.0 and IIOP.NET. Table of contents. Stefan Jäger / stefanjaeger@bluewin.ch 2007-10-10



Similar documents
Introduction to Entity Beans

Software Development using MacroMedia s JRun

Overview of Web Services API

Module 13 Implementing Java EE Web Services with JAX-WS

Java E-Commerce Martin Cooke,

Installation Guide of the Change Management API Reference Implementation

Implementing a Web Service Client using Java

Work with XI 3.0 Java Proxies

Understanding class paths in Java EE projects with Rational Application Developer Version 8.0

Lösungsvorschläge zum Übungsblatt 13: Fortgeschrittene Aspekte objektorientierter Programmierung (WS 2005/06)

Hello World RESTful web service tutorial

Creating a Java application using Perfect Developer and the Java Develo...

Java 2 Platform, Enterprise Edition (J2EE) Bruno Souza Java Technologist, Sun Microsystems, Inc.

How To Test A Load Test On A Java Testnet 2.5 (For A Testnet) On A Test Server On A Microsoft Web Server On An Ipad Or Ipad (For An Ipa) On Your Computer Or Ipa

EJB & J2EE. Component Technology with thanks to Jim Dowling. Components. Problems with Previous Paradigms. What EJB Accomplishes

CHAPTER 10: WEB SERVICES

WIRIS quizzes web services Getting started with PHP and Java

Building and Using Web Services With JDeveloper 11g

Oracle WebLogic Server

To install Multifront you need to have familiarity with Internet Information Services (IIS), Microsoft.NET Framework and SQL Server 2008.

Working with WebSphere 4.0

A Sample OFBiz application implementing remote access via RMI and SOAP Table of contents

GlassFish v3. Building an ex tensible modular Java EE application server. Jerome Dochez and Ludovic Champenois Sun Microsystems, Inc.

Configuring the LCDS Load Test Tool

Builder User Guide. Version 5.4. Visual Rules Suite - Builder. Bosch Software Innovations

SharePoint Integration

Appendix A Using the Java Compiler

Getting Started with the Internet Communications Engine

EVALUATION ONLY. WA2088 WebSphere Application Server 8.5 Administration on Windows. Student Labs. Web Age Solutions Inc.

First Java Programs. V. Paúl Pauca. CSC 111D Fall, Department of Computer Science Wake Forest University. Introduction to Computer Science

Introduction to Java

bbc Developing Service Providers Adobe Flash Media Rights Management Server November 2008 Version 1.5

SDK Code Examples Version 2.4.2

Demo: Controlling.NET Windows Forms from a Java Application. Version 7.3

Remote Method Invocation

Partitioning and Clustering Demonstration

Aufgabenstellung. Aufgabenstellung

JUnit Howto. Blaine Simpson

CS506 Web Design and Development Solved Online Quiz No. 01

SW5706 Application deployment problems

File class in Java. Scanner reminder. Files 10/19/2012. File Input and Output (Savitch, Chapter 10)

Developing, Deploying, and Debugging Applications on Windows Embedded Standard 7

Builder User Guide. Version Visual Rules Suite - Builder. Bosch Software Innovations

Consuming, Providing & Publishing WS

Developing Web Services with Apache CXF and Axis2

Web Services API Developer Guide

How to Install Java onto your system

Brekeke PBX Web Service

Oracle WebLogic Server

Component Middleware. Sophie Chabridon. INT - INF Department - Distributed Systems team 2006

How to use the Eclipse IDE for Java Application Development

Java Web Services SDK

With a single download, the ADT Bundle includes everything you need to begin developing apps:

See the Developer s Getting Started Guide for an introduction to My Docs Online Secure File Delivery and how to use it programmatically.

Web Applications. Originals of Slides and Source Code for Examples:

Summary. Griglie e Sistemi di Elaborazione Ubiqui. Corso di Laurea Specialistica in Ingegneria informatica. Lucidi delle Esercitazioni

Code Estimation Tools Directions for a Services Engagement

This presentation will provide a brief introduction to Rational Application Developer V7.5.

Tutorial 5: Developing Java applications

PowerTier Web Development Tools 4

Web Application Architecture (based J2EE 1.4 Tutorial)

IBM Tivoli Workload Scheduler Integration Workbench V8.6.: How to customize your automation environment by creating a custom Job Type plug-in

l What is Android? l Getting Started l The Emulator l Hello World l ADB l Text to Speech l Other APIs (camera, bitmap, etc)

Install guide for Websphere 7.0

UFTP AUTHENTICATION SERVICE

Practice Fusion API Client Installation Guide for Windows

Smooks Dev Tools Reference Guide. Version: GA

Developer's Guide: Driving Tivoli Workload Automation

Installing Java. Table of contents

Deploying a Logi Info Application on WAS

Amazon Fulfillment Web Service. Getting Started Guide Version 1.1

IBM Rational Rapid Developer Components & Web Services

IBM Operational Decision Manager Version 8 Release 5. Getting Started with Business Rules

SAML v1.1 for.net Developer Guide

Cleo Communications. CUEScript Training

HOBOlink Web Services V2 Developer s Guide

2009 Tutorial (DB4O and Visual Studio 2008 Express)

Creating Custom Web Pages for cagrid Services

How To Use Blackberry Web Services On A Blackberry Device

RPC over XML. Web services with Java. How to install it? Reference implementation. Setting the environment variables. Preparing the system

Java Language Tools COPYRIGHTED MATERIAL. Part 1. In this part...

How To Write A Bean In Java (Java) (Java 2.4.2) (Java.Net) (Javax) 2 (Java Bean) ( Java Bean) 2-4.5

Workshop for WebLogic introduces new tools in support of Java EE 5.0 standards. The support for Java EE5 includes the following technologies:

CORBAservices. Naming. Part of the CORBA Naming Service Interface in IDL. CORBA Naming Service

SETTING UP YOUR JAVA DEVELOPER ENVIRONMENT

NetBeans IDE Field Guide

Integration and Configuration of SofwareAG s webmethods Broker with JBOSS EAP 6.1

Web Services using Tomcat and Eclipse

Working With Derby. Version 10.2 Derby Document build: December 11, 2006, 7:06:09 AM (PST)

Creating Web Services Applications with IntelliJ IDEA

Installing Eclipse C++ for Windows

LAB4 Making Classes and Objects

E-invoice manual Instruction for a client implementation of the B2B web service

Objectives. Software Development using MacroMedia s JRun. Enterprise Application Model. Topics for Discussion

Onset Computer Corporation

OpenCV on Android Platforms

TCH Forecaster Installation Instructions

Enabling Grids for E-sciencE. Web services tools. David Fergusson. INFSO-RI

Title Release Notes PC SDK Date Dealt with by, telephone. Table of Content GENERAL... 2

Eclipse installation, configuration and operation

Transcription:

Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 2007-10-10 Table of contents 1. Introduction... 2 2. Building the EJB Sessionbean... 3 3. External Standard Java Client... 4 4. Java Client with RMI-IIOP... 6 5. C# Client with IIOP.NET... 9 6. Conclusion... 11 7. Appendix... 12 7.1 Ant: iiop_builder.xml... 12 7.2 Ant: client_stub_copy.xml... 14 Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 1/14

1. Introduction I played a little bit around with accessing an EJB Sessionbean from a C# client and encountered a lot of problems to get a reference of the EJB Sessionbean in C#. To help other people, I want to share my experience in this short article. This article focuses on the creation of a connection between EJB 3.0 and C# and does not describe special ways of working with it (e.g. callback). The main goal of this article is to create a usual EJB 3.0 Sessionbean and access to it on three different ways. 1. Usual external Java Client 2. Java Client with RMI-IIOP (this example is used to build up the basics for the access from IIOP.NET) 3. C# Client with IIOP.NET For this article, you need the basic knowledge - about CORBA and IIOP (meaning of stubs and IDL should be known) - about EJB beans and Java EE in general - about working with Eclipse (Ant, how to manage a server) and Visual Studio To follow up all examples, following tools are recommended - Visual Studio 2005 - Eclipse 3.2 - Ant - Glassfish v2 (Sun Java System Application Server 9.1) Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 2/14

2. Building the EJB Sessionbean 1. Register Glassfish in Eclipse server view 2. Create new EJB project in Eclipse and define as Target Runtime the Glassfish server. This automatically adds the Glassfish libraries to the build path. In the EJB Settings window, I recommend to use the Source Folder called src/java (to make them compatible for the Ant files, which are later used). It's important to use a project name without spaces. Else it cannot be deployed correctly to the Glassfish server. 3. Now create the EJB 3.0 Sessionbean with its Interface. package ch.stefanjaeger.ejb; import javax.ejb.remote; @Remote public interface EJBTest { public String hello(); public String echo(string str); package ch.stefanjaeger.ejb; import javax.ejb.stateless; @Stateless public class EJBTestBean implements EJBTest { public String hello() { return "hello, it works!"; public String echo(string str) { return str; 4. Build the project and publish it on the Glassfish server. Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 3/14

3. External Standard Java Client 1. For the reason, the external Java Client isn't running in the context of the application server, @EJB isn't available. It is required to get the reference of the EJB Sessionbean with JNDI. First, create the Java Client in a new Eclipse project (e.g. TestProject2). 2. If the project is created, add the EJB project to the build path. This is required, because else the Client is unable to resolve the interface EJBTest. 3. Create the Client package ch.stefanjaeger; import java.util.properties; import javax.naming.initialcontext; import javax.naming.nameclasspair; import javax.naming.namingenumeration; import ch.stefanjaeger.ejb.ejbtest; public class Client { public static void main(string[] args) { try { Properties p = new Properties(); p.put("org.omg.corba.orbinitialhost","localhost"); p.put("org.omg.corba.orbinitialport","3700"); InitialContext ctx = new InitialContext(p); // FQDN of the EJB is required String lookup = "ch.stefanjaeger.ejb.ejbtest"; Object ref = ctx.lookup(lookup); EJBTest obj = (EJBTest)ref; System.out.println("hello: " +obj.hello()); System.out.println("echo: " + obj.echo("test")); catch (Exception e) { e.printstacktrace(); 4. After running the Client, following output should appear. hello: hello, it works! echo: test If the example doesn't work, check, if the EJB is correctly deployed. This can be done with ctx.list (described some lines beneath). Another possibility is the port configuration of the Glassfish server for remote objects. Just check the port at http://localhost:4848 in Configuration ORB IIOP Listener orb-listener-1. Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 4/14

5. Just a little hint: If you want to know all registered objects, just add NamingEnumeration<NameClassPair> list = ctx.list(""); while (list.hasmore()) System.out.println(list.next().getName()); At least the registred EJB "ch.stefanjaeger.ejb.ejbtest" should appear. 6. Just another hint: instead of defining the whole classname in the ctx.lookup(), this syntax can also be used: Object ref = ctx.lookup(ejbtesthome.class.getname()); Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 5/14

4. Java Client with RMI-IIOP 1. If the standard Java Client isn't working, please don't start with this example and try to get the standard Java Client working. With the standard Java Client, you can ensure a working Glassfish environment. 2. First of all, I have to explain some compatibility issues with IIOP and EJB 3.0. The goal with this Client (and also with the C# Client) is to access directly with IIOP. The properties are changed from the standard Client to p.put(context.initial_context_factory, "com.sun.jndi.cosnaming.cnctxfactory"); p.put(context.provider_url, "iiop://localhost:3700"); If the Client is started, the Exception javax.naming.namenotfoundexception [Root exception is org.omg.cosnaming.namingcontextpackage.notfound: IDL:omg.org/CosNaming/NamingContext/NotFound:1.0] is thrown. The reason is, that access with cosnaming to EJB 3.0 Beans is only possible with EJB 2.x Home/Remote view. For this reason, the EJBTest Bean isn't registered with IIOP. To make the EJB Sessionbean accessible with IIOP, it needs to be rewritten as an EJB 2.x Bean. However, some EJB 3.0 syntax can be used (http://blogs.sun.com/enterprisetechtips/entry/ejb_3_0_compatibility_and). 3. Rewrite the EJB Sessionbean with Home and Remote interface and redeploy application. Remote Interface extends EJBObject (which is a remote object) and adds throw RemoteExcpetion: package ch.stefanjaeger.ejb; import java.rmi.remoteexception; import javax.ejb.ejbobject; public interface EJBTest extends EJBObject { public String hello() throws RemoteException; public String echo(string str) throws RemoteException; Home Interface extends EJBHome and returns in the method create the EJBTest bean (which gets implemented by EJBTestBean) package ch.stefanjaeger.ejb; import java.rmi.remoteexception; import javax.ejb.createexception; import javax.ejb.ejbhome; public interface EJBTestHome extends EJBHome { public EJBTest create() throws CreateException, RemoteException; Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 6/14

The EJBTestBean gets an annotation @RemoteHome, which defines the class of the EJBTestHome class (this class is needed for the creation of this bean). The method create() is declared with the annotation @init. This definition is required, because EJB 2.x always has a create() method to handle the bean creation. The @Init annotation tells the EJB container which method that is. package ch.stefanjaeger.ejb; import javax.ejb.init; import javax.ejb.remotehome; import javax.ejb.stateless; @Stateless @RemoteHome(EJBTestHome.class) public class EJBTestBean { @Init public void create() { public String hello() { return "hello, it works!"; public String echo(string str) { return str; 4. If we compare the output of the ctx.list("") operation with the old bean and the new one (the EJB 2.x compatible bean), we can see, that the Sessionbean now get's registered correctly. before ejb SerialContextProvider ch.stefanjaeger\.ejb\.ejbtest 3_x_Internal_RemoteBusinessHome after ejb SerialContextProvider ch.stefanjaeger\.ejb\.ejbtesthome 5. The next step is to adjust the Client. The handling of the EJB object is now a little bit different as with EJB 3.0. It uses the EJB 2.x methods create() for creating the object. Object ref = ctx.lookup("ch.stefanjaeger.ejb.ejbtest"); EJBTestHome objhome = (EJBTestHome) PortableRemoteObject.narrow(ref, EJBTestHome.class); EJBTest obj = (EJBTest) objhome.create(); If we want to compile the client now, we are getting a syntax error. The CreateException of the function objhome.create(); couldn't be resolved. The reason is that this information is stored in the JAR files from Java EE. Add to the build path the Jar files from Glassfish (in the build path window, add Variable, choose Server Runtime and add the Glassfish libraries). Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 7/14

Now, almost everything seems to be done. But if we run this Client, a NullpointerException is thrown from objhome.create(). The reference to objhome is null. That means, PortableRemoteObject.narrow() returns null. If you debug this situation, you will see that the class ch.stefanjaeger.ejb._ejbtesthome_stub is missing... If we remember what we are doing now, the reason for this missing file is clear. If you have already used RMI-IIOP, you know that you have to create the stub files manually, because they aren't created on runtime like RMI-JRMP. 6. To create the stub files, the batch file asadmin.bat from Glassfish is used with the parameter generatermistubs. It creates a Client jar file with all stubs, based on the ejb jar file. The resulting Client jar file can be imported to the Client build path. The previously added EJB project to the build path can be removed. To create the stub files and to copy the Client jar to the Client project, use the Ant files in the appendix. They are useful and save a lot of time. From the EJB project run "generate java client stub" (for the generation of the stubs, the Glassfish server need's to be running!). After that, in the folder /build/stub the stubs are generated. Then run "refresh stubs" from the Client Ant file, which copies the Client jar file to the lib folder of the Client (don't forget to add this jar file to the build path!). After that, the Client should run without any problems. Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 8/14

5. C# Client with IIOP.NET 1. After the implementation of the Java Client over RMI-IIOP, we can be sure, the EJB is accessible with IIOP. The next part is easy. First of all, download the latest version of IIOP.NET and compile it with nmake (open Visual Studio command prompt and navigate to the IIOP.NET folder and run nmake without any parameters. It creates in every folder a bin folder with the executables). 2. The next step is to generate the IDL files from the Java classes. After that, we can generate a DLL file with the IDLToCLSCompiler.exe. To do this use the Ant file and run the target "generate iiop.net dll". Don't forget to define the IIOP.NET folder and all the EJBs, which are remotely accessible (define Property iiop_net.path with the path and IDLToCLSCompiler.ejbs with "ch/stefanjaeger/ejb/ejbtest.idl ch/stefanjaeger/ejb/ejbtesthome.idl"). After running the Ant file, the DLL is created (located in IIOP.NET folder\idltoclscompiler\idlcompiler\bin\tmp). 3. Create a new VS.NET project and copy the IIOPChannel.dll (inside IIOP.NET folder\idltoclscompiler\idlcompiler\bin) and EJBTestClient.dll (inside IIOP.NET folder\idltoclscompiler\idlcompiler\bin\tmp) to the project bin directory. Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 9/14

4. Add a reference to the DLL's to the project and enter or copy following code: using System; using System.Collections.Generic; using System.Text; using Ch.Elca.Iiop; using System.Runtime.Remoting.Channels; using Ch.Elca.Iiop.Services; using omg.org.cosnaming; using ch.stefanjaeger.ejb; namespace TestApplication { class Program { static void Main(string[] args) { try { // register the channel IiopClientChannel channel = new IiopClientChannel(); ChannelServices.RegisterChannel(channel, false); // access COS nameing service RmiIiopInit init = new RmiIiopInit("localhost", 3700); NamingContext nameservice = init.getnameservice(); // get the object NameComponent[] name = new NameComponent[] { new NameComponent("ch", "stefanjaeger.ejb.ejbtesthome") ; // now, do the same as in Java EJBTestHome objhome = (EJBTestHome)nameService.resolve(name); EJBTest obj = objhome.create(); Console.WriteLine("hello: " + obj.hello()); Console.WriteLine("echo: " + obj.echo("test")); Console.ReadLine(); catch (Exception e) { Console.WriteLine("exception: " + e); 5. You can see, that the name of the EJB isn't in one string. To get a list of all registered names (and how they are registered) use the list method: omg.org.cosnaming.binding[] bindings; BindingIterator bindingiterator; nameservice.list(10, out bindings, out bindingiterator); foreach (omg.org.cosnaming.binding binding in bindings) { NameComponent[] currentname = binding.binding_name; Console.WriteLine(currentName[0].id + " - " + currentname[0].kind); Console.ReadLine(); 6. Note: after generating the DLL, the Java exceptions were not implemented in C#. They "should" be implemented (the example works without. To get more information about this, check out the IIOP.NET website). Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 10/14

6. Conclusion Accessing to an EJB from C# is easy, if you know, how to do it ;-) I spend several days trying every possible solution to get Java and.net working together. Most time I spend with was trying to get an EJB 3.0 object working with IIOP. After reading http://forums.java.net/jive/thread.jspa?threadid=18064 and http://blogs.sun.com/enterprisetechtips/entry/ejb_3_0_compatibility_and most of the problems were solved. I hope, this article helps you working with Glassfish and IIOP.NET. If you have any comments to this article, feel free to contact me. Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 11/14

7. Appendix 7.1 iiop_builder.xml It's important to change the yellow marked sections. Copy this file to the project dir. If it stored somewhere else, change the basedir in the first line. This Ant file doesn't have a deploy function! This have to be done by Eclipse. <project name="testproject" default="generate all" basedir="."> <!-- common used (author and name of project for jar file) --> <property name="author" value="stefan Jaeger" /> <property name="project" value="testproject" /> <!-- IIOP.NET configuration --> <property name="idltoclscompiler.target" value="ejbtestclient" /> <property name="idltoclscompiler.ejbs" value="ch/stefanjaeger/ejb/ejbtest.idl ch/stefanjaeger/ejb/ejbtesthome.idl" /> <!-- definition for app server --> <property name="sunappserver.serveraddress" value="localhost" /> <property name="sunappserver.serverportnumber" value="8080" /> <property name="sunappserver.adminname" value="admin" /> <property name="sunappserver.adminpassword" value="adminadmin" /> <!-- locations --> <property name="glassfish" location="d:/java/glassfish" /> <property name="build" location="build" /> <property name="dist" location="dist" /> <property name="src" location="src/java" /> <property name="iiop_net.path" location="d:/iiopnet.src.1.9.0.final" /> <!-- generated locations --> <property name="stub" location="${build/stub" /> <property name="classes" location="${build/classes" /> <property name="idltoclscompiler.path" location="${iiop_net.path/idltoclscompiler/idlcompiler/bin" /> <property name="idltoclscompiler.exec" location="${idltoclscompiler.path/idltoclscompiler.exe" /> <!-- Init/Clean --> <target name="init" description="create directory structure"> <mkdir dir="${build" /> <target name="clean" description="clean up"> <delete dir="${build" /> <delete dir="${dist" /> <!-- Classpath --> <target name="classpath" description="generates the classpath for compiling"> <path id="classpath"> <fileset dir="${glassfish/lib/"> <!-- <include name="**/*.jar" />--> <!-- alle notwendig?? --> <include name="appserv-rt.jar" /> <include name="javaee.jar" /> <include name="mail.jar" /> <include name="webservices-rt.jar" /> <include name="webservices-tools.jar" /> <include name="appserv-jstl.jar" /> <include name="appserv-tags.jar" /> Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 12/14

<include name="activation.jar" /> </fileset> </path> <!-- compile and dist for generating jar--> <target name="compile" depends="clean,init,classpath" description="compile the source"> <mkdir dir="${classes" /> <javac srcdir="${src" destdir="${classes"> <classpath> <path refid="classpath" /> </classpath> </javac> <target name="dist" depends="compile" description="create jar"> <mkdir dir="${dist" /> <jar jarfile="${dist/${project.jar" basedir="${classes"> <manifest> <attribute name="built-by" value="${author" /> <attribute name="sealed" value="false" /> </manifest> </jar> <!-- generate stubs --> <target name="generate java client stub" depends="dist" description="generate Client.jar with RMI-IIOP stubs"> <mkdir dir="${stub" /> <exec executable="${glassfish/bin/asadmin.bat" failonerror="true"> <arg line=" deploy " /> <arg line=" --generatermistubs" /> <arg line=" --retrieve ${stub/" /> <arg line=" ${dist/${project.jar" /> </exec> <!-- generate idl after dist, because idl files are generated in classes path --> <target name="idl" depends="dist" description="generate IDL files"> <rmic base="${classes" idl="true"> <classpath> <path refid="classpath" /> </classpath> </rmic> <target name="generate iiop.net dll" depends="idl" description="generate cls code form idl"> <delete dir="${idltoclscompiler.path/tmp" /> <copy todir="${idltoclscompiler.path/tmp"> <fileset dir="${classes/"> <include name="**/*.idl" /> </fileset> </copy> <copy file="${iiop_net.path/idltoclscompiler/idl/orb.idl" todir="${idltoclscompiler.path/tmp" /> <exec executable="${idltoclscompiler.exec" dir="${idltoclscompiler.path/tmp" failonerror="true"> <arg line=" -o. " /> <arg line=" ${IDLToCLSCompiler.target " /> <arg line=" ${IDLToCLSCompiler.ejbs " /> </exec> /> <echo level="info" message="dll file generated in ${IDLToCLSCompiler.path\tmp" <echo level="info" message="on error, check property IDLToCLSCompiler.ejbs!" /> Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 13/14

<!-- do everything --> <target name="generate all" depends="generate iiop.net dll,generate java client stub" description="generate all" /> </project> 7.2 client_stub_copy.xml Change the paths of the project locations. <project name="testprojectclient" default="refresh_stubs" basedir="."> <target name="refresh_stubs" description="get stub library"> <copy file="d:/testproject/build/stub/rmi-iiop.net-glassfishclient.jar" todir="d:/testprojectclient/lib" /> </project> Stefan Jäger / stefanjaeger@bluewin.ch EJB 3.0 and IIOP.NET 14/14