/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
package com.sun.star.comp.beans;
import java.awt.Container;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XEventListener;
import com.sun.star.connection.XConnection;
import com.sun.star.connection.XConnector;
import com.sun.star.bridge.XBridge;
import com.sun.star.bridge.XBridgeFactory;
import com.sun.star.beans.XPropertySet;
import com.sun.star.uno.XComponentContext;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.Exception;
import com.sun.star.lib.uno.helper.UnoUrl;
import com.sun.star.lib.util.NativeLibraryLoader;
/**
* This class reprecents a connection to the local office application.
*
* @since OOo 2.0.0
*/
public class LocalOfficeConnection
implements OfficeConnection
{
public static final String OFFICE_APP_NAME = "soffice";
public static final String OFFICE_LIB_NAME = "officebean";
public static final String OFFICE_ID_SUFFIX = "_Office";
private static String mProgramPath;
private Process mProcess;
private ContainerFactory mContainerFactory;
private XComponentContext mContext;
private XBridge mBridge;
private String mURL;
private String mConnType;
private String mPipe;
private String mPort;
private String mProtocol;
private String mInitialObject;
private List mComponents = new Vector();
private static long m_nBridgeCounter = 0;
//-------------------------------------------------------------------------
static
{
// preload shared libraries whichs import lips are linked to officebean
if ( System.getProperty( "os.name" ).startsWith( "Windows" ) )
{
try
{
NativeLibraryLoader.loadLibrary(LocalOfficeConnection.class.getClassLoader(), "msvcr70");
}
catch (Throwable e)
{
// loading twice would fail
System.err.println( "cannot find msvcr70" );
}
try
{
NativeLibraryLoader.loadLibrary(LocalOfficeConnection.class.getClassLoader(), "msvcr71");
}
catch (Throwable e)
{
// loading twice would fail
System.err.println( "cannot find msvcr71" );
}
try
{
NativeLibraryLoader.loadLibrary(LocalOfficeConnection.class.getClassLoader(), "uwinapi");
}
catch (Throwable e)
{
// loading twice would fail
System.err.println( "cannot find uwinapi" );
}
try
{
NativeLibraryLoader.loadLibrary(LocalOfficeConnection.class.getClassLoader(), "jawt");
}
catch (Throwable e)
{
// loading twice would fail
System.err.println( "cannot find jawt" );
}
}
// load shared library for JNI code
NativeLibraryLoader.loadLibrary( LocalOfficeConnection.class.getClassLoader(), "officebean" );
}
//-------------------------------------------------------------------------
// debugging method
private void dbgPrint( String aMessage )
{
System.err.println( aMessage );
}
/**
* Constructor.
* Sets up paths to the office application and native libraries if
* values are available in OFFICE_PROP_FILE
in the user
* home directory.
* "com.sun.star.beans.path" - the office application directory;
* "com.sun.star.beans.libpath" - native libraries directory.
*/
public LocalOfficeConnection()
{
// init member vars
try
{
setUnoUrl( "uno:pipe,name=" + getPipeName() + ";urp;StarOffice.ServiceManager" );
}
catch ( java.net.MalformedURLException e )
{}
}
/**
* protected Constructor
* Initialise a LocalOfficeConnection with an already running office.
* This C'Tor is only used in complex tests at the moment.
* @param xContext
*/
protected LocalOfficeConnection(com.sun.star.uno.XComponentContext xContext)
{
this.mContext = xContext;
}
/**
* Sets a connection URL.
* This implementation accepts a UNO URL with following format:
*
* url := uno:localoffice[,<params>];urp;StarOffice.ServiceManager * params := <path>[,<pipe>] * path := path=<pathv> * pipe := pipe=<pipev> * pathv := platform_specific_path_to_the_local_office_distribution * pipev := local_office_connection_pipe_name ** * @param url This is UNO URL which discribes the type of a connection. */ public void setUnoUrl(String url) throws java.net.MalformedURLException { mURL = null; String prefix = "uno:localoffice"; if ( url.startsWith(prefix) ) parseUnoUrlWithOfficePath( url, prefix ); else { try { UnoUrl aURL = UnoUrl.parseUnoUrl( url ); mProgramPath = null; mConnType = aURL.getConnection(); mPipe = (String) aURL.getConnectionParameters().get( "pipe" ); mPort = (String) aURL.getConnectionParameters().get( "port" ); mProtocol = aURL.getProtocol(); mInitialObject = aURL.getRootOid(); } catch ( com.sun.star.lang.IllegalArgumentException eIll ) { throw new java.net.MalformedURLException( "Invalid UNO connection URL."); } } mURL = url; } /** * Sets an AWT container catory. * * @param containerFactory This is a application provided AWT container * factory. */ public void setContainerFactory(ContainerFactory containerFactory) { mContainerFactory = containerFactory; } /** * Retrives the UNO component context. * Establishes a connection if necessary and initialises the * UNO service manager if it has not already been initialised. * This method can return
null
if it fails to connect
* to the office application.
*
* @return The office UNO component context.
*/
synchronized public XComponentContext getComponentContext()
{
if ( mContext == null )
mContext = connect();
return mContext;
}
/**
* Creates an office window.
* The window is either a sub-class of java.awt.Canvas (local) or
* java.awt.Container (RVP).
*
* @param container This is an AWT container.
* @return The office window instance.
*/
public OfficeWindow createOfficeWindow(Container container)
{
return new LocalOfficeWindow(this);
}
/**
* Closes the connection.
*/
public void dispose()
{
Iterator itr = mComponents.iterator();
while (itr.hasNext() == true) {
// ignore runtime exceptions in dispose
try { ((XEventListener)itr.next()).disposing(null); }
catch ( RuntimeException aExc ) {}
}
mComponents.clear();
//Terminate the bridge. It turned out that this is necessary for the bean
//to work properly when displayed in an applet within Internet Explorer.
//When navigating off the page which is showing the applet and then going
//back to it, then the Java remote bridge is damaged. That is the Java threads
//do not work properly anymore. Therefore when Applet.stop is called the connection
//to the office including the bridge needs to be terminated.
if (mBridge != null)
{
XComponent comp = (XComponent)UnoRuntime.queryInterface(
XComponent.class, mBridge);
if (comp != null)
comp.dispose();
else
System.err.println("LocalOfficeConnection: could not dispose bridge!");
mBridge = null;
}
mContainerFactory = null;
mContext = null;
}
/**
* Adds an event listener to the object.
*
* @param listener is a listener object.
*/
public void addEventListener(XEventListener listener)
{
mComponents.add(listener);
}
/**
* Removes an event listener from the listener list.
*
* @param listener is a listener object.
*/
public void removeEventListener(XEventListener listener)
{
mComponents.remove(listener);
}
/**
* Establishes the connection to the office.
*/
private XComponentContext connect()
{
try
{
// create default local component context
XComponentContext xLocalContext =
com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);
// initial serviceManager
XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();
// try to connect to soffice
Object aInitialObject = null;
try
{
aInitialObject = resolve(xLocalContext, mURL);
}
catch( com.sun.star.connection.NoConnectException e )
{
// launch soffice
OfficeService aSOffice = new OfficeService();
aSOffice.startupService();
// wait until soffice is started
long nMaxMillis = System.currentTimeMillis() + 1000*aSOffice.getStartupTime();
while ( aInitialObject == null )
{
try
{
// try to connect to soffice
Thread.currentThread().sleep( 500 );
aInitialObject = resolve(xLocalContext, mURL);
}
catch( com.sun.star.connection.NoConnectException aEx )
{
// soffice did not start in time
if ( System.currentTimeMillis() > nMaxMillis )
throw aEx;
}
}
}
finally
{
}
// XComponentContext
if( null != aInitialObject )
{
XPropertySet xPropertySet = (XPropertySet)
UnoRuntime.queryInterface( XPropertySet.class, aInitialObject);
Object xContext = xPropertySet.getPropertyValue("DefaultContext");
XComponentContext xComponentContext = (XComponentContext) UnoRuntime.queryInterface(
XComponentContext.class, xContext);
return xComponentContext;
}
}
catch( com.sun.star.connection.NoConnectException e )
{
System.out.println( "Couldn't connect to remote server" );
System.out.println( e.getMessage() );
}
catch( com.sun.star.connection.ConnectionSetupException e )
{
System.out.println( "Couldn't access necessary local resource to establish the interprocess connection" );
System.out.println( e.getMessage() );
}
catch( com.sun.star.lang.IllegalArgumentException e )
{
System.out.println( "uno-url is syntactical illegal ( " + mURL + " )" );
System.out.println( e.getMessage() );
}
catch( com.sun.star.uno.RuntimeException e )
{
System.out.println( "--- RuntimeException:" );
System.out.println( e.getMessage() );
e.printStackTrace();
System.out.println( "--- end." );
throw e;
}
catch( java.lang.Exception e )
{
System.out.println( "java.lang.Exception: " );
System.out.println( e );
e.printStackTrace();
System.out.println( "--- end." );
throw new com.sun.star.uno.RuntimeException( e.toString() );
}
return null;
}
//The function is copied and adapted from the UrlResolver.resolve.
//We cannot use the URLResolver because we need access to the bridge which has
//to be disposed when Applet.stop is called.
private Object resolve(XComponentContext xLocalContext, String dcp)
throws com.sun.star.connection.NoConnectException,
com.sun.star.connection.ConnectionSetupException,
com.sun.star.lang.IllegalArgumentException
{
String conDcp = null;
String protDcp = null;
String rootOid = null;
if(dcp.indexOf(';') == -1) {// use old style
conDcp = dcp;
protDcp = "iiop";
rootOid = "classic_uno";
}
else { // new style
int index = dcp.indexOf(':');
String url = dcp.substring(0, index).trim();
dcp = dcp.substring(index + 1).trim();
index = dcp.indexOf(';');
conDcp = dcp.substring(0, index).trim();
dcp = dcp.substring(index + 1).trim();
index = dcp.indexOf(';');
protDcp = dcp.substring(0, index).trim();
dcp = dcp.substring(index + 1).trim();
rootOid = dcp.trim().trim();
}
Object rootObject = null;
XBridgeFactory xBridgeFactory= null;
XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();
try {
xBridgeFactory = (XBridgeFactory)UnoRuntime.queryInterface(
XBridgeFactory.class,
xLocalServiceManager.createInstanceWithContext(
"com.sun.star.bridge.BridgeFactory", xLocalContext));
} catch (com.sun.star.uno.Exception e) {
throw new com.sun.star.uno.RuntimeException(e.getMessage());
}
synchronized(this) {
if(mBridge == null) {
Object connector= null;
try {
connector = xLocalServiceManager.createInstanceWithContext(
"com.sun.star.connection.Connector", xLocalContext);
} catch (com.sun.star.uno.Exception e) {
throw new com.sun.star.uno.RuntimeException(e.getMessage());
}
XConnector connector_xConnector = (XConnector)UnoRuntime.queryInterface(XConnector.class, connector);
// connect to the server
XConnection xConnection = connector_xConnector.connect(conDcp);
// create the bridge name. This should not be necessary if we pass an
//empty string as bridge name into createBridge. Then we should always get
//a new bridge. This does not work because of (i51323). Therefore we
//create unique bridge names for the current process.
String sBridgeName = "OOoBean_private_bridge_" + String.valueOf(m_nBridgeCounter++);
try {
mBridge = xBridgeFactory.createBridge(sBridgeName, protDcp, xConnection, null);
} catch (com.sun.star.bridge.BridgeExistsException e) {
throw new com.sun.star.uno.RuntimeException(e.getMessage());
}
}
rootObject = mBridge.getInstance(rootOid);
return rootObject;
}
}
/**
* Retrives a path to the office program folder.
*
* @return The path to the office program folder.
*/
static private String getProgramPath()
{
if (mProgramPath == null)
{
// determine name of executable soffice
String aExec = OFFICE_APP_NAME; // default for UNIX
String aOS = System.getProperty("os.name");
// running on Windows?
if (aOS.startsWith("Windows"))
aExec = OFFICE_APP_NAME + ".exe";
// add other non-UNIX operating systems here
// ...
// find soffice executable relative to this class's class loader:
File path = NativeLibraryLoader.getResource(
LocalOfficeConnection.class.getClassLoader(), aExec);
if (path != null)
mProgramPath = path.getParent();
// default is ""
if ( mProgramPath == null )
mProgramPath = "";
}
return mProgramPath;
}
/**
* Parses a connection URL.
* This method accepts a UNO URL with following format:* url := uno:localoffice[,<params>];urp;StarOffice.NamingService * params := <path>[,<pipe>] * path := path=<pathv> * pipe := pipe=<pipev> * pathv := platform_specific_path_to_the_local_office_distribution * pipev := local_office_connection_pipe_name ** *