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
043/**
044 * @deprecated Since 8.4. See 'soffice' use with {@link org.nuxeo.ecm.platform.convert.plugins.CommandLineConverter} instead
045 */
046@Deprecated
047public class OOoManagerComponent extends DefaultComponent implements OOoManagerService {
048
049    protected static final Log log = LogFactory.getLog(OOoManagerComponent.class);
050
051    private static final String CONNECTION_PROTOCOL_PROPERTY_KEY = "jod.connection.protocol";
052
053    private static final String MAX_TASKS_PER_PROCESS_PROPERTY_KEY = "jod.max.tasks.per.process";
054
055    private static final String OFFICE_HOME_PROPERTY_KEY = "jod.office.home";
056
057    private static final String TASK_EXECUTION_TIMEOUT_PROPERTY_KEY = "jod.task.execution.timeout";
058
059    private static final String TASK_QUEUE_TIMEOUT_PROPERTY_KEY = "jod.task.queue.timeout";
060
061    private static final String TEMPLATE_PROFILE_DIR_PROPERTY_KEY = "jod.template.profile.dir";
062
063    private static final String OFFICE_PIPES_PROPERTY_KEY = "jod.office.pipes";
064
065    private static final String OFFICE_PORTS_PROPERTY_KEY = "jod.office.ports";
066
067    protected static final String CONFIG_EP = "oooManagerConfig";
068
069    private static OfficeManager officeManager;
070
071    protected OOoManagerDescriptor descriptor = new OOoManagerDescriptor();
072
073    protected boolean started = false;
074
075    protected boolean starting = false;
076
077    protected boolean shutingdown = false;
078
079    public OOoManagerDescriptor getDescriptor() {
080        return descriptor;
081    }
082
083    @Override
084    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
085        if (CONFIG_EP.equals(extensionPoint)) {
086            OOoManagerDescriptor desc = (OOoManagerDescriptor) contribution;
087            descriptor = desc;
088        }
089    }
090
091    @Override
092    public void deactivate(ComponentContext context) {
093        stopOOoManager();
094    }
095
096    @Override
097    public OfficeDocumentConverter getDocumentConverter() {
098        if (isOOoManagerStarted()) {
099            return new OfficeDocumentConverter(officeManager);
100        } else {
101            log.error("OfficeManager is not started.");
102            return null;
103        }
104    }
105
106    public void executeTask(OfficeTask task) {
107        if (isOOoManagerStarted()) {
108            officeManager.execute(task);
109        } else {
110            log.error("OfficeManager is not started.");
111        }
112    }
113
114    @Override
115    public void stopOOoManager() {
116        if (isOOoManagerStarted() && !shutingdown) {
117            shutingdown = true;
118            officeManager.stop();
119            started = false;
120            shutingdown = false;
121            log.debug("Stopping ooo manager.");
122        } else {
123            log.debug("OOoManager already stopped..");
124        }
125    }
126
127    @Override
128    public void startOOoManager() throws IOException {
129        DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
130        if (!descriptor.isEnabled()) {
131            return;
132        }
133
134        starting = true;
135
136        try {
137            // Properties configuration
138            String connectionProtocol = Framework.getProperty(CONNECTION_PROTOCOL_PROPERTY_KEY);
139            if (connectionProtocol != null && !"".equals(connectionProtocol)) {
140                if (OfficeConnectionProtocol.PIPE.toString().equals(connectionProtocol)) {
141                    ConfigBuilderHelper.hackClassLoader();
142                    configuration.setConnectionProtocol(OfficeConnectionProtocol.PIPE);
143                } else if (OfficeConnectionProtocol.SOCKET.toString().equals(connectionProtocol)) {
144                    configuration.setConnectionProtocol(OfficeConnectionProtocol.SOCKET);
145                }
146            }
147            String maxTasksPerProcessProperty = Framework.getProperty(MAX_TASKS_PER_PROCESS_PROPERTY_KEY);
148            if (maxTasksPerProcessProperty != null && !"".equals(maxTasksPerProcessProperty)) {
149                Integer maxTasksPerProcess = Integer.valueOf(maxTasksPerProcessProperty);
150                configuration.setMaxTasksPerProcess(maxTasksPerProcess);
151            }
152            String officeHome = Framework.getProperty(OFFICE_HOME_PROPERTY_KEY);
153            if (officeHome != null && !"".equals(officeHome)) {
154                configuration.setOfficeHome(officeHome);
155            }
156
157            String taskExecutionTimeoutProperty = Framework.getProperty(TASK_EXECUTION_TIMEOUT_PROPERTY_KEY);
158            if (taskExecutionTimeoutProperty != null && !"".equals(taskExecutionTimeoutProperty)) {
159                Long taskExecutionTimeout = Long.valueOf(taskExecutionTimeoutProperty);
160                configuration.setTaskExecutionTimeout(taskExecutionTimeout);
161            }
162            String taskQueueTimeoutProperty = Framework.getProperty(TASK_QUEUE_TIMEOUT_PROPERTY_KEY);
163            if (taskQueueTimeoutProperty != null && !"".equals(taskQueueTimeoutProperty)) {
164                Long taskQueueTimeout = Long.valueOf(taskQueueTimeoutProperty);
165                configuration.setTaskQueueTimeout(taskQueueTimeout);
166            }
167            String templateProfileDir = Framework.getProperty(TEMPLATE_PROFILE_DIR_PROPERTY_KEY);
168            if (templateProfileDir != null && !"".equals(templateProfileDir)) {
169                File templateDirectory = new File(templateProfileDir);
170                if (!templateDirectory.exists()) {
171                    try {
172                        FileUtils.forceMkdir(templateDirectory);
173                    } catch (IOException e) {
174                        throw new RuntimeException("I/O Error: could not create JOD templateDirectory");
175                    }
176                }
177                configuration.setTemplateProfileDir(templateDirectory);
178            }
179
180            // Descriptor configuration
181            String pipeNamesProperty = Framework.getProperty(OFFICE_PIPES_PROPERTY_KEY);
182            String[] pipeNames = null;
183            if (pipeNamesProperty != null) {
184                String[] unvalidatedPipeNames = pipeNamesProperty.split(",\\s*");
185                ArrayList<String> validatedPipeNames = new ArrayList<>();
186                // Basic validation to avoid empty strings
187                for (int i = 0; i < unvalidatedPipeNames.length; i++) {
188                    String tmpPipeName = unvalidatedPipeNames[i].trim();
189                    if (tmpPipeName.length() > 0) {
190                        validatedPipeNames.add(tmpPipeName);
191                    }
192                }
193                pipeNames = validatedPipeNames.toArray(new String[0]);
194            } else {
195                pipeNames = descriptor.getPipeNames();
196            }
197            if (pipeNames != null && pipeNames.length != 0) {
198                configuration.setPipeNames(pipeNames);
199            }
200            String portNumbersProperty = Framework.getProperty(OFFICE_PORTS_PROPERTY_KEY);
201            int[] portNumbers = null;
202            if (portNumbersProperty != null) {
203                String[] portStrings = portNumbersProperty.split(",\\s*");
204                ArrayList<Integer> portList = new ArrayList<>();
205                for (int i = 0; i < portStrings.length; i++) {
206                    try {
207                        portList.add(Integer.parseInt(portStrings[i].trim()));
208                    } catch (NumberFormatException e) {
209                        log.error("Ignoring malformed port number: " + portStrings[i]);
210                    }
211                }
212                portNumbers = ArrayUtils.toPrimitive(portList.toArray(new Integer[0]));
213            } else {
214                portNumbers = descriptor.getPortNumbers();
215            }
216            if (portNumbers != null && portNumbers.length != 0) {
217                configuration.setPortNumbers(portNumbers);
218            }
219            try {
220                officeManager = configuration.buildOfficeManager();
221                officeManager.start();
222                started = true;
223                log.debug("Starting ooo manager.");
224            } catch (IllegalStateException | OfficeException e) {
225                started = false;
226                Throwable t = unwrapException(e);
227                log.warn("OpenOffice was not found, JOD Converter " + "won't be available: " + t.getMessage());
228            }
229        } finally {
230            starting = false;
231        }
232    }
233
234    public Throwable unwrapException(Throwable t) {
235        Throwable cause = t.getCause();
236        return cause == null ? t : unwrapException(cause);
237    }
238
239    @Override
240    public void applicationStarted(ComponentContext context) {
241        log.info("Starting OOo manager");
242        Runnable oooStarter = new Runnable() {
243            @Override
244            public void run() {
245                try {
246                    startOOoManager();
247                } catch (IOException e) {
248                    log.error("Could not start OOoManager.", e);
249                }
250            }
251        };
252        Thread oooStarterThread = new Thread(oooStarter);
253        oooStarterThread.setDaemon(true);
254        oooStarterThread.start();
255        log.info("Started OOo Manager");
256    }
257
258    @Override
259    public boolean isOOoManagerStarted() {
260        if (shutingdown) {
261            return false;
262        }
263        if (!starting) {
264            return started;
265        }
266
267        // wait a little bit
268        // while we are starting Ooo
269        for (int i = 0; i < 200; i++) {
270            if (starting) {
271                try {
272                    Thread.sleep(300);
273                } catch (InterruptedException e) {
274                    // NOP
275                }
276            }
277            if (!starting) {
278                return started;
279            }
280        }
281
282        log.error("Timeout on waiting for officeManager to start");
283
284        return started;
285    }
286
287    public OfficeManager getOfficeManager() {
288        return officeManager;
289    }
290}