001/*
002 * (C) Copyright 2006-2017 Nuxeo (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 */
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.OfficeManager;
033import org.artofsolving.jodconverter.office.OfficeTask;
034import org.nuxeo.runtime.api.Framework;
035import org.nuxeo.runtime.model.ComponentContext;
036import org.nuxeo.runtime.model.ComponentInstance;
037import org.nuxeo.runtime.model.DefaultComponent;
038
039/**
040 * @deprecated Since 8.4. See 'soffice' use with {@link org.nuxeo.ecm.platform.convert.plugins.CommandLineConverter} instead
041 */
042@Deprecated
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 OOoState state = new OOoState();
070
071
072    public OOoManagerDescriptor getDescriptor() {
073        return descriptor;
074    }
075
076    @Override
077    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
078        if (CONFIG_EP.equals(extensionPoint)) {
079            OOoManagerDescriptor desc = (OOoManagerDescriptor) contribution;
080            descriptor = desc;
081        }
082    }
083
084    @Override
085    public OfficeDocumentConverter getDocumentConverter() {
086        if (isOOoManagerStarted()) {
087            return new OfficeDocumentConverter(officeManager);
088        } else {
089            log.error("OfficeManager is not started.");
090            return null;
091        }
092    }
093
094    public void executeTask(OfficeTask task) {
095        if (isOOoManagerStarted()) {
096            officeManager.execute(task);
097        } else {
098            log.error("OfficeManager is not started.");
099        }
100    }
101
102    @Override
103    public synchronized void stopOOoManager() throws Exception {
104        if (state.isAlive()) {
105            state.update(OOoState.STOPPING);
106            officeManager.stop();
107            state.update(OOoState.STOPPED);
108            log.debug("Stopping ooo manager.");
109        } else {
110            log.debug("OOoManager already stopped..");
111        }
112    }
113
114    @Override
115    public synchronized void startOOoManager() throws Exception {
116        startOOoManager(false);
117    }
118
119    public synchronized void startOOoManager(boolean async) throws Exception {
120        if (!descriptor.isEnabled()) {
121            return;
122        }
123        if (state.isAlive()) {
124            log.info("OOo manager already started!");
125            return;
126        }
127        state.update(OOoState.STARTING);
128        log.info("Starting OOo manager");
129        if (async) {
130            Runnable oooStarter = new Runnable() {
131                @Override
132                public void run() {
133                    try {
134                        doStartOOoManager();
135                    } catch (Exception e) {
136                        log.error("Could not start OOoManager.", e);
137                    }
138                }
139            };
140            Thread oooStarterThread = new Thread(oooStarter);
141            oooStarterThread.setDaemon(true);
142            oooStarterThread.start();
143        } else {
144            doStartOOoManager();
145        }
146        log.info("Started OOo Manager");
147    }
148
149    private void doStartOOoManager() throws Exception {
150        DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
151
152        // Properties configuration
153        String connectionProtocol = Framework.getProperty(CONNECTION_PROTOCOL_PROPERTY_KEY);
154        if (connectionProtocol != null && !"".equals(connectionProtocol)) {
155            if (OfficeConnectionProtocol.PIPE.toString().equals(connectionProtocol)) {
156                ConfigBuilderHelper.hackClassLoader();
157                configuration.setConnectionProtocol(OfficeConnectionProtocol.PIPE);
158            } else if (OfficeConnectionProtocol.SOCKET.toString().equals(connectionProtocol)) {
159                configuration.setConnectionProtocol(OfficeConnectionProtocol.SOCKET);
160            }
161        }
162        String maxTasksPerProcessProperty = Framework.getProperty(MAX_TASKS_PER_PROCESS_PROPERTY_KEY);
163        if (maxTasksPerProcessProperty != null && !"".equals(maxTasksPerProcessProperty)) {
164            Integer maxTasksPerProcess = Integer.valueOf(maxTasksPerProcessProperty);
165            configuration.setMaxTasksPerProcess(maxTasksPerProcess);
166        }
167        String officeHome = Framework.getProperty(OFFICE_HOME_PROPERTY_KEY);
168        if (officeHome != null && !"".equals(officeHome)) {
169            configuration.setOfficeHome(officeHome);
170        }
171
172        String taskExecutionTimeoutProperty = Framework.getProperty(TASK_EXECUTION_TIMEOUT_PROPERTY_KEY);
173        if (taskExecutionTimeoutProperty != null && !"".equals(taskExecutionTimeoutProperty)) {
174            Long taskExecutionTimeout = Long.valueOf(taskExecutionTimeoutProperty);
175            configuration.setTaskExecutionTimeout(taskExecutionTimeout);
176        }
177        String taskQueueTimeoutProperty = Framework.getProperty(TASK_QUEUE_TIMEOUT_PROPERTY_KEY);
178        if (taskQueueTimeoutProperty != null && !"".equals(taskQueueTimeoutProperty)) {
179            Long taskQueueTimeout = Long.valueOf(taskQueueTimeoutProperty);
180            configuration.setTaskQueueTimeout(taskQueueTimeout);
181        }
182        String templateProfileDir = Framework.getProperty(TEMPLATE_PROFILE_DIR_PROPERTY_KEY);
183        if (templateProfileDir != null && !"".equals(templateProfileDir)) {
184            File templateDirectory = new File(templateProfileDir);
185            if (!templateDirectory.exists()) {
186                try {
187                    FileUtils.forceMkdir(templateDirectory);
188                } catch (IOException e) {
189                    throw new RuntimeException("I/O Error: could not create JOD templateDirectory");
190                }
191            }
192            configuration.setTemplateProfileDir(templateDirectory);
193        }
194
195        // Descriptor configuration
196        String pipeNamesProperty = Framework.getProperty(OFFICE_PIPES_PROPERTY_KEY);
197        String[] pipeNames = null;
198        if (pipeNamesProperty != null) {
199            String[] unvalidatedPipeNames = pipeNamesProperty.split(",\\s*");
200            ArrayList<String> validatedPipeNames = new ArrayList<>();
201            // Basic validation to avoid empty strings
202            for (int i = 0; i < unvalidatedPipeNames.length; i++) {
203                String tmpPipeName = unvalidatedPipeNames[i].trim();
204                if (tmpPipeName.length() > 0) {
205                    validatedPipeNames.add(tmpPipeName);
206                }
207            }
208            pipeNames = validatedPipeNames.toArray(new String[0]);
209        } else {
210            pipeNames = descriptor.getPipeNames();
211        }
212        if (pipeNames != null && pipeNames.length != 0) {
213            configuration.setPipeNames(pipeNames);
214        }
215        String portNumbersProperty = Framework.getProperty(OFFICE_PORTS_PROPERTY_KEY);
216        int[] portNumbers = null;
217        if (portNumbersProperty != null) {
218            String[] portStrings = portNumbersProperty.split(",\\s*");
219            ArrayList<Integer> portList = new ArrayList<>();
220            for (int i = 0; i < portStrings.length; i++) {
221                try {
222                    portList.add(Integer.parseInt(portStrings[i].trim()));
223                } catch (NumberFormatException e) {
224                    log.error("Ignoring malformed port number: " + portStrings[i]);
225                }
226            }
227            portNumbers = ArrayUtils.toPrimitive(portList.toArray(new Integer[0]));
228        } else {
229            portNumbers = descriptor.getPortNumbers();
230        }
231        if (portNumbers != null && portNumbers.length != 0) {
232            configuration.setPortNumbers(portNumbers);
233        }
234        try {
235            officeManager = configuration.buildOfficeManager();
236            officeManager.start();
237            state.update(OOoState.STARTED);
238            log.debug("Starting ooo manager.");
239        } catch (Throwable e) {
240            state.update(OOoState.STOPPED);
241            Throwable t = unwrapException(e);
242            log.warn("OpenOffice was not found, JOD Converter " + "won't be available: " + t.getMessage());
243        }
244    }
245
246    public Throwable unwrapException(Throwable t) {
247        Throwable cause = t.getCause();
248        return cause == null ? t : unwrapException(cause);
249    }
250
251    @Override
252    public void start(ComponentContext context) {
253        try {
254            startOOoManager(true);
255        } catch (Exception e) {
256            log.error("Failed to start OOoManager", e);
257        }
258    }
259
260    @Override
261    public void stop(ComponentContext context) {
262        try {
263            stopOOoManager();
264        } catch (Exception e) {
265            log.error("Failed to stop OOoManager.", e);
266        }
267    }
268
269    @Override
270    public boolean isOOoManagerStarted() {
271        try {
272            return state.isAlive();
273        } catch (InterruptedException e) {
274            log.warn("Thread interrupted while checkin OOo manager state");
275            return false;
276        }
277    }
278
279    public OfficeManager getOfficeManager() {
280        return officeManager;
281    }
282
283    public OOoState getState() {
284        return state;
285    }
286
287}