1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.exoplatform.wcm.connector.viewer;
18
19 import java.awt.image.BufferedImage;
20 import java.awt.image.RenderedImage;
21 import java.io.BufferedInputStream;
22 import java.io.BufferedOutputStream;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.OutputStream;
30 import java.io.Serializable;
31 import java.util.logging.Level;
32 import java.util.logging.Logger;
33
34 import javax.imageio.ImageIO;
35 import javax.jcr.Node;
36 import javax.jcr.Session;
37 import javax.ws.rs.GET;
38 import javax.ws.rs.Path;
39 import javax.ws.rs.PathParam;
40 import javax.ws.rs.core.Response;
41
42 import org.apache.commons.lang.StringUtils;
43 import org.artofsolving.jodconverter.office.OfficeException;
44 import org.exoplatform.services.cache.CacheService;
45 import org.exoplatform.services.cache.ExoCache;
46 import org.exoplatform.services.cms.impl.Utils;
47 import org.exoplatform.services.cms.jodconverter.JodConverterService;
48 import org.exoplatform.services.cms.mimetype.DMSMimeTypeResolver;
49 import org.exoplatform.services.jcr.RepositoryService;
50 import org.exoplatform.services.jcr.core.ManageableRepository;
51 import org.exoplatform.services.jcr.ext.app.SessionProviderService;
52 import org.exoplatform.services.jcr.ext.common.SessionProvider;
53 import org.exoplatform.services.log.ExoLogger;
54 import org.exoplatform.services.log.Log;
55 import org.exoplatform.services.pdfviewer.ObjectKey;
56 import org.exoplatform.services.pdfviewer.PDFViewerService;
57 import org.exoplatform.services.rest.resource.ResourceContainer;
58 import org.exoplatform.services.wcm.utils.WCMCoreUtils;
59 import org.icepdf.core.exceptions.PDFException;
60 import org.icepdf.core.exceptions.PDFSecurityException;
61 import org.icepdf.core.pobjects.Document;
62 import org.icepdf.core.pobjects.Page;
63 import org.icepdf.core.pobjects.Stream;
64 import org.icepdf.core.util.GraphicsRenderingHints;
65
66
67
68
69
70
71
72
73 @Path("/pdfviewer/{repoName}/")
74 public class PDFViewerRESTService implements ResourceContainer {
75
76 private static final int MAX_NAME_LENGTH= 150;
77 private static final String LASTMODIFIED = "Last-Modified";
78 private static final String PDF_VIEWER_CACHE = "ecms.PDFViewerRestService";
79 private RepositoryService repositoryService_;
80 private ExoCache<Serializable, Object> pdfCache;
81 private JodConverterService jodConverter_;
82 private static final Log LOG = ExoLogger.getLogger(PDFViewerRESTService.class.getName());
83
84 public PDFViewerRESTService(RepositoryService repositoryService,
85 CacheService caService,
86 JodConverterService jodConverter) throws Exception {
87 repositoryService_ = repositoryService;
88 jodConverter_ = jodConverter;
89 PDFViewerService pdfViewerService = WCMCoreUtils.getService(PDFViewerService.class);
90 if(pdfViewerService != null){
91 pdfCache = pdfViewerService.getCache();
92 }else{
93 pdfCache = caService.getCacheInstance(PDF_VIEWER_CACHE);
94 }
95 }
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 @GET
112 @Path("/{workspaceName}/{pageNumber}/{rotation}/{scale}/{uuid}/")
113 public Response getCoverImage(@PathParam("repoName") String repoName,
114 @PathParam("workspaceName") String wsName,
115 @PathParam("uuid") String uuid,
116 @PathParam("pageNumber") String pageNumber,
117 @PathParam("rotation") String rotation,
118 @PathParam("scale") String scale) throws Exception {
119 return getImageByPageNumber(repoName, wsName, uuid, pageNumber, rotation, scale);
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133 @GET
134 @Path("/{workspaceName}/{uuid}/")
135 public Response getPDFFile(@PathParam("repoName") String repoName,
136 @PathParam("workspaceName") String wsName,
137 @PathParam("uuid") String uuid) throws Exception {
138 Session session = null;
139 InputStream is = null;
140 String fileName = null;
141 try {
142 ManageableRepository repository = repositoryService_.getCurrentRepository();
143 session = getSystemProvider().getSession(wsName, repository);
144 Node currentNode = session.getNodeByUUID(uuid);
145 fileName = Utils.getTitle(currentNode);
146 File pdfFile = getPDFDocumentFile(currentNode, repoName);
147 is = new FileInputStream(pdfFile);
148 } catch (Exception e) {
149 if (LOG.isErrorEnabled()) {
150 LOG.error(e);
151 }
152 }
153 return Response.ok(is).header("Content-Disposition","attachment; filename=\"" + fileName+"\"").build();
154 }
155
156 private Response getImageByPageNumber(String repoName, String wsName, String uuid,
157 String pageNumber, String strRotation, String strScale) throws Exception {
158 StringBuilder bd = new StringBuilder();
159 StringBuilder bd1 = new StringBuilder();
160 StringBuilder bd2 = new StringBuilder();
161 bd.append(repoName).append("/").append(wsName).append("/").append(uuid);
162 Session session = null;
163 try {
164 Object objCache = pdfCache.get(new ObjectKey(bd.toString()));
165 InputStream is = null;
166 ManageableRepository repository = repositoryService_.getCurrentRepository();
167 session = getSystemProvider().getSession(wsName, repository);
168 Node currentNode = session.getNodeByUUID(uuid);
169 String lastModified = (String) pdfCache.get(new ObjectKey(bd1.append(bd.toString())
170 .append("/jcr:lastModified").toString()));
171 String baseVersion = (String) pdfCache.get(new ObjectKey(bd2.append(bd.toString())
172 .append("/jcr:baseVersion").toString()));
173 if(objCache!=null) {
174 File content = new File((String) pdfCache.get(new ObjectKey(bd.toString())));
175 if (!content.exists()) {
176 initDocument(currentNode, repoName);
177 }
178 is = pushToCache(new File((String) pdfCache.get(new ObjectKey(bd.toString()))),
179 repoName, wsName, uuid, pageNumber, strRotation, strScale, lastModified, baseVersion);
180 } else {
181 File file = getPDFDocumentFile(currentNode, repoName);
182 is = pushToCache(file, repoName, wsName, uuid, pageNumber, strRotation, strScale, lastModified, baseVersion);
183 }
184 return Response.ok(is, "image").header(LASTMODIFIED, lastModified).build();
185 } catch (Exception e) {
186 if (LOG.isErrorEnabled()) {
187 LOG.error(e);
188 }
189 }
190 return Response.ok().build();
191 }
192
193 private SessionProvider getSystemProvider() {
194 SessionProviderService service = WCMCoreUtils.getService(SessionProviderService.class);
195 return service.getSystemSessionProvider(null) ;
196 }
197
198 private InputStream pushToCache(File content, String repoName, String wsName, String uuid,
199 String pageNumber, String strRotation, String strScale, String lastModified,
200 String baseVersion) throws FileNotFoundException {
201 StringBuilder bd = new StringBuilder();
202 bd.append(repoName).append("/").append(wsName).append("/").append(uuid).append("/").append(
203 pageNumber).append("/").append(strRotation).append("/").append(strScale);
204 StringBuilder bd1 = new StringBuilder().append(bd).append("/jcr:lastModified");
205 StringBuilder bd2 = new StringBuilder().append(bd).append("/jcr:baseVersion");
206 String filePath = (String) pdfCache.get(new ObjectKey(bd.toString()));
207 String fileModifiedTime = (String) pdfCache.get(new ObjectKey(bd1.toString()));
208 String jcrBaseVersion = (String) pdfCache.get(new ObjectKey(bd2.toString()));
209 if (filePath == null || !(new File(filePath).exists()) || !StringUtils.equals(baseVersion, fileModifiedTime) ||
210 !StringUtils.equals(jcrBaseVersion, baseVersion)) {
211 File file = buildFileImage(content, uuid, pageNumber, strRotation, strScale);
212 filePath = file.getPath();
213 pdfCache.put(new ObjectKey(bd.toString()), filePath);
214 pdfCache.put(new ObjectKey(bd1.toString()), lastModified);
215 pdfCache.put(new ObjectKey(bd2.toString()), baseVersion);
216 }
217 return new BufferedInputStream(new FileInputStream(new File(filePath)));
218 }
219
220 private Document buildDocumentImage(File input, String name) {
221 Document document = new Document();
222
223
224
225
226 Logger.getLogger(Document.class.toString()).setLevel(Level.OFF);
227
228
229 try {
230
231 name = reduceFileNameSize(name);
232 document.setInputStream(new BufferedInputStream(new FileInputStream(input)), name);
233 } catch (PDFException ex) {
234 if (LOG.isDebugEnabled()) {
235 LOG.error("Error parsing PDF document " + ex);
236 }
237 } catch (PDFSecurityException ex) {
238 if (LOG.isDebugEnabled()) {
239 LOG.error("Error encryption not supported " + ex);
240 }
241 } catch (FileNotFoundException ex) {
242 if (LOG.isDebugEnabled()) {
243 LOG.error("Error file not found " + ex);
244 }
245 } catch (IOException ex) {
246 if (LOG.isDebugEnabled()) {
247 LOG.debug("Error handling PDF document: {} {}", name, ex.toString());
248 }
249 }
250
251 return document;
252 }
253
254 private File buildFileImage(File input, String path, String pageNumber, String strRotation, String strScale) {
255 Document document = buildDocumentImage(input, path);
256
257
258
259
260 Logger.getLogger(Stream.class.toString()).setLevel(Level.OFF);
261
262
263 float scale = 1.0f;
264 try {
265 scale = Float.parseFloat(strScale);
266
267 if (scale > 3.0f) {
268 scale = 3.0f;
269 }
270 } catch (NumberFormatException e) {
271 scale = 1.0f;
272 }
273 float rotation = 0.0f;
274 try {
275 rotation = Float.parseFloat(strRotation);
276 } catch (NumberFormatException e) {
277 rotation = 0.0f;
278 }
279 int maximumOfPage = document.getNumberOfPages();
280 int pageNum = 1;
281 try {
282 pageNum = Integer.parseInt(pageNumber);
283 } catch(NumberFormatException e) {
284 pageNum = 1;
285 }
286 if(pageNum >= maximumOfPage) pageNum = maximumOfPage;
287 else if(pageNum < 1) pageNum = 1;
288
289 BufferedImage image = (BufferedImage) document.getPageImage(pageNum - 1, GraphicsRenderingHints.SCREEN,
290 Page.BOUNDARY_CROPBOX, rotation, scale);
291 RenderedImage rendImage = image;
292 File file = null;
293 try {
294 file= File.createTempFile("imageCapture1_" + pageNum,".png");
295
296
297
298
299
300 ImageIO.write(rendImage, "png", file);
301 } catch (IOException e) {
302 if (LOG.isErrorEnabled()) {
303 LOG.error(e);
304 }
305 } finally {
306 image.flush();
307
308 document.dispose();
309 }
310 return file;
311 }
312
313
314
315
316
317
318
319
320 public Document initDocument(Node currentNode, String repoName) throws Exception {
321 return buildDocumentImage(getPDFDocumentFile(currentNode, repoName), currentNode.getName());
322 }
323
324
325
326
327
328
329
330
331 public File getPDFDocumentFile(Node currentNode, String repoName) throws Exception {
332 String wsName = currentNode.getSession().getWorkspace().getName();
333 String uuid = currentNode.getUUID();
334 StringBuilder bd = new StringBuilder();
335 StringBuilder bd1 = new StringBuilder();
336 StringBuilder bd2 = new StringBuilder();
337 bd.append(repoName).append("/").append(wsName).append("/").append(uuid);
338 bd1.append(bd).append("/jcr:lastModified");
339 bd2.append(bd).append("/jcr:baseVersion");
340 String path = (String) pdfCache.get(new ObjectKey(bd.toString()));
341 String lastModifiedTime = (String)pdfCache.get(new ObjectKey(bd1.toString()));
342 String baseVersion = (String)pdfCache.get(new ObjectKey(bd2.toString()));
343 File content = null;
344 String name = currentNode.getName().replaceAll(":","_");
345 Node contentNode = currentNode.getNode("jcr:content");
346 String lastModified = getJcrLastModified(currentNode);
347 String jcrBaseVersion = getJcrBaseVersion(currentNode);
348
349 if (path == null || !(content = new File(path)).exists() || !lastModified.equals(lastModifiedTime) ||
350 !StringUtils.equals(baseVersion, jcrBaseVersion)) {
351 String mimeType = contentNode.getProperty("jcr:mimeType").getString();
352 InputStream input = new BufferedInputStream(contentNode.getProperty("jcr:data").getStream());
353
354 if (name.indexOf(".") > 0) name = name.substring(0, name.lastIndexOf("."));
355
356 name = reduceFileNameSize(name);
357 content = File.createTempFile(name + "_tmp", ".pdf");
358
359
360
361
362
363
364 String extension = DMSMimeTypeResolver.getInstance().getExtension(mimeType);
365 if ("pdf".equals(extension)) {
366 read(input, new BufferedOutputStream(new FileOutputStream(content)));
367 } else {
368
369 File in = File.createTempFile(name + "_tmp", "." + extension);
370 read(input, new BufferedOutputStream(new FileOutputStream(in)));
371 try {
372 boolean success = jodConverter_.convert(in, content, "pdf");
373
374 if (!success) {
375 content.delete();
376 }
377 } catch (OfficeException connection) {
378 content.delete();
379 if (LOG.isErrorEnabled()) {
380 LOG.error("Exception when using Office Service");
381 }
382 } finally {
383 in.delete();
384 }
385 }
386 if (content.exists()) {
387 pdfCache.put(new ObjectKey(bd.toString()), content.getPath());
388 pdfCache.put(new ObjectKey(bd1.toString()), lastModified);
389 pdfCache.put(new ObjectKey(bd2.toString()), jcrBaseVersion);
390 }
391 }
392 return content;
393 }
394
395 private String getJcrLastModified(Node node) throws Exception {
396 Node checkedNode = node;
397 if (node.isNodeType("nt:frozenNode")) {
398 checkedNode = node.getSession().getNodeByUUID(node.getProperty("jcr:frozenUuid").getString());
399 }
400 return Utils.getJcrContentLastModified(checkedNode);
401 }
402
403 private String getJcrBaseVersion(Node node) throws Exception {
404 Node checkedNode = node;
405 if (node.isNodeType("nt:frozenNode")) {
406 checkedNode = node.getSession().getNodeByUUID(node.getProperty("jcr:frozenUuid").getString());
407 }
408 return checkedNode.hasProperty("jcr:baseVersion") ? checkedNode.getProperty("jcr:baseVersion").getString() : null;
409 }
410
411 private void read(InputStream is, OutputStream os) throws Exception {
412 int bufferLength = 1024;
413 int readLength = 0;
414 while (readLength > -1) {
415 byte[] chunk = new byte[bufferLength];
416 readLength = is.read(chunk);
417 if (readLength > 0) {
418 os.write(chunk, 0, readLength);
419 }
420 }
421 os.flush();
422 os.close();
423 }
424
425
426
427
428
429
430 private String reduceFileNameSize(String name) {
431 return (name != null && name.length() > MAX_NAME_LENGTH) ? name.substring(0, MAX_NAME_LENGTH) : name;
432 }
433
434
435 }