Zero Client Installation for Visual C++ Accessing Java |
Zero client installation is a form of late binding which does not require any installation on client machine. It provides the ability to access Java Objects running anywhere from COM clients running on unmodified Windows machines.
This example shows you how you can access Java objects (including EJBs) running on any Operating System from COM clients running on a standard Microsoft Windows NT (SP4 or greater) or Windows 2000 machine using J-Integra®™.
There is zero deployment overhead: you will not have to install any software at all on the Windows client machine.
This example uses the objref moniker string generated by GetJvmMoniker, and is a complete analogue to the VB to Java (zero client installation) example. The main difference is that this sample shows how to access Java class from a Visual C++ client. For the details on how this sample works please refer to VB to Java (zero client installation) example.
On any machine that has the jintegra.jar file on it, set your CLASSPATH to include jintegra.jar.
Then run the com.linar.jintegra.GetJvmMoniker Java class, specifying the full TCP/IP name or address of the machine running the JVM in which Java objects will be accessed, and a free TCP/IP port number as parameters:
java com.linar.jintegra.GetJvmMoniker mymachine.mycompany.com 1350
Note If you have already tried the VB to Java (zero client installation) example, there is no need to run GetJvmMoniker again.
You will see a long message displayed which shows the objref moniker and explains how to use it. The full text is also copied to your clipboard:
Create a file called Simple.java with the following contents:
public class Simple { // public static void main(String[] args) throws Exception { // Register the JVM with the name "ajvm" com.linar.jintegra.Jvm.register("ajvm"); Thread.sleep(6000000); // Sleep for a while } public int property1 = 77; public static java.util.Date property2; public void method1(String aString) { System.out.println("method1 has been called with " + aString); } } |
Compile the file using javac Simple.java then run it using
java -DJINTEGRA_DCOM_PORT=1350 Simple
Start Microsoft Visual C++
Click on File>New>Projects>MFC AppWizard (exe)
Name the new project MFC_Moniker and click OK
Click the 3D controls check box and click Finish
Add two buttons to your MFC_Moniker dialogue box and call them BindToObject and CoGetObject. These buttons access both the BindToObject() and CoGetObject() APIs.
Now we will add functionality to our client to make it an actual COM client and to enable it retrieve COM object through a given objref moniker string:
On a Workspace panel (usually the left one) click a ClassView tab
Right-click the CMFC_MonikerDlg class
Select Add member variable
Set the Type to IDispatch*, the Name to m_pIDJvm, and the Access to protected
Go to the CMFC_MonikerDlg::OnInitDialog() member function by clicking on OnInitDialog() in theWorkspace panel.
After // TODO: Add extra initialization here add the following code:
m_pIDJvm = NULL; HRESULT hRes = CoInitialize(NULL); if(FAILED(hRes)) { AfxGetApp()->ExitInstance(); } m_pszMoniker = OLESTR("objref:TUVPVwEAAAAABAIAAAAAAMAAAAAAAABGABAAAAAAAABKaW50ZWdyYVRhbGtUb01lV2hhdHNBbGxUaGlzVGhlbhcAEAAHAGcAZgBpAGwAawBvAHYAWwAxADMANQAwAF0AAAAAAAoA//8AAAAAAAAAAAAA:"); |
Replace the moniker value above with the actual value generated by GetJvmMoniker.
Go to the CMFC_MonikerApp::InitInstance() member function.
After int nResponse = dlg.DoModal(); add the line:
CoUninitialize();
Double-click the BindToObject button
Click OK (accept defaults) on the message box popped up by Wizard and you¡¯ll be in the body of the empty message handler for the first button:
void CMFC_MonikerDlg::OnButton1() { // TODO: Add your control notification handler code here }
In the same manner create and empty handler for the CoGetObject button.
Now copy and paste the following code:
void CMFC_MonikerDlg:: OnButton1() { LPCTSTR ctLabel = _T("Moniker Test"); TCHAR tcBuf[0x20]; IDispatch* pIDObj = NULL; // Java Object ref long lTest = 0; BSTR bsTest = SysAllocString(L"MFC Client, OnButton1()"); BSTR bsObj = SysAllocString(L"Simple"); // Get pointer to JVM: // create a new binding context for parsing and binding the moniker IBindCtx *pbc = 0; HRESULT hr = CreateBindCtx(0, &pbc); if(SUCCEEDED(hr)) { ULONG cchEaten; IMoniker *pmk = NULL; // ask COM to convert the display name to a moniker object hr = MkParseDisplayName(pbc, m_pszMoniker, &cchEaten, &pmk); if(SUCCEEDED(hr)) { // ask the moniker to find or create the object that it refers to hr = pmk->BindToObject(pbc, 0, IID_IDispatch, (void**)&m_pIDJvm); // we now have a pointer to the desired object, so release // the moniker and the binding context pmk->Release(); } pbc->Release(); } if(FAILED(hr)) { goto out; } // Use pIDispJvm: Get Object: hr = GetJavaObject(bsObj, &pIDObj); if(FAILED(hr)) { goto out; } // Test COM object: if(FAILED(hr = TestCom(pIDObj, bsTest))) { goto out; } out: if(m_pIDJvm) { lTest = m_pIDJvm->Release(); m_pIDJvm = NULL; } if(SUCCEEDED(hr)) { MessageBox(_T("Success"), ctLabel, MB_OK|MB_ICONINFORMATION); } else { _stprintf(tcBuf, _T("Failed\nHResult = %X"), hr); MessageBox(tcBuf, ctLabel, MB_OK|MB_ICONSTOP); } } void CMFC_MonikerDlg::OnButton2() { HRESULT hr = S_OK; IDispatch* pIDObj = NULL; // Java Object ref long lTest = 0; LPCTSTR ctLabel = _T("Moniker Test2"); TCHAR tcBuf[0x20]; BSTR bsObj = SysAllocString(L"Simple"); BSTR bsTest = SysAllocString(L"MFC Client, OnButton2()"); // Get pointer to JVM: hr = CoGetObject(m_pszMoniker, 0, IID_IDispatch, (void**)&m_pIDJvm); if(FAILED(hr)) { goto out; } // Use pIDispJvm: Get Object: hr = GetJavaObject(bsObj, &pIDObj); if(FAILED(hr)) { goto out; } // Test COM object: if(FAILED(hr = TestCom(pIDObj, bsTest))) { goto out; } out: if(m_pIDJvm) { lTest = m_pIDJvm->Release(); m_pIDJvm = NULL; } if(SUCCEEDED(hr)) { MessageBox(_T("Success"), ctLabel, MB_OK|MB_ICONINFORMATION); } else { _stprintf(tcBuf, _T("Failed\nHResult = %X"), hr); MessageBox(tcBuf, ctLabel, MB_OK|MB_ICONSTOP); } } |
Both handlers work the same way:
Retrieve pIDJvmpointer to JVM object¡¯s IDispatch interface
Retrieve pIDObjpointer to Java object¡¯s IDispatch interface
Test-run method and property put/get on the Java object
The difference is in the method of retrieving pIDJvm:
The BindToObject button uses the CreateBindCtx(), MkParseDisplayName() and BindToObject() APIs
The CoGetObject button uses the CoGetObject () API.
We will add two helper functions: GetJavaObject() will retrieve pointer to the Java object¡¯s IDispatch interface, and TestCom() will run complete test on the retrieved Java object.
On a Workspace panel (usually the left one) click a ClassView tab.
Right-click the CMFC_MonikerDlg class.
Select Add member function.
Set the Type to HRESULT, the Declaration to GetJavaObject(BSTR bsObj, IDispatch** ppIDObj) and the Access to public.
Add another member function and set the Type to HRESULT, the Declaration to TestCom(IDispatch* pIDObj, BSTR bsMessage and the Access to public.
Copy and paste the following code for those functions:
HRESULT CMFC_MonikerDlg::TestCom(IDispatch *pIDObj, BSTR bsMessage) { HRESULT hr = S_OK; DISPID dispid; DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; OLECHAR FAR* szMember = L"property1"; long nTest = 13; // Specify method and params: VARIANTARG MyArgs[1]; // Just 1 arg VariantInit(&MyArgs[0]); VARIANT vResult; VariantInit(&vResult); DISPPARAMS MyParams = {MyArgs, 0, 1, NULL}; // Just 1 not named param if(NULL == pIDObj) { hr = E_INVALIDARG; goto out; } // Set Simple.property1 hr = pIDObj->GetIDsOfNames(IID_NULL, &szMember, 1, GetUserDefaultLCID(), &dispid); if(FAILED(hr)) { goto out; } MyParams.rgvarg[0].vt = VT_I4; MyParams.rgvarg[0].lVal = nTest; // Set to something MyParams.rgdispidNamedArgs = &dispid; MyParams.cArgs = 1; MyParams.cNamedArgs = 1; hr = pIDObj->Invoke( dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &MyParams, NULL, NULL, NULL); if(FAILED(hr)) { goto out; } // Get Simple.property1 hr = pIDObj->Invoke( dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vResult, NULL, NULL); if(FAILED(hr)) { goto out; } // Check property value: if(VT_I4 != vResult.vt) { hr = E_FAIL; goto out; } if(nTest != vResult.iVal) { hr = E_FAIL; goto out; } szMember = L"method1"; hr = pIDObj->GetIDsOfNames(IID_NULL, &szMember, 1, GetUserDefaultLCID(), &dispid); MyParams.rgvarg[0].vt = VT_BSTR; // String MyParams.rgvarg[0].bstrVal = bsMessage; hr = pIDObj->Invoke( dispid, IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD, &MyParams, &vResult, NULL, NULL); if(FAILED(hr)) { goto out; } out: VariantClear(&MyArgs[0]); // Embedded BSTR will be freed VariantClear(&vResult); // Embedded pIDisp will be released return hr; } HRESULT CMFC_MonikerDlg::GetJavaObject(BSTR bsObj, IDispatch **ppIDObj) { HRESULT hr = S_OK; *ppIDObj = NULL; OLECHAR* name1 = L"get"; // Set simple = jvm.get("Simple") DISPID dispid; // Specify method and params: VARIANTARG MyArgs[1]; // Just 1 arg VariantInit(&MyArgs[0]); MyArgs[0].vt = VT_BSTR; // String MyArgs[0].bstrVal = bsObj; //MyArgs[0].bstrVal = SysAllocString(L"Simple"); DISPPARAMS MyParams = {MyArgs, 0, 1, NULL}; // Just 1 not named param VARIANT vResult; VariantInit(&vResult); hr = m_pIDJvm->GetIDsOfNames(IID_NULL, &name1, 1, GetUserDefaultLCID(), &dispid); if(FAILED(hr)) { goto out; } hr = m_pIDJvm->Invoke( dispid, IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD, &MyParams, &vResult, NULL, NULL); if(FAILED(hr)) { goto out; } // retrieve returned pointer to jvm.Simple object: if(VT_DISPATCH != vResult.vt) { goto out; } *ppIDObj = vResult.pdispVal; // if all OK out: return hr; } |
Run your Visual C++ client. Click the BindToObject button. The Java object will be retrieved and tested. A success message will be displayed by client, and an output string from the Java object will appear on the Java console.
Test the CoGetObject button in the same way.
Normally you will have the following output on your Java console:
C:\JI_Support\ZeroInst>java -DJINTEGRA_DCOM_PORT=1350 Simple method1 has been called with MFC Client, OnButton1() method1 has been called with MFC Client, OnButton2()
If something goes wrong you will receive an error message from your client.
If you receive the error message: ¡°Failure. Hresult = 8007000E¡± (Not enough storage is available to complete this operation), you probably did not started Java with the command:
java -DJINTEGRA_DCOM_PORT=1350 Simple
If you receive error message: ¡°Failure. Hresult = 800706BA¡± (The RPC server is unavailable), you have probably started Java on the wrong port, not the one that was indicated in your GetJvmMoniker command.