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