001/* 002 * (C) Copyright 2006-2015 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nuxeo - initial API and implementation 016 * 017 */ 018 019package org.nuxeo.ecm.platform.convert.ooomanager; 020 021import java.io.File; 022import java.io.IOException; 023import java.util.ArrayList; 024 025import org.apache.commons.io.FileUtils; 026import org.apache.commons.lang.ArrayUtils; 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.artofsolving.jodconverter.OfficeDocumentConverter; 030import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration; 031import org.artofsolving.jodconverter.office.OfficeConnectionProtocol; 032import org.artofsolving.jodconverter.office.OfficeException; 033import org.artofsolving.jodconverter.office.OfficeManager; 034import org.artofsolving.jodconverter.office.OfficeTask; 035 036import org.nuxeo.runtime.api.Framework; 037import org.nuxeo.runtime.model.ComponentContext; 038import org.nuxeo.runtime.model.ComponentInstance; 039import org.nuxeo.runtime.model.DefaultComponent; 040 041public class OOoManagerComponent extends DefaultComponent implements OOoManagerService { 042 043 protected static final Log log = LogFactory.getLog(OOoManagerComponent.class); 044 045 private static final String CONNECTION_PROTOCOL_PROPERTY_KEY = "jod.connection.protocol"; 046 047 private static final String MAX_TASKS_PER_PROCESS_PROPERTY_KEY = "jod.max.tasks.per.process"; 048 049 private static final String OFFICE_HOME_PROPERTY_KEY = "jod.office.home"; 050 051 private static final String TASK_EXECUTION_TIMEOUT_PROPERTY_KEY = "jod.task.execution.timeout"; 052 053 private static final String TASK_QUEUE_TIMEOUT_PROPERTY_KEY = "jod.task.queue.timeout"; 054 055 private static final String TEMPLATE_PROFILE_DIR_PROPERTY_KEY = "jod.template.profile.dir"; 056 057 private static final String OFFICE_PIPES_PROPERTY_KEY = "jod.office.pipes"; 058 059 private static final String OFFICE_PORTS_PROPERTY_KEY = "jod.office.ports"; 060 061 protected static final String CONFIG_EP = "oooManagerConfig"; 062 063 private static OfficeManager officeManager; 064 065 protected OOoManagerDescriptor descriptor = new OOoManagerDescriptor(); 066 067 protected boolean started = false; 068 069 protected boolean starting = false; 070 071 protected boolean shutingdown = false; 072 073 public OOoManagerDescriptor getDescriptor() { 074 return descriptor; 075 } 076 077 @Override 078 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 079 if (CONFIG_EP.equals(extensionPoint)) { 080 OOoManagerDescriptor desc = (OOoManagerDescriptor) contribution; 081 descriptor = desc; 082 } 083 } 084 085 @Override 086 public void deactivate(ComponentContext context) { 087 stopOOoManager(); 088 } 089 090 @Override 091 public OfficeDocumentConverter getDocumentConverter() { 092 if (isOOoManagerStarted()) { 093 return new OfficeDocumentConverter(officeManager); 094 } else { 095 log.error("OfficeManager is not started."); 096 return null; 097 } 098 } 099 100 public void executeTask(OfficeTask task) { 101 if (isOOoManagerStarted()) { 102 officeManager.execute(task); 103 } else { 104 log.error("OfficeManager is not started."); 105 } 106 } 107 108 @Override 109 public void stopOOoManager() { 110 if (isOOoManagerStarted() && !shutingdown) { 111 shutingdown = true; 112 officeManager.stop(); 113 started = false; 114 shutingdown = false; 115 log.debug("Stopping ooo manager."); 116 } else { 117 log.debug("OOoManager already stopped.."); 118 } 119 } 120 121 @Override 122 public void startOOoManager() throws IOException { 123 DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration(); 124 125 starting = true; 126 127 try { 128 // Properties configuration 129 String connectionProtocol = Framework.getProperty(CONNECTION_PROTOCOL_PROPERTY_KEY); 130 if (connectionProtocol != null && !"".equals(connectionProtocol)) { 131 if (OfficeConnectionProtocol.PIPE.toString().equals(connectionProtocol)) { 132 ConfigBuilderHelper.hackClassLoader(); 133 configuration.setConnectionProtocol(OfficeConnectionProtocol.PIPE); 134 } else if (OfficeConnectionProtocol.SOCKET.toString().equals(connectionProtocol)) { 135 configuration.setConnectionProtocol(OfficeConnectionProtocol.SOCKET); 136 } 137 } 138 String maxTasksPerProcessProperty = Framework.getProperty(MAX_TASKS_PER_PROCESS_PROPERTY_KEY); 139 if (maxTasksPerProcessProperty != null && !"".equals(maxTasksPerProcessProperty)) { 140 Integer maxTasksPerProcess = Integer.valueOf(maxTasksPerProcessProperty); 141 configuration.setMaxTasksPerProcess(maxTasksPerProcess); 142 } 143 String officeHome = Framework.getProperty(OFFICE_HOME_PROPERTY_KEY); 144 if (officeHome != null && !"".equals(officeHome)) { 145 configuration.setOfficeHome(officeHome); 146 } 147 148 String taskExecutionTimeoutProperty = Framework.getProperty(TASK_EXECUTION_TIMEOUT_PROPERTY_KEY); 149 if (taskExecutionTimeoutProperty != null && !"".equals(taskExecutionTimeoutProperty)) { 150 Long taskExecutionTimeout = Long.valueOf(taskExecutionTimeoutProperty); 151 configuration.setTaskExecutionTimeout(taskExecutionTimeout); 152 } 153 String taskQueueTimeoutProperty = Framework.getProperty(TASK_QUEUE_TIMEOUT_PROPERTY_KEY); 154 if (taskQueueTimeoutProperty != null && !"".equals(taskQueueTimeoutProperty)) { 155 Long taskQueueTimeout = Long.valueOf(taskQueueTimeoutProperty); 156 configuration.setTaskQueueTimeout(taskQueueTimeout); 157 } 158 String templateProfileDir = Framework.getProperty(TEMPLATE_PROFILE_DIR_PROPERTY_KEY); 159 if (templateProfileDir != null && !"".equals(templateProfileDir)) { 160 File templateDirectory = new File(templateProfileDir); 161 if (!templateDirectory.exists()) { 162 try { 163 FileUtils.forceMkdir(templateDirectory); 164 } catch (IOException e) { 165 throw new RuntimeException("I/O Error: could not create JOD templateDirectory"); 166 } 167 } 168 configuration.setTemplateProfileDir(templateDirectory); 169 } 170 171 // Descriptor configuration 172 String pipeNamesProperty = Framework.getProperty(OFFICE_PIPES_PROPERTY_KEY); 173 String[] pipeNames = null; 174 if (pipeNamesProperty != null) { 175 String[] unvalidatedPipeNames = pipeNamesProperty.split(",\\s*"); 176 ArrayList<String> validatedPipeNames = new ArrayList<>(); 177 // Basic validation to avoid empty strings 178 for (int i = 0; i < unvalidatedPipeNames.length; i++) { 179 String tmpPipeName = unvalidatedPipeNames[i].trim(); 180 if (tmpPipeName.length() > 0) { 181 validatedPipeNames.add(tmpPipeName); 182 } 183 } 184 pipeNames = validatedPipeNames.toArray(new String[0]); 185 } else { 186 pipeNames = descriptor.getPipeNames(); 187 } 188 if (pipeNames != null && pipeNames.length != 0) { 189 configuration.setPipeNames(pipeNames); 190 } 191 String portNumbersProperty = Framework.getProperty(OFFICE_PORTS_PROPERTY_KEY); 192 int[] portNumbers = null; 193 if (portNumbersProperty != null) { 194 String[] portStrings = portNumbersProperty.split(",\\s*"); 195 ArrayList<Integer> portList = new ArrayList<>(); 196 for (int i = 0; i < portStrings.length; i++) { 197 try { 198 portList.add(Integer.parseInt(portStrings[i].trim())); 199 } catch (NumberFormatException e) { 200 log.error("Ignoring malformed port number: " + portStrings[i]); 201 } 202 } 203 portNumbers = ArrayUtils.toPrimitive(portList.toArray(new Integer[0])); 204 } else { 205 portNumbers = descriptor.getPortNumbers(); 206 } 207 if (portNumbers != null && portNumbers.length != 0) { 208 configuration.setPortNumbers(portNumbers); 209 } 210 try { 211 officeManager = configuration.buildOfficeManager(); 212 officeManager.start(); 213 started = true; 214 log.debug("Starting ooo manager."); 215 } catch (IllegalStateException | OfficeException e) { 216 started = false; 217 Throwable t = unwrapException(e); 218 log.warn("OpenOffice was not found, JOD Converter " + "won't be available: " + t.getMessage()); 219 } 220 } finally { 221 starting = false; 222 } 223 } 224 225 public Throwable unwrapException(Throwable t) { 226 Throwable cause = t.getCause(); 227 return cause == null ? t : unwrapException(cause); 228 } 229 230 @Override 231 public void applicationStarted(ComponentContext context) { 232 log.info("Starting OOo manager"); 233 Runnable oooStarter = new Runnable() { 234 @Override 235 public void run() { 236 try { 237 startOOoManager(); 238 } catch (IOException e) { 239 log.error("Could not start OOoManager.", e); 240 } 241 } 242 }; 243 Thread oooStarterThread = new Thread(oooStarter); 244 oooStarterThread.setDaemon(true); 245 oooStarterThread.start(); 246 log.info("Started OOo Manager"); 247 } 248 249 @Override 250 public boolean isOOoManagerStarted() { 251 if (shutingdown) { 252 return false; 253 } 254 if (!starting) { 255 return started; 256 } 257 258 // wait a little bit 259 // while we are starting Ooo 260 for (int i = 0; i < 200; i++) { 261 if (starting) { 262 try { 263 Thread.sleep(300); 264 } catch (InterruptedException e) { 265 // NOP 266 } 267 } 268 if (!starting) { 269 return started; 270 } 271 } 272 273 log.error("Timeout on waiting for officeManager to start"); 274 275 return started; 276 } 277 278 public OfficeManager getOfficeManager() { 279 return officeManager; 280 } 281}