/*************************************************************************
*
* $RCSfile: LocalOfficeConnection.java,v $
*
* $Revision: 1.12.2.1 $
*
* last change: $Author: jsc $ $Date: 2003/02/24 21:09:47 $
*
* 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., October, 2000
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2000 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: 2000 by Sun Microsystems, Inc.
*
* All Rights Reserved.
*
* Contributor(s): _______________________________________
*
*
************************************************************************/
package com.sun.star.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.lang.XSingleServiceFactory;
import com.sun.star.lang.XMultiServiceFactory;
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.comp.loader.JavaLoader;
import com.sun.star.loader.XImplementationLoader;
import com.sun.star.uno.XComponentContext;
import com.sun.star.uno.Type;
import com.sun.star.uno.AnyConverter;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.Exception;
/**
* 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_PROP_FILE = "officebean.properties";
public static final String OFFICE_CONN_PROT = "urp";
public static final String OFFICE_BRIDGE_NAME = "officebridge";
public static final String OFFICE_ID_SUFFIX = "_Office";
private String mURL;
private ContainerFactory mContainerFactory;
private XMultiServiceFactory mLocalServiceFactory;
private String mProgramPath;
private String mLibPath;
private String mPipeName;
private XConnection mConnection;
private XBridge mBridge;
private XComponentContext mComponentContext;
private List mComponents = new Vector();
/**
* Static constructor.
*
* Reads the officebean properties file form use's home directory.
*/
static {
try {
String name;
name = (System.getProperty("os.name").startsWith("Windows"))?
OFFICE_PROP_FILE: ("." + OFFICE_PROP_FILE);
File file = new File(System.getProperty("user.home"), name);
if (file.canRead()) {
Properties properties = new Properties();
properties.load(new FileInputStream(file));
Enumeration e = properties.propertyNames();
while (e.hasMoreElements()) {
name = (String)e.nextElement();
if (name.startsWith("com.sun.star.beans.") &&
(System.getProperty(name) == null))
System.setProperty(name, properties.getProperty(name));
}
}
} catch (IOException e) {
}
}
/**
* 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()
{
mProgramPath = System.getProperty("com.sun.star.beans.path");
mLibPath = System.getProperty("com.sun.star.beans.libpath");
}
/**
* Sets a connection URL.
* This implementation accepts a UNO URL with following format:<br />
* <pre>
* 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
* </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;
parseUnoUrl(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 (mComponentContext == null)
connect();
return mComponentContext;
}
/**
* 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) {
((XEventListener)itr.next()).disposing(null);
}
mComponents.clear();
// Close the connection
if (mBridge != null) {
XComponent comp = (XComponent)UnoRuntime.queryInterface(
XComponent.class, mBridge);
comp.dispose();
mComponentContext = null;
mBridge = null;
mConnection = 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 void connect()
{
try {
buildBridge();
} catch (com.sun.star.uno.Exception exp) {
mConnection = null;
}
// Return if the bridge has not been built.
if (mBridge == null)
return;
XMultiServiceFactory factory = null;
try {
// Get the component context.
Object object = null;
object = mBridge.getInstance("StarOffice.ComponentContext");
mComponentContext = (XComponentContext)UnoRuntime.queryInterface(
XComponentContext.class, object);
if ((mComponentContext == null) ||
(mComponentContext.getServiceManager() == null))
{
// We have original StarOffice 6.0 so do it again.
mComponentContext = null;
object = mBridge.getInstance("StarOffice.ServiceManager");
factory = (XMultiServiceFactory)UnoRuntime.queryInterface(
XMultiServiceFactory.class, object);
XPropertySet props = (XPropertySet)UnoRuntime.queryInterface(
XPropertySet.class, object);
mComponentContext = (XComponentContext)AnyConverter.toObject(
new Type(XComponentContext.class),
props.getPropertyValue("DefaultContext"));
}
} catch (java.lang.Exception exp) {
// Unsuccesful connection clean up.
if (mBridge != null) {
XComponent comp;
if (factory != null) {
// Dispose the service manager if it has been created.
comp = (XComponent)UnoRuntime.queryInterface(
XComponent.class, factory);
comp.dispose();
factory = null;
}
// Dispose the bridge.
comp = (XComponent)UnoRuntime.queryInterface(
XComponent.class, mBridge);
comp.dispose();
mBridge = null;
// Release the connection and the component context.
mComponentContext = null;
mConnection = null;
}
}
}
/**
* Builds new UNO bridge to the office.
*/
private void buildBridge()
throws com.sun.star.uno.Exception
{
if (mBridge != null)
return;
if (mConnection == null)
buildConnection();
Object object = mLocalServiceFactory.createInstance(
"com.sun.star.bridge.BridgeFactory");
XBridgeFactory bridgeFactory;
bridgeFactory = (XBridgeFactory)UnoRuntime.queryInterface(
XBridgeFactory.class, object);
mBridge = bridgeFactory.createBridge(
OFFICE_BRIDGE_NAME, OFFICE_CONN_PROT, mConnection, null);
}
/**
* Builds new URP connection to the office.
*/
private void buildConnection()
throws com.sun.star.uno.Exception
{
if (mLocalServiceFactory == null)
buildEnvironment();
NativeConnection connection = new NativeConnection();
connection.connect(new OfficeService());
mConnection = connection;
}
/**
* Builds UNO runtime environment.
*/
private void buildEnvironment()
throws com.sun.star.uno.Exception
{
loadNativeLibrary();
Object object = new JavaLoader();
XImplementationLoader implLoader;
implLoader = (XImplementationLoader)UnoRuntime.queryInterface(
XImplementationLoader.class, object);
object = implLoader.activate(
"com.sun.star.comp.servicemanager.ServiceManager", null, null, null);
// Ensure that we have got a factory
XSingleServiceFactory managerFact;
managerFact = (XSingleServiceFactory)UnoRuntime.queryInterface(
XSingleServiceFactory.class, object);
// Create an instance of the ServiceManager
XMultiServiceFactory serviceMgr;
serviceMgr = (XMultiServiceFactory)UnoRuntime.queryInterface(
XMultiServiceFactory.class, managerFact.createInstance());
// Set the ServiceManager at the JavaLoader with the XInitialization
XInitialization init;
init = (XInitialization)UnoRuntime.queryInterface(
XInitialization.class, implLoader);
Object[] initargs = { serviceMgr };
init.initialize(initargs);
// Use the XSet interface at the ServiceManager to add the factory of
// the loader
XSet serviceMgrXSet = (XSet)UnoRuntime.queryInterface(
XSet.class, serviceMgr);
XSingleServiceFactory singleServiceFact;
// Add the factory of the loader
singleServiceFact = (XSingleServiceFactory)UnoRuntime.queryInterface(
XSingleServiceFactory.class,
implLoader.activate(
"com.sun.star.comp.loader.JavaLoader", null, null, null));
serviceMgrXSet.insert(singleServiceFact);
// Add the service manager
serviceMgrXSet.insert(managerFact);
// Add the factory of the URLResolver
singleServiceFact = (XSingleServiceFactory)UnoRuntime.queryInterface(
XSingleServiceFactory.class,
implLoader.activate(
"com.sun.star.comp.urlresolver.UrlResolver", null, null, null));
serviceMgrXSet.insert(singleServiceFact);
// Add the bridgefactory
singleServiceFact = (XSingleServiceFactory)UnoRuntime.queryInterface(
XSingleServiceFactory.class,
implLoader.activate(
"com.sun.star.comp.bridgefactory.BridgeFactory", null, null, null));
serviceMgrXSet.insert(singleServiceFact);
// Ufff ... that's all
mLocalServiceFactory = serviceMgr;
}
/**
* Loads native libraries.
*/
private void loadNativeLibrary()
{
System.loadLibrary(OFFICE_LIB_NAME);
}
/**
* Retrives a path to the office program folder.
*
* @return The path to the office program folder.
*/
private String getProgramPath()
{
if (mProgramPath == null) {
String path = null;
String name = System.getProperty("os.name");
if (("SunOS".equals(name)) || ("Linux".equals(name))) {
path = System.getProperty("user.home");
path += "/staroffice6.0";
} else if (name.startsWith("Windows")) {
path = "c:\\Program Files\\StarOffice6.0\\program";
} else
return null; // unknown OS!
// Check the directory
if ((new File(path)).isDirectory() == true)
mProgramPath = path;
else
mProgramPath = ""; // default path
}
return mProgramPath;
}
/**
* Retrives a path to the native libraries folder.
* This method depends on ODK directory layout. It expects the root ODK
* directory contains 'classes' directory, where <code>OFFICE_LIB_NAME</code>
* jar file is located, and OS specific directory for native libraries.
*
* @return The path to the native libraries folder.
*/
private String getLibPath()
{
if (mLibPath == null) {
String fsep = System.getProperty("file.separator");
Class info = getClass();
String name = new String(
'/' + info.getName().replace('.', '/') + ".class");
String path = info.getResource(name).getFile();
// Get program directory path
int idx = path.indexOf(
fsep + "classes" + fsep + OFFICE_LIB_NAME + ".jar");
if (idx > 0) {
path = path.substring(0, idx);
// Get ODK directory absolute path
idx = path.indexOf(fsep);
path = path.substring(idx);
// Add OS specific portion of path
name = System.getProperty("os.name");
if ("SunOS".equals(name))
path += "/solsparc/lib";
else if ("Linux".equals(name))
path += "/linux/lib";
else if (name.startsWith("Windows"))
path += fsep + "windows" + fsep + "bin";
else
return null; // unknown OS!
// Check the directory
if ((new File(path)).isDirectory() == true)
mLibPath = path;
}
}
return mLibPath;
}
/**
* Parses a connection URL.
* This method accepts a UNO URL with following format:<br />
* <pre>
* 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
* </pre>
*
* @param url This is UNO URL which describes the type of a connection.
* @exception java.net.MalformedURLException when inappropreate URL was
* provided.
*/
private void parseUnoUrl(String url)
throws java.net.MalformedURLException
{
String prefix = "uno:localoffice";
if (url.startsWith(prefix) == false)
throw new java.net.MalformedURLException(
"Invalid UNO connection URL.");
// 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)
mPipeName = pipe;
}
/**
* @para This is an implementation of the native office service.
*/
private class OfficeService
implements NativeService
{
private Process mProcess;
/**
* Retrive the office service identifier.
*
* @return The identifier of the office service.
*/
public String getIdentifier()
{
return (mPipeName == null)?
(System.getProperty("user.name") + OFFICE_ID_SUFFIX):
mPipeName;
}
/**
* Starts the office process.
*/
public void startupService()
throws java.io.IOException
{
String[] cmdArray = new String[3];
cmdArray[0] = (new File(getProgramPath(), OFFICE_APP_NAME)).getPath();
cmdArray[1] = "-invisible";
cmdArray[2] = "-accept=pipe,name=" + getIdentifier() + ";" +
OFFICE_CONN_PROT + ";StarOffice.NamingService";
mProcess = Runtime.getRuntime().exec(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;
}
}
}