Files
libreoffice/bean/com/sun/star/comp/beans/LocalOfficeConnection.java

664 lines
22 KiB
Java
Raw Normal View History

2004-09-06 14:11:38 +00:00
/*************************************************************************
*
* $RCSfile: LocalOfficeConnection.java,v $
*
2004-09-14 14:10:24 +00:00
* $Revision: 1.2 $
2004-09-06 14:11:38 +00:00
*
2004-09-14 14:10:24 +00:00
* last change: $Author: mi $ $Date: 2004-09-14 15:07:23 $
2004-09-06 14:11:38 +00:00
*
* The Contents of this file are made available subject to the terms of
* either of the following licenses
*
* - GNU Lesser General Public License Version 2.1
* - Sun Industry Standards Source License Version 1.1
*
* Sun Microsystems Inc., September, 2004
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2004 by Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, CA 94303, USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*
* Sun Industry Standards Source License Version 1.1
* =================================================
* The contents of this file are subject to the Sun Industry Standards
* Source License Version 1.1 (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.openoffice.org/license.html.
*
* Software provided under this License is provided on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
* See the License for the specific provisions governing your rights and
* obligations concerning the Software.
*
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
*
* Copyright: 2004 by Sun Microsystems, Inc.
*
* All Rights Reserved.
*
* Contributor(s): _______________________________________
*
*
************************************************************************/
package com.sun.star.comp.beans;
import java.awt.Component;
import java.awt.Container;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.Properties;
import java.util.Enumeration;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XEventListener;
import com.sun.star.lang.XInitialization;
import com.sun.star.container.XSet;
import com.sun.star.connection.XConnection;
import com.sun.star.bridge.XBridge;
import com.sun.star.bridge.XBridgeFactory;
import com.sun.star.bridge.XUnoUrlResolver;
import com.sun.star.comp.loader.JavaLoader;
import com.sun.star.loader.XImplementationLoader;
import com.sun.star.beans.XPropertySet;
import com.sun.star.uno.XComponentContext;
import com.sun.star.uno.Type;
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.
*/
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 Process mProcess;
private ContainerFactory mContainerFactory;
private XComponentContext mContext;
private String mURL;
private String mProgramPath;
private String mConnType;
private String mPipe;
private String mPort;
private String mProtocol;
private String mInitialObject;
private List mComponents = new Vector();
2004-09-14 14:10:24 +00:00
//-------------------------------------------------------------------------
// debugging method
private void dbgPrint( String aMessage )
{
System.err.println( aMessage );
}
2004-09-06 14:11:38 +00:00
/**
* Constructor.
* Sets up paths to the office application and native libraries if
* values are available in <code>OFFICE_PROP_FILE</code> in the user
* home directory.<br />
* "com.sun.star.beans.path" - the office application directory;<br/>
* "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 )
{}
// load libofficebean.so/officebean.dll
String aSharedLibName = getProgramPath() + java.io.File.separator +
System.mapLibraryName(OFFICE_LIB_NAME);
2004-09-14 14:10:24 +00:00
dbgPrint( "System.load( libofficebean.so )" );
2004-09-06 14:11:38 +00:00
System.load( aSharedLibName );
}
/**
* Sets a connection URL.
* This implementation accepts a UNO URL with following format:<br />
* <pre>
* url := uno:localoffice[,&lt;params&gt;];urp;StarOffice.ServiceManager
* params := &lt;path&gt;[,&lt;pipe&gt;]
* path := path=&lt;pathv&gt;
* pipe := pipe=&lt;pipev&gt;
* pathv := platform_specific_path_to_the_local_office_distribution
* pipev := local_office_connection_pipe_name
* </pre>
*
* @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 <code>null</code> if it fails to connect
* to the office application.
*
* @return The office UNO component context.
*/
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();
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
2004-09-14 14:10:24 +00:00
dbgPrint( "create initial component context" );
2004-09-06 14:11:38 +00:00
XComponentContext xLocalContext =
com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);
// initial serviceManager
2004-09-14 14:10:24 +00:00
dbgPrint( "get local service manager" );
2004-09-06 14:11:38 +00:00
XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();
// create a urlresolver
2004-09-14 14:10:24 +00:00
dbgPrint( "get local service manager" );
2004-09-06 14:11:38 +00:00
Object urlResolver = xLocalServiceManager.createInstanceWithContext(
"com.sun.star.bridge.UnoUrlResolver", xLocalContext );
// query for the XUnoUrlResolver interface
XUnoUrlResolver xUrlResolver =
(XUnoUrlResolver) UnoRuntime.queryInterface( XUnoUrlResolver.class, urlResolver );
// try to connect to soffice
Object aInitialObject = null;
try
{
2004-09-14 14:10:24 +00:00
dbgPrint( "xUrlResolver.resolve( " + mURL + " )" );
2004-09-06 14:11:38 +00:00
aInitialObject = xUrlResolver.resolve( mURL );
2004-09-14 14:10:24 +00:00
dbgPrint( "xUrlResolver.resolve() - done" );
2004-09-06 14:11:38 +00:00
}
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 = xUrlResolver.resolve( 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;
}
/**
* Retrives a path to the office program folder.
*
* @return The path to the office program folder.
*/
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(
this.getClass().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:<br />
* <pre>
* url := uno:localoffice[,&lt;params&gt;];urp;StarOffice.NamingService
* params := &lt;path&gt;[,&lt;pipe&gt;]
* path := path=&lt;pathv&gt;
* pipe := pipe=&lt;pipev&gt;
* pathv := platform_specific_path_to_the_local_office_distribution
* pipev := local_office_connection_pipe_name
* </pre>
*
* <h4>Examples</h4>
* <ul>
* <li>"uno:localoffice,pipe=xyz_Office,path=/opt/openoffice11/program;urp;StarOffice.ServiceManager";
* <li>"uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager";
* </ul>
*
* @param url This is UNO URL which describes the type of a connection.
* @exception java.net.MalformedURLException when inappropreate URL was
* provided.
*/
private void parseUnoUrlWithOfficePath(String url, String prefix)
throws java.net.MalformedURLException
{
// Extruct parameters.
int idx = url.indexOf(";urp;StarOffice.NamingService");
if (idx < 0)
throw new java.net.MalformedURLException(
"Invalid UNO connection URL.");
String params = url.substring(prefix.length(), idx + 1);
// Parse parameters.
String name = null;
String path = null;
String pipe = null;
char ch;
int state = 0;
StringBuffer buffer = new StringBuffer();
for(idx = 0; idx < params.length(); idx += 1) {
ch = params.charAt(idx);
switch (state) {
case 0: // initial state
switch(ch) {
case ',':
buffer.delete(0, buffer.length());
state = 1;
break;
case ';':
state = 7;
break;
default:
buffer.delete(0, buffer.length());
buffer.append(ch);
state = 1;
break;
}
break;
case 1: // parameter name
switch(ch) {
case ' ':
case '=':
name = buffer.toString();
state = (ch == ' ')? 2: 3;
break;
case ',':
case ';':
state = -6; // error: invalid name
break;
default:
buffer.append(ch);
break;
}
break;
case 2: // equal between the name and the value
switch(ch) {
case '=':
state = 3;
break;
case ' ':
break;
default:
state = -1; // error: missing '='
break;
}
break;
case 3: // value leading spaces
switch(ch) {
case ' ':
break;
default:
buffer.delete(0, buffer.length());
buffer.append(ch);
state = 4;
break;
}
break;
case 4: // value
switch(ch) {
case ' ':
case ',':
case ';':
idx -= 1; // put back the last read character
state = 5;
if (name.equals("path")) {
if (path == null)
path = buffer.toString();
else
state = -3; // error: more then one 'path'
} else if (name.equals("pipe")) {
if (pipe == null)
pipe = buffer.toString();
else
state = -4; // error: more then one 'pipe'
} else
state = -2; // error: unknown parameter
buffer.delete(0, buffer.length());
break;
default:
buffer.append(ch);
break;
}
break;
case 5: // a delimeter after the value
switch(ch) {
case ' ':
break;
case ',':
state = 6;
break;
case ';':
state = 7;
break;
default:
state = -5; // error: ' ' inside the value
break;
}
break;
case 6: // leading spaces before next parameter name
switch(ch) {
case ' ':
break;
default:
buffer.delete(0, buffer.length());
buffer.append(ch);
state = 1;
break;
}
break;
default:
throw new java.net.MalformedURLException(
"Invalid UNO connection URL.");
}
}
if (state != 7)
throw new java.net.MalformedURLException(
"Invalid UNO connection URL.");
// Set up the connection parameters.
if (path != null)
mProgramPath = path;
if (pipe != null)
mPipe = pipe;
}
/** creates a unique pipe name.
*/
static String getPipeName()
{
// turn user name into a URL and file system safe name (% chars will not work)
String aPipeName = System.getProperty("user.name") + OFFICE_ID_SUFFIX;
aPipeName = aPipeName.replaceAll( "_", "%B7" );
return java.net.URLEncoder.encode(aPipeName).replaceAll( "\\+", "%20" ).replaceAll( "%", "_" );
}
/**
* @para This is an implementation of the native office service.
*/
private class OfficeService
implements NativeService
{
/**
* Retrive the office service identifier.
*
* @return The identifier of the office service.
*/
public String getIdentifier()
{
if ( mPipe == null)
return getPipeName();
else
return mPipe;
}
/**
* Starts the office process.
*/
public void startupService()
throws java.io.IOException
{
// create call with arguments
String[] cmdArray = new String[4];
cmdArray[0] = (new File(getProgramPath(), OFFICE_APP_NAME)).getPath();
cmdArray[1] = "-nologo";
cmdArray[2] = "-nodefault";
if ( mConnType.equals( "pipe" ) )
cmdArray[3] = "-accept=pipe,name=" + getIdentifier() + ";" +
mProtocol + ";" + mInitialObject;
else if ( mConnType.equals( "socket" ) )
cmdArray[3] = "-accept=socket,port=" + mPort + ";urp";
else
throw new java.io.IOException( "not connection specified" );
// start process
2004-09-14 14:10:24 +00:00
dbgPrint( "exec" + cmdArray[0] + " " + cmdArray[1] + " " + cmdArray[2] + " " + cmdArray[3] );
2004-09-06 14:11:38 +00:00
mProcess = Runtime.getRuntime().exec(cmdArray);
if ( mProcess == null )
throw new RuntimeException( "cannot start soffice: " + cmdArray );
}
/**
* Retrives the ammount of time to wait for the startup.
*
* @return The ammount of time to wait in seconds(?).
*/
public int getStartupTime()
{
return 60;
}
}
}