/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.officeimporter.internal.server; import java.io.File; import java.io.InputStream; import javax.inject.Inject; import javax.inject.Singleton; import org.artofsolving.jodconverter.OfficeDocumentConverter; import org.artofsolving.jodconverter.document.JsonDocumentFormatRegistry; import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration; import org.artofsolving.jodconverter.office.ExternalOfficeManagerConfiguration; import org.artofsolving.jodconverter.office.OfficeManager; import org.slf4j.Logger; import org.xwiki.component.annotation.Component; import org.xwiki.environment.Environment; import org.xwiki.officeimporter.converter.OfficeConverter; import org.xwiki.officeimporter.internal.converter.DefaultOfficeConverter; import org.xwiki.officeimporter.server.OfficeServer; import org.xwiki.officeimporter.server.OfficeServerConfiguration; import org.xwiki.officeimporter.server.OfficeServerException; /** * Default {@link OfficeServer} implementation. * * @version $Id: 6e3da402bac998a7461627febb3c2a2c8d3b836b $ * @since 5.0M2 */ @Component @Singleton public class DefaultOfficeServer implements OfficeServer { /** * The path to the file that can be used to configure the office document conversion. */ private static final String DOCUMENT_FORMATS_PATH = "/document-formats.js"; /** * The office server configuration. */ @Inject private OfficeServerConfiguration config; /** * Used to query global temporary working directory. */ @Inject private Environment environment; /** * The logger to log. */ @Inject private Logger logger; /** * Internal {@link OfficeManager} used to control / connect the office server. */ private OfficeManager jodManager; /** * Internal {@link OfficeDocumentConverter} used to convert office documents. */ private OfficeDocumentConverter jodConverter; /** * Current office server process state. */ private ServerState state; /** * Used for carrying out document conversion tasks. */ private OfficeConverter converter; /** * Default constructor. */ public DefaultOfficeServer() { setState(ServerState.NOT_CONNECTED); } /** * Initialize JodConverter. * * @throws OfficeServerException when failed to initialize */ public void initialize() throws OfficeServerException { if (this.config.getServerType() == OfficeServerConfiguration.SERVER_TYPE_INTERNAL) { DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration(); configuration.setPortNumber(this.config.getServerPort()); String homePath = this.config.getHomePath(); if (homePath != null) { configuration.setOfficeHome(homePath); } String profilePath = this.config.getProfilePath(); if (profilePath != null) { configuration.setTemplateProfileDir(new File(profilePath)); } configuration.setMaxTasksPerProcess(this.config.getMaxTasksPerProcess()); configuration.setTaskExecutionTimeout(this.config.getTaskExecutionTimeout()); try { this.jodManager = configuration.buildOfficeManager(); } catch (Exception e) { // Protect against exceptions raised by JodManager. For example if it cannot autodetect the office home, // it'll throw an java.lang.IllegalStateException exception... // We wrap this in an OfficeServerException in order to display some nicer message to the user. throw new OfficeServerException("Failed to start Office server. Reason: " + e.getMessage(), e); } } else if (this.config.getServerType() == OfficeServerConfiguration.SERVER_TYPE_EXTERNAL_LOCAL) { ExternalOfficeManagerConfiguration externalProcessOfficeManager = new ExternalOfficeManagerConfiguration(); externalProcessOfficeManager.setPortNumber(this.config.getServerPort()); externalProcessOfficeManager.setConnectOnStart(true); this.jodManager = externalProcessOfficeManager.buildOfficeManager(); } else { setState(ServerState.CONF_ERROR); throw new OfficeServerException("Invalid office server configuration."); } this.jodConverter = null; // Try to use the JSON document format registry to configure the office document conversion. InputStream input = getClass().getResourceAsStream(DOCUMENT_FORMATS_PATH); if (input != null) { try { this.jodConverter = new OfficeDocumentConverter(this.jodManager, new JsonDocumentFormatRegistry(input)); } catch (Exception e) { this.logger.warn("Failed to parse {} . The default document format registry will be used instead.", DOCUMENT_FORMATS_PATH, e); } } else { this.logger.debug("{} is missing. The default document format registry will be used instead.", DOCUMENT_FORMATS_PATH); } if (this.jodConverter == null) { // Use the default document format registry. this.jodConverter = new OfficeDocumentConverter(this.jodManager); } File workDir = this.environment.getTemporaryDirectory(); this.converter = new DefaultOfficeConverter(this.jodConverter, workDir); } @Override public ServerState getState() { return this.state; } @Override public void start() throws OfficeServerException { // If the office server is running then stop it in order to restart the connection. stop(); initialize(); try { this.jodManager.start(); setState(ServerState.CONNECTED); this.logger.info("Open Office instance started."); } catch (Exception e) { setState(ServerState.ERROR); throw new OfficeServerException("Error while connecting / starting the office server.", e); } } @Override public void stop() throws OfficeServerException { // We should try stopping the office server even if the status is not connected but we should not raise an // error if there is a failure to stop. boolean connected = checkState(ServerState.CONNECTED); try { this.jodManager.stop(); setState(ServerState.NOT_CONNECTED); this.logger.info("Open Office instance stopped."); } catch (Exception e) { if (connected) { setState(ServerState.ERROR); throw new OfficeServerException("Error while disconnecting / shutting down the office server.", e); } } } @Override public OfficeConverter getConverter() { return this.converter; } /** * Utility method for setting the current office manager state. * * @param newState new state. */ private void setState(ServerState newState) { this.state = newState; } /** * Utility method for checking the office manager state. * * @param expectedState expected state. * @return true if office manger is in given state, false otherwise. */ private boolean checkState(ServerState expectedState) { return (this.state == expectedState); } }