diff --git a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties index 12530bcb5a9c9107313f60cb0d45c28e3ab60884..598a943a77ed9a7fa90f7a69a17a9dd6bdcf35be 100755 --- a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties +++ b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties @@ -78,6 +78,11 @@ imageTagThreshold=8000 # Maximum allowed number of define text tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 2500) defineTextThreshold=2500 +#------------------------------------ +# Number of threads in the pool to do the presentation conversion. +#------------------------------------ +numConversionThreads=2 + #---------------------------------------------------- # Additional conversion of the presentation slides to SVG # to be used in the HTML5 client diff --git a/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml b/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml index 271051c05535e4c853e72d8347491d824630994c..148c479fcafec9c25d19a4caef259243718c1350 100755 --- a/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml +++ b/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml @@ -92,6 +92,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. <bean id="generatedSlidesInfoHelper" class="org.bigbluebutton.presentation.GeneratedSlidesInfoHelperImp"/> <bean id="pdfToSwfSlidesGenerationService" class="org.bigbluebutton.presentation.imp.PdfToSwfSlidesGenerationService"> + <constructor-arg index="0" value="${numConversionThreads}"/> <property name="counterService" ref="pageCounterService"/> <property name="pageConverter" ref="pdf2SwfPageConverter"/> <property name="pdfPageToImageConversionService" ref="imageConvSvc"/> diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/ImageToSwfSlide.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/ImageToSwfSlide.java index 85112e57767da10bc80934c51f6f1dc958a9edb4..b39875d4dcda483abcd62b3e5118ce636f90ca9e 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/ImageToSwfSlide.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/ImageToSwfSlide.java @@ -1,21 +1,21 @@ /** -* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ -* -* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). -* -* This program 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 3.0 of the License, or (at your option) any later -* version. -* -* BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. -* -*/ + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program 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 3.0 of the License, or (at your option) any later + * version. + * + * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + */ package org.bigbluebutton.presentation; @@ -27,68 +27,68 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ImageToSwfSlide { - private static Logger log = LoggerFactory.getLogger(ImageToSwfSlide.class); - - private UploadedPresentation pres; - private int page; - - private PageConverter imageToSwfConverter; - private String BLANK_SLIDE; - - private boolean done = false; - private File slide; - - public ImageToSwfSlide(UploadedPresentation pres, int page) { - this.pres = pres; - this.page = page; - } - - public ImageToSwfSlide createSlide() { - File presentationFile = pres.getUploadedFile(); - slide = new File(presentationFile.getParent() + File.separatorChar + "slide-" + page + ".swf"); - log.debug("Creating slide " + slide.getAbsolutePath()); - imageToSwfConverter.convert(presentationFile, slide, page); - - // If all fails, generate a blank slide. - if (!slide.exists()) { - log.warn("Creating blank slide for " + slide.getAbsolutePath()); - generateBlankSlide(); - } - - done = true; - - return this; - } - - public void generateBlankSlide() { - if (BLANK_SLIDE != null) { - copyBlankSlide(slide); - } else { - log.error("Blank slide has not been set"); - } - } - - private void copyBlankSlide(File slide) { - try { - FileUtils.copyFile(new File(BLANK_SLIDE), slide); - } catch (IOException e) { - log.error("IOException while copying blank slide."); - } - } - - public void setPageConverter(PageConverter converter) { - this.imageToSwfConverter = converter; - } - - public void setBlankSlide(String blankSlide) { - this.BLANK_SLIDE = blankSlide; - } - - public boolean isDone() { - return done; - } - - public int getPageNumber() { - return page; - } + private static Logger log = LoggerFactory.getLogger(ImageToSwfSlide.class); + + private UploadedPresentation pres; + private int page; + + private PageConverter imageToSwfConverter; + private String BLANK_SLIDE; + + private boolean done = false; + private File slide; + + public ImageToSwfSlide(UploadedPresentation pres, int page) { + this.pres = pres; + this.page = page; + } + + public ImageToSwfSlide createSlide() { + File presentationFile = pres.getUploadedFile(); + slide = new File(presentationFile.getParent() + File.separatorChar + "slide-" + page + ".swf"); + log.debug("Creating slide " + slide.getAbsolutePath()); + imageToSwfConverter.convert(presentationFile, slide, page, pres); + + // If all fails, generate a blank slide. + if (!slide.exists()) { + log.warn("Creating blank slide for " + slide.getAbsolutePath()); + generateBlankSlide(); + } + + done = true; + + return this; + } + + public void generateBlankSlide() { + if (BLANK_SLIDE != null) { + copyBlankSlide(slide); + } else { + log.error("Blank slide has not been set"); + } + } + + private void copyBlankSlide(File slide) { + try { + FileUtils.copyFile(new File(BLANK_SLIDE), slide); + } catch (IOException e) { + log.error("IOException while copying blank slide."); + } + } + + public void setPageConverter(PageConverter converter) { + this.imageToSwfConverter = converter; + } + + public void setBlankSlide(String blankSlide) { + this.BLANK_SLIDE = blankSlide; + } + + public boolean isDone() { + return done; + } + + public int getPageNumber() { + return page; + } } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PageConverter.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PageConverter.java index dc639d9ef8cd0e2763e855995d6d2112e7e48616..c8120a49db47bccee116df8c78b945af714756d7 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PageConverter.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PageConverter.java @@ -22,5 +22,5 @@ package org.bigbluebutton.presentation; import java.io.File; public interface PageConverter { - public boolean convert(File presentation, File output, int page); + public boolean convert(File presentation, File output, int page, UploadedPresentation pres); } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PdfToSwfSlide.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PdfToSwfSlide.java index 93921c67b82d0c00c4b65da729083947626e708e..7abc43627377763ca46e4c9c4f683cb27dba86eb 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PdfToSwfSlide.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PdfToSwfSlide.java @@ -1,119 +1,148 @@ /** -* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ -* -* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). -* -* This program 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 3.0 of the License, or (at your option) any later -* version. -* -* BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. -* -*/ + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program 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 3.0 of the License, or (at your option) any later + * version. + * + * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + */ package org.bigbluebutton.presentation; import java.io.File; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import org.apache.commons.io.FileUtils; import org.bigbluebutton.presentation.imp.PdfPageToImageConversionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.Gson; + public class PdfToSwfSlide { - private static Logger log = LoggerFactory.getLogger(PdfToSwfSlide.class); - - private UploadedPresentation pres; - private int page; - - private PageConverter pdfToSwfConverter; - private PdfPageToImageConversionService imageConvertService; - private String BLANK_SLIDE; - private int MAX_SWF_FILE_SIZE; - - private volatile boolean done = false; - private File slide; - - public PdfToSwfSlide(UploadedPresentation pres, int page) { - this.pres = pres; - this.page = page; - } - - public PdfToSwfSlide createSlide() { - File presentationFile = pres.getUploadedFile(); - slide = new File(presentationFile.getParent() + File.separatorChar + "slide-" + page + ".swf"); - if (! pdfToSwfConverter.convert(presentationFile, slide, page)) { - log.info("Failed to convert slide. Let's take an image snapshot and convert to SWF"); - imageConvertService.convertPageAsAnImage(presentationFile, slide, page); - } else { - if (slideMayHaveTooManyObjects(slide)) { - log.info("Slide is too big. Let's take an image snapshot and convert to SWF"); - imageConvertService.convertPageAsAnImage(presentationFile, slide, page); - } - } - - // If all fails, generate a blank slide. - if (!slide.exists()) { - log.warn("Failed to create slide. Creating blank slide for " + slide.getAbsolutePath()); - generateBlankSlide(); - } - - done = true; - - return this; - } - - private boolean slideMayHaveTooManyObjects(File slide) { - // If the resulting swf file is greater than 500K, it probably contains a lot of objects - // that it becomes very slow to render on the client. Take an image snapshot instead and - // use it to generate the SWF file. (ralam Sept 2, 2009) - return slide.length() > MAX_SWF_FILE_SIZE; - } - - public void generateBlankSlide() { - if (BLANK_SLIDE != null) { - copyBlankSlide(slide); - } else { - log.error("Blank slide has not been set"); - } - } - - private void copyBlankSlide(File slide) { - try { - FileUtils.copyFile(new File(BLANK_SLIDE), slide); - } catch (IOException e) { - log.error("IOException while copying blank slide."); - } - } - - public void setPageConverter(PageConverter converter) { - this.pdfToSwfConverter = converter; - } - - public void setPdfPageToImageConversionService(PdfPageToImageConversionService service) { - this.imageConvertService = service; - } - - public void setBlankSlide(String blankSlide) { - this.BLANK_SLIDE = blankSlide; - } - - public void setMaxSwfFileSize(int size) { - this.MAX_SWF_FILE_SIZE = size; - } - - public boolean isDone() { - return done; - } - - public int getPageNumber() { - return page; - } + private static Logger log = LoggerFactory.getLogger(PdfToSwfSlide.class); + + private UploadedPresentation pres; + private int page; + private PageConverter pdfToSwfConverter; + private PdfPageToImageConversionService imageConvertService; + private String BLANK_SLIDE; + private int MAX_SWF_FILE_SIZE; + + private volatile boolean done = false; + private File slide; + + public PdfToSwfSlide(UploadedPresentation pres, int page) { + this.pres = pres; + this.page = page; + } + + public PdfToSwfSlide createSlide() { + File presentationFile = pres.getUploadedFile(); + slide = new File(presentationFile.getParent() + File.separatorChar + "slide-" + page + ".swf"); + if (! pdfToSwfConverter.convert(presentationFile, slide, page, pres)) { + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + logData.put("page", page); + logData.put("size(KB)", slide.length()/1024); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.warn("Failed to convert slide: data={}", logStr); + + imageConvertService.convertPageAsAnImage(presentationFile, slide, page, pres); + } + + // If all fails, generate a blank slide. + if (!slide.exists()) { + log.warn("Failed to create slide. Creating blank slide for " + slide.getAbsolutePath()); + generateBlankSlide(); + } + + done = true; + + return this; + } + + private boolean slideMayHaveTooManyObjects(File slide) { + // If the resulting swf file is greater than 500K, it probably contains a lot of objects + // that it becomes very slow to render on the client. Take an image snapshot instead and + // use it to generate the SWF file. (ralam Sept 2, 2009) + return slide.length() > MAX_SWF_FILE_SIZE; + } + + public void generateBlankSlide() { + if (BLANK_SLIDE != null) { + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + logData.put("page", page); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.warn("Creating blank slide: data={}", logStr); + + copyBlankSlide(slide); + } else { + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + logData.put("page", page); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.warn("Failed to create blank slide: data={}", logStr); + } + } + + private void copyBlankSlide(File slide) { + try { + FileUtils.copyFile(new File(BLANK_SLIDE), slide); + } catch (IOException e) { + log.error("IOException while copying blank slide."); + } + } + + public void setPageConverter(PageConverter converter) { + this.pdfToSwfConverter = converter; + } + + public void setPdfPageToImageConversionService(PdfPageToImageConversionService service) { + this.imageConvertService = service; + } + + public void setBlankSlide(String blankSlide) { + this.BLANK_SLIDE = blankSlide; + } + + public void setMaxSwfFileSize(int size) { + this.MAX_SWF_FILE_SIZE = size; + } + + public boolean isDone() { + return done; + } + + public int getPageNumber() { + return page; + } } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/ImageMagickPageConverter.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/ImageMagickPageConverter.java index c7fcf0c7658c791338d11852eb106fa1d91b7f26..05985617a3c4d4fe0aafaabfbd1dadf5803d0478 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/ImageMagickPageConverter.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/ImageMagickPageConverter.java @@ -1,52 +1,54 @@ /** -* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ -* -* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). -* -* This program 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 3.0 of the License, or (at your option) any later -* version. -* -* BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. -* -*/ + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program 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 3.0 of the License, or (at your option) any later + * version. + * + * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + */ package org.bigbluebutton.presentation.imp; import java.io.File; + import org.bigbluebutton.presentation.PageConverter; +import org.bigbluebutton.presentation.UploadedPresentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ImageMagickPageConverter implements PageConverter { - private static Logger log = LoggerFactory.getLogger(ImageMagickPageConverter.class); - - private String IMAGEMAGICK_DIR; - - public boolean convert(File presentationFile, File output, int page){ - - String COMMAND = IMAGEMAGICK_DIR + "/convert -depth 8 " + presentationFile.getAbsolutePath() + " " + output.getAbsolutePath(); - - boolean done = new ExternalProcessExecutor().exec(COMMAND, 60000); - - if (done && output.exists()) { - return true; - } else { - log.warn("Failed to convert: " + output.getAbsolutePath() + " does not exist."); - return false; - } - - } - - public void setImageMagickDir(String dir) { - IMAGEMAGICK_DIR = dir; - } + private static Logger log = LoggerFactory.getLogger(ImageMagickPageConverter.class); + + private String IMAGEMAGICK_DIR; + + public boolean convert(File presentationFile, File output, int page, UploadedPresentation pres){ + + String COMMAND = IMAGEMAGICK_DIR + "/convert -depth 8 " + presentationFile.getAbsolutePath() + " " + output.getAbsolutePath(); + + boolean done = new ExternalProcessExecutor().exec(COMMAND, 60000); + + if (done && output.exists()) { + return true; + } else { + log.warn("Failed to convert: " + output.getAbsolutePath() + " does not exist."); + return false; + } + + } + + public void setImageMagickDir(String dir) { + IMAGEMAGICK_DIR = dir; + } } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Jpeg2SwfPageConverter.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Jpeg2SwfPageConverter.java index 97dc83570f99552fa66252de7469c5414d996321..16e4b280565f489a1bdaf707901f7a53b3b2ed86 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Jpeg2SwfPageConverter.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Jpeg2SwfPageConverter.java @@ -20,7 +20,9 @@ package org.bigbluebutton.presentation.imp; import java.io.File; + import org.bigbluebutton.presentation.PageConverter; +import org.bigbluebutton.presentation.UploadedPresentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +31,7 @@ public class Jpeg2SwfPageConverter implements PageConverter { private String SWFTOOLS_DIR; - public boolean convert(File presentationFile, File output, int page){ + public boolean convert(File presentationFile, File output, int page, UploadedPresentation pres){ String COMMAND = SWFTOOLS_DIR + "/jpeg2swf -o " + output.getAbsolutePath() + " " + presentationFile.getAbsolutePath(); diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Office2PdfPageConverter.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Office2PdfPageConverter.java index 62061e59754d44571edeb4a8f6d7475e795aa2ab..9a0eeadb8db8c87c6f619d52f1fa5b09088299ad 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Office2PdfPageConverter.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Office2PdfPageConverter.java @@ -1,21 +1,21 @@ /** -* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ -* -* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). -* -* This program 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 3.0 of the License, or (at your option) any later -* version. -* -* BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. -* -*/ + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program 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 3.0 of the License, or (at your option) any later + * version. + * + * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + */ package org.bigbluebutton.presentation.imp; @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.Map; import org.bigbluebutton.presentation.PageConverter; +import org.bigbluebutton.presentation.UploadedPresentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,39 +33,39 @@ import com.artofsolving.jodconverter.openoffice.connection.*; import com.artofsolving.jodconverter.openoffice.converter.*; public class Office2PdfPageConverter implements PageConverter { - private static Logger log = LoggerFactory.getLogger(Office2PdfPageConverter.class); - - public boolean convert(File presentationFile, File output, int page){ - SocketOpenOfficeConnection connection = new SocketOpenOfficeConnection(8100); + private static Logger log = LoggerFactory.getLogger(Office2PdfPageConverter.class); - try { - connection.connect(); - - log.debug("Converting " + presentationFile.getAbsolutePath() + " to " + output.getAbsolutePath()); - - DefaultDocumentFormatRegistry registry = new DefaultDocumentFormatRegistry(); - OpenOfficeDocumentConverter converter = new OpenOfficeDocumentConverter(connection, registry); + public boolean convert(File presentationFile, File output, int page, UploadedPresentation pres){ + SocketOpenOfficeConnection connection = new SocketOpenOfficeConnection(8100); - DocumentFormat pdf = registry.getFormatByFileExtension("pdf"); - Map<String, Object> pdfOptions = new HashMap<String, Object>(); - pdfOptions.put("ReduceImageResolution", Boolean.TRUE); - pdfOptions.put("MaxImageResolution", Integer.valueOf(300)); - pdf.setExportOption(DocumentFamily.TEXT, "FilterData", pdfOptions); - - converter.convert(presentationFile, output, pdf); - connection.disconnect(); - - if (output.exists()) { - return true; - } else { - log.warn("Failed to convert: " + output.getAbsolutePath() + " does not exist."); - return false; - } - - } catch(Exception e) { - log.error("Exception: Failed to convert " + output.getAbsolutePath()); - return false; - } - } + try { + connection.connect(); + + log.debug("Converting " + presentationFile.getAbsolutePath() + " to " + output.getAbsolutePath()); + + DefaultDocumentFormatRegistry registry = new DefaultDocumentFormatRegistry(); + OpenOfficeDocumentConverter converter = new OpenOfficeDocumentConverter(connection, registry); + + DocumentFormat pdf = registry.getFormatByFileExtension("pdf"); + Map<String, Object> pdfOptions = new HashMap<String, Object>(); + pdfOptions.put("ReduceImageResolution", Boolean.TRUE); + pdfOptions.put("MaxImageResolution", Integer.valueOf(300)); + pdf.setExportOption(DocumentFamily.TEXT, "FilterData", pdfOptions); + + converter.convert(presentationFile, output, pdf); + connection.disconnect(); + + if (output.exists()) { + return true; + } else { + log.warn("Failed to convert: " + output.getAbsolutePath() + " does not exist."); + return false; + } + + } catch(Exception e) { + log.error("Exception: Failed to convert " + output.getAbsolutePath()); + return false; + } + } } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java index 24156a351a59bf4b19ac8ef9772ab717d0fb781c..9dd17f4f17f7478dfb9173a569f0a21c6c86b20f 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java @@ -1,21 +1,21 @@ /** -* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ -* -* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). -* -* This program 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 3.0 of the License, or (at your option) any later -* version. -* -* BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. -* -*/ + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program 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 3.0 of the License, or (at your option) any later + * version. + * + * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + */ package org.bigbluebutton.presentation.imp; @@ -27,44 +27,44 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class OfficeToPdfConversionService { - private static Logger log = LoggerFactory.getLogger(OfficeToPdfConversionService.class); - - /* - * Convert the Office document to PDF. If successful, update - * UploadPresentation.uploadedFile with the new PDF out and - * UploadPresentation.lastStepSuccessful to TRUE. - */ - public UploadedPresentation convertOfficeToPdf(UploadedPresentation pres) { - initialize(pres); - if (SupportedFileTypes.isOfficeFile(pres.getFileType())) { - File pdfOutput = setupOutputPdfFile(pres); - if (convertOfficeDocToPdf(pres, pdfOutput)) { - log.info("Successfully converted office file to pdf."); - makePdfTheUploadedFileAndSetStepAsSuccess(pres, pdfOutput); - } else { - log.warn("Failed to convert " + pres.getUploadedFile().getAbsolutePath() + " to Pdf."); - } - } - return pres; - } - - public void initialize(UploadedPresentation pres) { - pres.setLastStepSuccessful(false); - } - - private File setupOutputPdfFile(UploadedPresentation pres) { - File presentationFile = pres.getUploadedFile(); - String filenameWithoutExt = presentationFile.getAbsolutePath().substring(0, presentationFile.getAbsolutePath().lastIndexOf(".")); - return new File(filenameWithoutExt + ".pdf"); - } - - private boolean convertOfficeDocToPdf(UploadedPresentation pres, File pdfOutput) { - PageConverter converter = new Office2PdfPageConverter(); - return converter.convert(pres.getUploadedFile(), pdfOutput, 0); - } - - private void makePdfTheUploadedFileAndSetStepAsSuccess(UploadedPresentation pres, File pdf) { - pres.setUploadedFile(pdf); - pres.setLastStepSuccessful(true); - } + private static Logger log = LoggerFactory.getLogger(OfficeToPdfConversionService.class); + + /* + * Convert the Office document to PDF. If successful, update + * UploadPresentation.uploadedFile with the new PDF out and + * UploadPresentation.lastStepSuccessful to TRUE. + */ + public UploadedPresentation convertOfficeToPdf(UploadedPresentation pres) { + initialize(pres); + if (SupportedFileTypes.isOfficeFile(pres.getFileType())) { + File pdfOutput = setupOutputPdfFile(pres); + if (convertOfficeDocToPdf(pres, pdfOutput)) { + log.info("Successfully converted office file to pdf."); + makePdfTheUploadedFileAndSetStepAsSuccess(pres, pdfOutput); + } else { + log.warn("Failed to convert " + pres.getUploadedFile().getAbsolutePath() + " to Pdf."); + } + } + return pres; + } + + public void initialize(UploadedPresentation pres) { + pres.setLastStepSuccessful(false); + } + + private File setupOutputPdfFile(UploadedPresentation pres) { + File presentationFile = pres.getUploadedFile(); + String filenameWithoutExt = presentationFile.getAbsolutePath().substring(0, presentationFile.getAbsolutePath().lastIndexOf(".")); + return new File(filenameWithoutExt + ".pdf"); + } + + private boolean convertOfficeDocToPdf(UploadedPresentation pres, File pdfOutput) { + PageConverter converter = new Office2PdfPageConverter(); + return converter.convert(pres.getUploadedFile(), pdfOutput, 0, pres); + } + + private void makePdfTheUploadedFileAndSetStepAsSuccess(UploadedPresentation pres, File pdf) { + pres.setUploadedFile(pdf); + pres.setLastStepSuccessful(true); + } } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageConverter.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageConverter.java index d41b02f94d0cfe137758167649cdfd18a979e521..42ae5dcfbf46b5070cac2aba5daaa08c3ab36853 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageConverter.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageConverter.java @@ -22,16 +22,20 @@ package org.bigbluebutton.presentation.imp; import java.io.File; import java.io.IOException; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.commons.io.FilenameUtils; import org.bigbluebutton.presentation.PageConverter; +import org.bigbluebutton.presentation.UploadedPresentation; import org.bigbluebutton.presentation.handlers.Pdf2PngPageConverterHandler; import org.bigbluebutton.presentation.handlers.Pdf2SwfPageConverterHandler; import org.bigbluebutton.presentation.handlers.Png2SwfPageConverterHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.Gson; import com.zaxxer.nuprocess.NuAbstractProcessHandler; import com.zaxxer.nuprocess.NuProcess; import com.zaxxer.nuprocess.NuProcessBuilder; @@ -48,8 +52,12 @@ public class Pdf2SwfPageConverter implements PageConverter { private long placementsThreshold; private long defineTextThreshold; private long imageTagThreshold; + private String convTimeout = "5s"; + private int WAIT_FOR_SEC = 6; + + public boolean convert(File presentation, File output, int page, UploadedPresentation pres) { + long convertStart = System.currentTimeMillis(); - public boolean convert(File presentation, File output, int page) { String source = presentation.getAbsolutePath(); String dest = output.getAbsolutePath(); String AVM2SWF = "-T9"; @@ -57,33 +65,39 @@ public class Pdf2SwfPageConverter implements PageConverter { // Building the command line wrapped in shell to be able to use shell // feature like the pipe NuProcessBuilder pb = new NuProcessBuilder( - Arrays.asList( + Arrays.asList("timeout", convTimeout, "/bin/sh", "-c", SWFTOOLS_DIR - + File.separator - + "pdf2swf" - + " -vv " - + AVM2SWF - + " -F " - + fontsDir - + " -p " - + String.valueOf(page) - + " " - + source - + " -o " - + dest - + " | egrep 'shape id|Updating font|Drawing' | sed 's/ / /g' | cut -d' ' -f 1-3 | sort | uniq -cw 15")); + + File.separator + + "pdf2swf" + + " -vv " + + AVM2SWF + + " -F " + + fontsDir + + " -p " + + String.valueOf(page) + + " " + + source + + " -o " + + dest + + " | egrep 'shape id|Updating font|Drawing' | sed 's/ / /g' | cut -d' ' -f 1-3 | sort | uniq -cw 15")); Pdf2SwfPageConverterHandler pHandler = new Pdf2SwfPageConverterHandler(); pb.setProcessListener(pHandler); + + long pdf2SwfStart = System.currentTimeMillis(); + NuProcess process = pb.start(); try { - process.waitFor(60, TimeUnit.SECONDS); + process.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS); } catch (InterruptedException e) { log.error(e.getMessage()); } + long pdf2SwfEnd = System.currentTimeMillis(); + log.debug("Pdf2Swf conversion duration: {} sec", (pdf2SwfEnd - pdf2SwfStart)/1000); + File destFile = new File(dest); if (pHandler.isConversionSuccessfull() && destFile.exists() && pHandler.numberOfPlacements() < placementsThreshold @@ -91,43 +105,57 @@ public class Pdf2SwfPageConverter implements PageConverter { && pHandler.numberOfImageTags() < imageTagThreshold) { return true; } else { - log.debug( - "Previous conversion generated {} PlaceObject tags, {} DefineText tags and {} Images. Flattening to png image before converting again to a swf.", - pHandler.numberOfPlacements(), pHandler.numberOfTextTags(), - pHandler.numberOfImageTags()); + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + logData.put("page", page); + logData.put("convertSuccess", pHandler.isConversionSuccessfull()); + logData.put("fileExists", destFile.exists()); + logData.put("numObjectTags", pHandler.numberOfPlacements()); + logData.put("numTextTags", pHandler.numberOfTextTags()); + logData.put("numImageTags", pHandler.numberOfImageTags()); + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.warn("Potential problem with generated SWF: data={}", logStr); File tempPdfPage = null; File tempPng = null; - String basePresentationame = FilenameUtils.getBaseName(presentation - .getName()); + String basePresentationame = FilenameUtils.getBaseName(presentation.getName()); try { - tempPdfPage = File.createTempFile(basePresentationame + "-" + page, - ".pdf"); + tempPdfPage = File.createTempFile(basePresentationame + "-" + page, ".pdf"); tempPng = File.createTempFile(basePresentationame + "-" + page, ".png"); } catch (IOException ioException) { // We should never fall into this if the server is correctly configured log.error("Unable to create temporary files"); } + long gsStart = System.currentTimeMillis(); + // Step 1: Extract the PDF page into a single PDF file - NuProcessBuilder pbPdf = new NuProcessBuilder(Arrays.asList( + NuProcessBuilder pbPdf = new NuProcessBuilder(Arrays.asList("timeout", convTimeout, GHOSTSCRIPT_EXEC, "-sDEVICE=pdfwrite", "-dNOPAUSE", "-dQUIET", "-dBATCH", "-dFirstPage=" + page, "-dLastPage=" + page, "-sOutputFile=" + tempPdfPage.getAbsolutePath(), noPdfMarkWorkaround, presentation.getAbsolutePath())); - NuAbstractProcessHandler pbPdfHandler = new NuAbstractProcessHandler() { - }; + NuAbstractProcessHandler pbPdfHandler = new NuAbstractProcessHandler() {}; pbPdf.setProcessListener(pbPdfHandler); NuProcess processPdf = pbPdf.start(); try { - processPdf.waitFor(60, TimeUnit.SECONDS); + processPdf.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS); } catch (InterruptedException e) { log.error(e.getMessage()); } + long gsEnd = System.currentTimeMillis(); + log.debug("Ghostscript conversion duration: {} sec", (gsStart - gsEnd)/1000); + + long magickStart = System.currentTimeMillis(); + // Step 2: Convert a PDF page to PNG - NuProcessBuilder pbPng = new NuProcessBuilder(Arrays.asList( + NuProcessBuilder pbPng = new NuProcessBuilder(Arrays.asList("timeout", convTimeout, IMAGEMAGICK_DIR + File.separator + "convert", "-density", "150", "-quality", "90", "-flatten", "+dither", "-depth", "8", tempPdfPage.getAbsolutePath(), tempPng.getAbsolutePath())); @@ -135,24 +163,41 @@ public class Pdf2SwfPageConverter implements PageConverter { pbPng.setProcessListener(pbPngHandler); NuProcess processPng = pbPng.start(); try { - processPng.waitFor(60, TimeUnit.SECONDS); + processPng.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS); } catch (InterruptedException e) { log.error(e.getMessage()); } + long magickEnd = System.currentTimeMillis(); + + logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + logData.put("page", page); + logData.put("conversionTime(sec)", (magickEnd - magickStart)/1000); + + logStr = gson.toJson(logData); + + log.debug("ImageMagick conversion duration: {} sec", (magickEnd - magickStart)/1000); + + long png2swfStart = System.currentTimeMillis(); // Step 3: Convert a PNG image to SWF source = tempPng.getAbsolutePath(); - NuProcessBuilder pbSwf = new NuProcessBuilder(Arrays.asList(SWFTOOLS_DIR + NuProcessBuilder pbSwf = new NuProcessBuilder(Arrays.asList("timeout", convTimeout, SWFTOOLS_DIR + File.separator + "png2swf", "-o", dest, source)); Png2SwfPageConverterHandler pSwfHandler = new Png2SwfPageConverterHandler(); pbSwf.setProcessListener(pSwfHandler); NuProcess processSwf = pbSwf.start(); try { - processSwf.waitFor(60, TimeUnit.SECONDS); + processSwf.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS); } catch (InterruptedException e) { log.error(e.getMessage()); } + long png2swfEnd = System.currentTimeMillis(); + log.debug("ImageMagick conversion duration: {} sec", (png2swfEnd - png2swfStart)/1000); + // Delete the temporary PNG and PDF files after finishing the image // conversion tempPdfPage.delete(); @@ -160,6 +205,20 @@ public class Pdf2SwfPageConverter implements PageConverter { boolean doneSwf = pSwfHandler.isConversionSuccessfull(); + long convertEnd = System.currentTimeMillis(); + + logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + logData.put("page", page); + logData.put("conversionTime(sec)", (convertEnd - convertStart)/1000); + + logStr = gson.toJson(logData); + + log.debug("Problem page conversion duration: {} sec", (convertEnd - convertStart)/1000); + + if (doneSwf && destFile.exists()) { return true; } else { diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfPageToImageConversionService.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfPageToImageConversionService.java index 5717a508146da48dd4c60cbdbc305cf6cd597417..6d6bac69853ae699a52f09fa0c0c78c26e08cbbc 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfPageToImageConversionService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfPageToImageConversionService.java @@ -1,21 +1,21 @@ /** -* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ -* -* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). -* -* This program 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 3.0 of the License, or (at your option) any later -* version. -* -* BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. -* -*/ + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program 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 3.0 of the License, or (at your option) any later + * version. + * + * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + */ package org.bigbluebutton.presentation.imp; @@ -23,44 +23,45 @@ import java.io.File; import org.bigbluebutton.presentation.PageConverter; import org.bigbluebutton.presentation.PageExtractor; +import org.bigbluebutton.presentation.UploadedPresentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PdfPageToImageConversionService { - private static Logger log = LoggerFactory.getLogger(PdfPageToImageConversionService.class); - - private PageExtractor extractor; - private PageConverter pdfToImageConverter; - private PageConverter imageToSwfConverter; - - public boolean convertPageAsAnImage(File presentationFile, File output, int page) { - File tempDir = new File(presentationFile.getParent() + File.separatorChar + "temp"); - tempDir.mkdir(); + private static Logger log = LoggerFactory.getLogger(PdfPageToImageConversionService.class); - File tempPdfFile = new File(tempDir.getAbsolutePath() + File.separator + "temp-" + page + ".pdf"); + private PageExtractor extractor; + private PageConverter pdfToImageConverter; + private PageConverter imageToSwfConverter; - if (extractor.extractPage(presentationFile, tempPdfFile, page)) { - File tempPngFile = new File(tempDir.getAbsolutePath() + "/temp-" + page + ".svg"); + public boolean convertPageAsAnImage(File presentationFile, File output, int page, UploadedPresentation pres) { + File tempDir = new File(presentationFile.getParent() + File.separatorChar + "temp"); + tempDir.mkdir(); - if (pdfToImageConverter.convert(tempPdfFile, tempPngFile, 1)) { - if (imageToSwfConverter.convert(tempPngFile, output, 1)) { - return true; - } - } - } - - return false; - } - - public void setPageExtractor(PageExtractor extractor) { - this.extractor = extractor; - } - - public void setPdfToImageConverter(PageConverter imageConverter) { - this.pdfToImageConverter = imageConverter; - } - - public void setImageToSwfConverter(PageConverter swfConverter) { - this.imageToSwfConverter = swfConverter; - } + File tempPdfFile = new File(tempDir.getAbsolutePath() + File.separator + "temp-" + page + ".pdf"); + + if (extractor.extractPage(presentationFile, tempPdfFile, page)) { + File tempPngFile = new File(tempDir.getAbsolutePath() + "/temp-" + page + ".svg"); + + if (pdfToImageConverter.convert(tempPdfFile, tempPngFile, 1, pres)) { + if (imageToSwfConverter.convert(tempPngFile, output, 1, pres)) { + return true; + } + } + } + + return false; + } + + public void setPageExtractor(PageExtractor extractor) { + this.extractor = extractor; + } + + public void setPdfToImageConverter(PageConverter imageConverter) { + this.pdfToImageConverter = imageConverter; + } + + public void setImageToSwfConverter(PageConverter swfConverter) { + this.imageToSwfConverter = swfConverter; + } } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java index 8d831675dc4e5d212a93c8b2def16cbdd7028e49..a4e3369bd04e2b39a2e6806bb1c912a4cbf9ada2 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java @@ -1,26 +1,28 @@ /** -* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ -* -* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below). -* -* This program 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 3.0 of the License, or (at your option) any later -* version. -* -* BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. -* -*/ + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below). + * + * This program 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 3.0 of the License, or (at your option) any later + * version. + * + * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + */ package org.bigbluebutton.presentation.imp; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; @@ -30,6 +32,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.bigbluebutton.presentation.ConversionMessageConstants; import org.bigbluebutton.presentation.ConversionUpdateMessage; @@ -43,191 +46,257 @@ import org.bigbluebutton.presentation.UploadedPresentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.Gson; + public class PdfToSwfSlidesGenerationService { - private static Logger log = LoggerFactory.getLogger(PdfToSwfSlidesGenerationService.class); - - private final ExecutorService executor = Executors.newFixedThreadPool(2); - - private SwfSlidesGenerationProgressNotifier notifier; - private PageCounterService counterService; - private PageConverter pdfToSwfConverter; - private PdfPageToImageConversionService imageConvertService; - - private ThumbnailCreator thumbnailCreator; - private TextFileCreator textFileCreator; - private SvgImageCreator svgImageCreator; - private long MAX_CONVERSION_TIME = 5*60*1000; - private String BLANK_SLIDE; - private int MAX_SWF_FILE_SIZE; - private boolean svgImagesRequired; - - public void generateSlides(UploadedPresentation pres) { - determineNumberOfPages(pres); - if (pres.getNumberOfPages() > 0) { - convertPdfToSwf(pres); - createTextFiles(pres); - createThumbnails(pres); - - // only create SVG images if the configuration requires it - if (svgImagesRequired) { - createSvgImages(pres); - } - - notifier.sendConversionCompletedMessage(pres); - } - } - - private boolean determineNumberOfPages(UploadedPresentation pres) { - try { - counterService.determineNumberOfPages(pres); - return true; - } catch (CountingPageException e) { - sendFailedToCountPageMessage(e, pres); - } - return false; - } - - private void sendFailedToCountPageMessage(CountingPageException e, UploadedPresentation pres) { - MessageBuilder builder = new ConversionUpdateMessage.MessageBuilder(pres); - - if (e.getExceptionType() == CountingPageException.ExceptionType.PAGE_COUNT_EXCEPTION) { - builder.messageKey(ConversionMessageConstants.PAGE_COUNT_FAILED_KEY); - } else if (e.getExceptionType() == CountingPageException.ExceptionType.PAGE_EXCEEDED_EXCEPTION) { - builder.numberOfPages(e.getPageCount()); - builder.maxNumberPages(e.getMaxNumberOfPages()); - builder.messageKey(ConversionMessageConstants.PAGE_COUNT_EXCEEDED_KEY); - } - notifier.sendConversionUpdateMessage(builder.build().getMessage()); - } - - private void createThumbnails(UploadedPresentation pres) { - notifier.sendCreatingThumbnailsUpdateMessage(pres); - thumbnailCreator.createThumbnails(pres); - } - - private void createTextFiles(UploadedPresentation pres) { - notifier.sendCreatingTextFilesUpdateMessage(pres); - textFileCreator.createTextFiles(pres); - } - - private void createSvgImages(UploadedPresentation pres) { - notifier.sendCreatingSvgImagesUpdateMessage(pres); - svgImageCreator.createSvgImages(pres); - } - - private void convertPdfToSwf(UploadedPresentation pres) { - int numPages = pres.getNumberOfPages(); - List<PdfToSwfSlide> slides = setupSlides(pres, numPages); - - CompletionService<PdfToSwfSlide> completionService; - - completionService = new ExecutorCompletionService<PdfToSwfSlide>(executor); - generateSlides(pres, slides, completionService); - } - - private void generateSlides(UploadedPresentation pres, List<PdfToSwfSlide> slides, CompletionService<PdfToSwfSlide> completionService) { - long MAXWAIT = MAX_CONVERSION_TIME * 60 /*seconds*/ * 1000 /*millis*/; - - List<FutureTask<PdfToSwfSlide>> tasks = new ArrayList<FutureTask<PdfToSwfSlide>>(slides.size()); - for (final PdfToSwfSlide slide : slides) { - Callable<PdfToSwfSlide> c = new Callable<PdfToSwfSlide>() { - public PdfToSwfSlide call() { - return slide.createSlide(); - }; - }; - - FutureTask<PdfToSwfSlide> task = new FutureTask<PdfToSwfSlide>(c); - tasks.add(task); - completionService.submit(c); - } - - int slidesCompleted = 0; - - for (final PdfToSwfSlide slide : slides) { - Future<PdfToSwfSlide> future = null; - try { - future = completionService.poll(MAXWAIT, TimeUnit.MILLISECONDS); - if (future != null) { - PdfToSwfSlide s = future.get(); - slidesCompleted++; - notifier.sendConversionUpdateMessage(slidesCompleted, pres); - } else { - log.warn("Timedout waiting for page to finish conversion. meetingId=" + pres.getMeetingId() + " presId=" + pres.getId() + " presName=[" + pres.getName() + "]"); - } - } catch (InterruptedException e) { - log.error("InterruptedException while creating slide. meetingId=" + pres.getMeetingId() + " presId=" + pres.getId() + " presName=[" + pres.getName() + "]"); - } catch (ExecutionException e) { - log.error("ExecutionException while creating slide. meetingId=" + pres.getMeetingId() + " presId=" + pres.getId() + " presName=[" + pres.getName() + "]"); - } - } - - for (final PdfToSwfSlide slide : slides) { - if (! slide.isDone()){ - log.warn("Creating blank slide. meetingId=" + pres.getMeetingId() + " presId=" + pres.getId() + " name=[" + pres.getName() + " page=" + slide.getPageNumber()); - - slide.generateBlankSlide(); - notifier.sendConversionUpdateMessage(slidesCompleted++, pres); - } - } - } - - private List<PdfToSwfSlide> setupSlides(UploadedPresentation pres, int numPages) { - List<PdfToSwfSlide> slides = new ArrayList<PdfToSwfSlide>(numPages); - - for (int page = 1; page <= numPages; page++) { - PdfToSwfSlide slide = new PdfToSwfSlide(pres, page); - slide.setBlankSlide(BLANK_SLIDE); - slide.setMaxSwfFileSize(MAX_SWF_FILE_SIZE); - slide.setPageConverter(pdfToSwfConverter); - slide.setPdfPageToImageConversionService(imageConvertService); - - slides.add(slide); - } - - return slides; - } - - - - public void setCounterService(PageCounterService counterService) { - this.counterService = counterService; - } - - public void setPageConverter(PageConverter converter) { - this.pdfToSwfConverter = converter; - } - - public void setPdfPageToImageConversionService(PdfPageToImageConversionService service) { - this.imageConvertService = service; - } - - public void setBlankSlide(String blankSlide) { - this.BLANK_SLIDE = blankSlide; - } - - public void setMaxSwfFileSize(int size) { - this.MAX_SWF_FILE_SIZE = size; - } - - public void setSvgImagesRequired(boolean svg) { - this.svgImagesRequired = svg; - } - - public void setThumbnailCreator(ThumbnailCreator thumbnailCreator) { - this.thumbnailCreator = thumbnailCreator; - } - public void setTextFileCreator(TextFileCreator textFileCreator) { - this.textFileCreator = textFileCreator; - } - public void setSvgImageCreator(SvgImageCreator svgImageCreator) { - this.svgImageCreator = svgImageCreator; - } - public void setMaxConversionTime(int minutes) { - MAX_CONVERSION_TIME = minutes * 60 * 1000; - } - - public void setSwfSlidesGenerationProgressNotifier(SwfSlidesGenerationProgressNotifier notifier) { - this.notifier = notifier; - } - + private static Logger log = LoggerFactory.getLogger(PdfToSwfSlidesGenerationService.class); + + private SwfSlidesGenerationProgressNotifier notifier; + private PageCounterService counterService; + private PageConverter pdfToSwfConverter; + private PdfPageToImageConversionService imageConvertService; + private ExecutorService executor; + private ThumbnailCreator thumbnailCreator; + private TextFileCreator textFileCreator; + private SvgImageCreator svgImageCreator; + private long MAX_CONVERSION_TIME = 5*60*1000; + private String BLANK_SLIDE; + private int MAX_SWF_FILE_SIZE; + private boolean svgImagesRequired; + private final long CONVERSION_TIMEOUT = 20000000000L; // 20s + private int NUM_CONVERSION_THREADS = 2; + + public PdfToSwfSlidesGenerationService(int numConversionThreads) { + executor = Executors.newFixedThreadPool(numConversionThreads); + } + + public void generateSlides(UploadedPresentation pres) { + determineNumberOfPages(pres); + if (pres.getNumberOfPages() > 0) { + convertPdfToSwf(pres); + createTextFiles(pres); + createThumbnails(pres); + + // only create SVG images if the configuration requires it + if (svgImagesRequired) { + createSvgImages(pres); + } + + notifier.sendConversionCompletedMessage(pres); + } + } + + private boolean determineNumberOfPages(UploadedPresentation pres) { + try { + counterService.determineNumberOfPages(pres); + return true; + } catch (CountingPageException e) { + sendFailedToCountPageMessage(e, pres); + } + return false; + } + + private void sendFailedToCountPageMessage(CountingPageException e, UploadedPresentation pres) { + MessageBuilder builder = new ConversionUpdateMessage.MessageBuilder(pres); + + if (e.getExceptionType() == CountingPageException.ExceptionType.PAGE_COUNT_EXCEPTION) { + builder.messageKey(ConversionMessageConstants.PAGE_COUNT_FAILED_KEY); + } else if (e.getExceptionType() == CountingPageException.ExceptionType.PAGE_EXCEEDED_EXCEPTION) { + builder.numberOfPages(e.getPageCount()); + builder.maxNumberPages(e.getMaxNumberOfPages()); + builder.messageKey(ConversionMessageConstants.PAGE_COUNT_EXCEEDED_KEY); + } + notifier.sendConversionUpdateMessage(builder.build().getMessage()); + } + + private void createThumbnails(UploadedPresentation pres) { + notifier.sendCreatingThumbnailsUpdateMessage(pres); + thumbnailCreator.createThumbnails(pres); + } + + private void createTextFiles(UploadedPresentation pres) { + notifier.sendCreatingTextFilesUpdateMessage(pres); + textFileCreator.createTextFiles(pres); + } + + private void createSvgImages(UploadedPresentation pres) { + notifier.sendCreatingSvgImagesUpdateMessage(pres); + svgImageCreator.createSvgImages(pres); + } + + private void convertPdfToSwf(UploadedPresentation pres) { + int numPages = pres.getNumberOfPages(); + List<PdfToSwfSlide> slides = setupSlides(pres, numPages); + + CompletionService<PdfToSwfSlide> completionService = new ExecutorCompletionService<PdfToSwfSlide>(executor); + + generateSlides(pres, slides, completionService); + } + + private void generateSlides(UploadedPresentation pres, List<PdfToSwfSlide> slides, CompletionService<PdfToSwfSlide> completionService) { + long MAXWAIT = MAX_CONVERSION_TIME * 60 /*seconds*/ * 1000 /*millis*/; + int slidesCompleted = 0; + + long presConvStart = System.currentTimeMillis(); + + for (final PdfToSwfSlide slide : slides) { + long pageConvStart = System.currentTimeMillis(); + + Callable<PdfToSwfSlide> c = new Callable<PdfToSwfSlide>() { + public PdfToSwfSlide call() { + return slide.createSlide(); + }; + }; + + Future<PdfToSwfSlide> f = executor.submit(c); + long endNanos = System.nanoTime() + CONVERSION_TIMEOUT; + try { + // Only wait for the remaining time budget + long timeLeft = endNanos - System.nanoTime(); + PdfToSwfSlide s = f.get(timeLeft, TimeUnit.NANOSECONDS); + slidesCompleted++; + notifier.sendConversionUpdateMessage(slidesCompleted, pres); + } catch (ExecutionException e) { + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + logData.put("page", slide.getPageNumber()); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.warn("ExecutionException while converting page: data={}", logStr); + log.error(e.getMessage()); + } catch (InterruptedException e) { + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + logData.put("page", slide.getPageNumber()); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.warn("InterruptedException while converting page: data={}", logStr); + Thread.currentThread().interrupt(); + } catch (TimeoutException e) { + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + logData.put("page", slide.getPageNumber()); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.warn("TimeoutException while converting page: data={}", logStr); + f.cancel(true); + } + + long pageConvEnd = System.currentTimeMillis(); + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + logData.put("page", slide.getPageNumber()); + logData.put("conversionTime(sec)", (pageConvEnd - pageConvStart)/1000); + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.debug("Page conversion duration(sec): data={}", logStr); + + } + + for (final PdfToSwfSlide slide : slides) { + if (! slide.isDone()){ + + slide.generateBlankSlide(); + + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.warn("Creating blank slide: data={}", logStr); + + notifier.sendConversionUpdateMessage(slidesCompleted++, pres); + } + } + + long presConvEnd = System.currentTimeMillis(); + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", pres.getMeetingId()); + logData.put("presId", pres.getId()); + logData.put("filename", pres.getName()); + logData.put("conversionTime(sec)", (presConvEnd - presConvStart)/1000); + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.debug("Presentation conversion duration (sec): data={}", logStr); + } + + + private List<PdfToSwfSlide> setupSlides(UploadedPresentation pres, int numPages) { + List<PdfToSwfSlide> slides = new ArrayList<PdfToSwfSlide>(numPages); + + for (int page = 1; page <= numPages; page++) { + PdfToSwfSlide slide = new PdfToSwfSlide(pres, page); + slide.setBlankSlide(BLANK_SLIDE); + slide.setMaxSwfFileSize(MAX_SWF_FILE_SIZE); + slide.setPageConverter(pdfToSwfConverter); + slide.setPdfPageToImageConversionService(imageConvertService); + + slides.add(slide); + } + + return slides; + } + + + + public void setCounterService(PageCounterService counterService) { + this.counterService = counterService; + } + + public void setPageConverter(PageConverter converter) { + this.pdfToSwfConverter = converter; + } + + public void setPdfPageToImageConversionService(PdfPageToImageConversionService service) { + this.imageConvertService = service; + } + + public void setBlankSlide(String blankSlide) { + this.BLANK_SLIDE = blankSlide; + } + + public void setMaxSwfFileSize(int size) { + this.MAX_SWF_FILE_SIZE = size; + } + + public void setSvgImagesRequired(boolean svg) { + this.svgImagesRequired = svg; + } + + public void setThumbnailCreator(ThumbnailCreator thumbnailCreator) { + this.thumbnailCreator = thumbnailCreator; + } + public void setTextFileCreator(TextFileCreator textFileCreator) { + this.textFileCreator = textFileCreator; + } + public void setSvgImageCreator(SvgImageCreator svgImageCreator) { + this.svgImageCreator = svgImageCreator; + } + public void setMaxConversionTime(int minutes) { + MAX_CONVERSION_TIME = minutes * 60 * 1000; + } + + public void setSwfSlidesGenerationProgressNotifier(SwfSlidesGenerationProgressNotifier notifier) { + this.notifier = notifier; + } + } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Png2SwfPageConverter.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Png2SwfPageConverter.java index 79651a8292588a8f0a8315e75d60440e721791b0..e7c72f6703eeaae771459d422da97fecfb45819f 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Png2SwfPageConverter.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Png2SwfPageConverter.java @@ -1,49 +1,51 @@ /** -* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ -* -* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). -* -* This program 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 3.0 of the License, or (at your option) any later -* version. -* -* BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. -* -*/ + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program 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 3.0 of the License, or (at your option) any later + * version. + * + * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + */ package org.bigbluebutton.presentation.imp; import java.io.File; + import org.bigbluebutton.presentation.PageConverter; +import org.bigbluebutton.presentation.UploadedPresentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Png2SwfPageConverter implements PageConverter { - private static Logger log = LoggerFactory.getLogger(Png2SwfPageConverter.class); - - private String SWFTOOLS_DIR; - - public boolean convert(File presentationFile, File output, int page){ - String COMMAND = SWFTOOLS_DIR + "/png2swf -o " + output.getAbsolutePath() + " " + presentationFile.getAbsolutePath(); - - boolean done = new ExternalProcessExecutor().exec(COMMAND, 60000); - - if (done && output.exists()) { - return true; - } else { - log.warn("Failed to convert: " + output.getAbsolutePath() + " does not exist."); - return false; - } - } - - public void setSwfToolsDir(String dir) { - SWFTOOLS_DIR = dir; - } + private static Logger log = LoggerFactory.getLogger(Png2SwfPageConverter.class); + + private String SWFTOOLS_DIR; + + public boolean convert(File presentationFile, File output, int page, UploadedPresentation pres){ + String COMMAND = SWFTOOLS_DIR + "/png2swf -o " + output.getAbsolutePath() + " " + presentationFile.getAbsolutePath(); + + boolean done = new ExternalProcessExecutor().exec(COMMAND, 60000); + + if (done && output.exists()) { + return true; + } else { + log.warn("Failed to convert: " + output.getAbsolutePath() + " does not exist."); + return false; + } + } + + public void setSwfToolsDir(String dir) { + SWFTOOLS_DIR = dir; + } }