001
014
015 package com.liferay.portlet.documentlibrary.util;
016
017 import com.liferay.portal.kernel.configuration.Filter;
018 import com.liferay.portal.kernel.image.ImageToolUtil;
019 import com.liferay.portal.kernel.lar.PortletDataContext;
020 import com.liferay.portal.kernel.log.Log;
021 import com.liferay.portal.kernel.log.LogFactoryUtil;
022 import com.liferay.portal.kernel.messaging.DestinationNames;
023 import com.liferay.portal.kernel.messaging.MessageBusException;
024 import com.liferay.portal.kernel.messaging.MessageBusUtil;
025 import com.liferay.portal.kernel.process.ClassPathUtil;
026 import com.liferay.portal.kernel.process.ProcessCallable;
027 import com.liferay.portal.kernel.process.ProcessException;
028 import com.liferay.portal.kernel.process.ProcessExecutor;
029 import com.liferay.portal.kernel.repository.model.FileEntry;
030 import com.liferay.portal.kernel.repository.model.FileVersion;
031 import com.liferay.portal.kernel.util.ContentTypes;
032 import com.liferay.portal.kernel.util.FileUtil;
033 import com.liferay.portal.kernel.util.GetterUtil;
034 import com.liferay.portal.kernel.util.InstancePool;
035 import com.liferay.portal.kernel.util.MimeTypesUtil;
036 import com.liferay.portal.kernel.util.OSDetector;
037 import com.liferay.portal.kernel.util.PropsKeys;
038 import com.liferay.portal.kernel.util.StringBundler;
039 import com.liferay.portal.kernel.util.StringPool;
040 import com.liferay.portal.kernel.util.Validator;
041 import com.liferay.portal.kernel.xml.Element;
042 import com.liferay.portal.repository.liferayrepository.model.LiferayFileVersion;
043 import com.liferay.portal.util.PrefsPropsUtil;
044 import com.liferay.portal.util.PropsUtil;
045 import com.liferay.portal.util.PropsValues;
046 import com.liferay.portlet.documentlibrary.NoSuchFileEntryException;
047 import com.liferay.portlet.documentlibrary.store.DLStoreUtil;
048
049 import java.awt.image.BufferedImage;
050 import java.awt.image.RenderedImage;
051
052 import java.io.File;
053 import java.io.FileInputStream;
054 import java.io.FileOutputStream;
055 import java.io.InputStream;
056
057 import java.util.Arrays;
058 import java.util.LinkedList;
059 import java.util.List;
060 import java.util.Set;
061 import java.util.Vector;
062
063 import javax.imageio.ImageIO;
064
065 import javax.portlet.PortletPreferences;
066
067 import org.apache.commons.lang.time.StopWatch;
068 import org.apache.pdfbox.pdmodel.PDDocument;
069 import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
070 import org.apache.pdfbox.pdmodel.PDPage;
071
072 import org.im4java.core.ConvertCmd;
073 import org.im4java.core.IMOperation;
074 import org.im4java.process.ProcessStarter;
075
076
082 public class PDFProcessorImpl
083 extends DLPreviewableProcessor implements PDFProcessor {
084
085 public static PDFProcessorImpl getInstance() {
086 return _instance;
087 }
088
089 public void exportGeneratedFiles(
090 PortletDataContext portletDataContext, FileEntry fileEntry,
091 Element fileEntryElement)
092 throws Exception {
093
094 exportThumbnails(
095 portletDataContext, fileEntry, fileEntryElement, "pdf");
096
097 exportPreviews(portletDataContext, fileEntry, fileEntryElement);
098 }
099
100 public void generateImages(FileVersion fileVersion) throws Exception {
101 Initializer._initializedInstance._generateImages(fileVersion);
102 }
103
104 public String getGlobalSearchPath() throws Exception {
105 PortletPreferences preferences = PrefsPropsUtil.getPreferences();
106
107 String globalSearchPath = preferences.getValue(
108 PropsKeys.IMAGEMAGICK_GLOBAL_SEARCH_PATH, null);
109
110 if (Validator.isNotNull(globalSearchPath)) {
111 return globalSearchPath;
112 }
113
114 String filterName = null;
115
116 if (OSDetector.isApple()) {
117 filterName = "apple";
118 }
119 else if (OSDetector.isWindows()) {
120 filterName = "windows";
121 }
122 else {
123 filterName = "unix";
124 }
125
126 return PropsUtil.get(
127 PropsKeys.IMAGEMAGICK_GLOBAL_SEARCH_PATH, new Filter(filterName));
128 }
129
130 public InputStream getPreviewAsStream(FileVersion fileVersion, int index)
131 throws Exception {
132
133 return Initializer._initializedInstance.doGetPreviewAsStream(
134 fileVersion, index, PREVIEW_TYPE);
135 }
136
137 public int getPreviewFileCount(FileVersion fileVersion) {
138 try {
139 return Initializer._initializedInstance.doGetPreviewFileCount(
140 fileVersion);
141 }
142 catch (Exception e) {
143 _log.error(e, e);
144 }
145
146 return 0;
147 }
148
149 public long getPreviewFileSize(FileVersion fileVersion, int index)
150 throws Exception {
151
152 return Initializer._initializedInstance.doGetPreviewFileSize(
153 fileVersion, index);
154 }
155
156 public InputStream getThumbnailAsStream(FileVersion fileVersion, int index)
157 throws Exception {
158
159 return doGetThumbnailAsStream(fileVersion, index);
160 }
161
162 public long getThumbnailFileSize(FileVersion fileVersion, int index)
163 throws Exception {
164
165 return doGetThumbnailFileSize(fileVersion, index);
166 }
167
168 public boolean hasImages(FileVersion fileVersion) {
169 boolean hasImages = false;
170
171 try {
172 hasImages = _hasImages(fileVersion);
173
174 if (!hasImages && isSupported(fileVersion)) {
175 Initializer._initializedInstance._queueGeneration(fileVersion);
176 }
177 }
178 catch (Exception e) {
179 _log.error(e, e);
180 }
181
182 return hasImages;
183 }
184
185 public void importGeneratedFiles(
186 PortletDataContext portletDataContext, FileEntry fileEntry,
187 FileEntry importedFileEntry, Element fileEntryElement)
188 throws Exception {
189
190 importThumbnails(
191 portletDataContext, fileEntry, importedFileEntry, fileEntryElement,
192 "pdf");
193
194 importPreviews(
195 portletDataContext, fileEntry, importedFileEntry, fileEntryElement);
196 }
197
198 public boolean isDocumentSupported(FileVersion fileVersion) {
199 return Initializer._initializedInstance.isSupported(fileVersion);
200 }
201
202 public boolean isDocumentSupported(String mimeType) {
203 return Initializer._initializedInstance.isSupported(mimeType);
204 }
205
206 public boolean isImageMagickEnabled() throws Exception {
207 if (PrefsPropsUtil.getBoolean(PropsKeys.IMAGEMAGICK_ENABLED)) {
208 return true;
209 }
210
211 if (!_warned) {
212 StringBundler sb = new StringBundler(5);
213
214 sb.append("Liferay is not configured to use ImageMagick for ");
215 sb.append("generating Document Library previews and will default ");
216 sb.append("to PDFBox. For better quality previews, install ");
217 sb.append("ImageMagick and enable it in portal-ext.properties.");
218
219 _log.warn(sb.toString());
220
221 _warned = true;
222 }
223
224 return false;
225 }
226
227 public boolean isSupported(String mimeType) {
228 if (Validator.isNull(mimeType)) {
229 return false;
230 }
231
232 if (mimeType.equals(ContentTypes.APPLICATION_PDF) ||
233 mimeType.equals(ContentTypes.APPLICATION_X_PDF)) {
234
235 return true;
236 }
237
238 if (DocumentConversionUtil.isEnabled()) {
239 Set<String> extensions = MimeTypesUtil.getExtensions(mimeType);
240
241 for (String extension : extensions) {
242 extension = extension.substring(1);
243
244 String[] targetExtensions =
245 DocumentConversionUtil.getConversions(extension);
246
247 if (Arrays.binarySearch(targetExtensions, "pdf") >= 0) {
248 return true;
249 }
250 }
251 }
252
253 return false;
254 }
255
256 public void reset() throws Exception {
257 if (isImageMagickEnabled()) {
258 _globalSearchPath = getGlobalSearchPath();
259
260 ProcessStarter.setGlobalSearchPath(_globalSearchPath);
261
262 _convertCmd = new ConvertCmd();
263 }
264 else {
265 _convertCmd = null;
266 }
267 }
268
269 public void trigger(FileVersion fileVersion) {
270 Initializer._initializedInstance._queueGeneration(fileVersion);
271 }
272
273 protected void exportPreviews(
274 PortletDataContext portletDataContext, FileEntry fileEntry,
275 Element fileEntryElement)
276 throws Exception {
277
278 FileVersion fileVersion = fileEntry.getFileVersion();
279
280 if (!isSupported(fileVersion) || !_hasImages(fileVersion)) {
281 return;
282 }
283
284 if (!portletDataContext.isPerformDirectBinaryImport()) {
285 int previewFileCount = PDFProcessorUtil.getPreviewFileCount(
286 fileVersion);
287
288 fileEntryElement.addAttribute(
289 "bin-path-pdf-preview-count", String.valueOf(previewFileCount));
290
291 for (int i = 0; i < previewFileCount; i++) {
292 exportPreview(
293 portletDataContext, fileEntry, fileEntryElement, "pdf",
294 PREVIEW_TYPE, i);
295 }
296 }
297 }
298
299 @Override
300 protected String getPreviewType(FileVersion fileVersion) {
301 return PREVIEW_TYPE;
302 }
303
304 @Override
305 protected String getThumbnailType(FileVersion fileVersion) {
306 return THUMBNAIL_TYPE;
307 }
308
309 protected void importPreviews(
310 PortletDataContext portletDataContext, FileEntry fileEntry,
311 FileEntry importedFileEntry, Element fileEntryElement)
312 throws Exception {
313
314 int previewFileCount = GetterUtil.getInteger(
315 fileEntryElement.attributeValue("bin-path-preview-pdf-count"));
316
317 for (int i = 0; i < previewFileCount; i++) {
318 importPreview(
319 portletDataContext, fileEntry, importedFileEntry,
320 fileEntryElement, "pdf", PREVIEW_TYPE, i);
321 }
322 }
323
324 protected void initialize() {
325 try {
326 FileUtil.mkdirs(PREVIEW_TMP_PATH);
327 FileUtil.mkdirs(THUMBNAIL_TMP_PATH);
328
329 reset();
330 }
331 catch (Exception e) {
332 _log.warn(e, e);
333 }
334 }
335
336 private PDFProcessorImpl() {
337 }
338
339 private void _generateImages(FileVersion fileVersion)
340 throws Exception {
341
342 try {
343 if (_hasImages(fileVersion)) {
344 return;
345 }
346
347 String extension = fileVersion.getExtension();
348
349 if (extension.equals("pdf")) {
350 if (fileVersion instanceof LiferayFileVersion) {
351 try {
352 LiferayFileVersion liferayFileVersion =
353 (LiferayFileVersion)fileVersion;
354
355 File file = liferayFileVersion.getFile(false);
356
357 _generateImages(fileVersion, file);
358
359 return;
360 }
361 catch (UnsupportedOperationException uoe) {
362 }
363 }
364
365 InputStream inputStream = fileVersion.getContentStream(false);
366
367 _generateImages(fileVersion, inputStream);
368 }
369 else if (DocumentConversionUtil.isEnabled()) {
370 InputStream inputStream = fileVersion.getContentStream(false);
371
372 String tempFileId = DLUtil.getTempFileId(
373 fileVersion.getFileEntryId(), fileVersion.getVersion());
374
375 File file = DocumentConversionUtil.convert(
376 tempFileId, inputStream, extension, "pdf");
377
378 _generateImages(fileVersion, file);
379 }
380 }
381 catch (NoSuchFileEntryException nsfee) {
382 }
383 finally {
384 _fileVersionIds.remove(fileVersion.getFileVersionId());
385 }
386 }
387
388 private void _generateImages(FileVersion fileVersion, File file)
389 throws Exception {
390
391 if (isImageMagickEnabled()) {
392 _generateImagesIM(fileVersion, file);
393 }
394 else {
395 _generateImagesPB(fileVersion, file);
396 }
397 }
398
399 private void _generateImages(
400 FileVersion fileVersion, InputStream inputStream)
401 throws Exception {
402
403 if (isImageMagickEnabled()) {
404 _generateImagesIM(fileVersion, inputStream);
405 }
406 else {
407 _generateImagesPB(fileVersion, inputStream);
408 }
409 }
410
411 private void _generateImagesIM(FileVersion fileVersion, File file)
412 throws Exception {
413
414 if (_isGeneratePreview(fileVersion)) {
415 StopWatch stopWatch = null;
416
417 if (_log.isInfoEnabled()) {
418 stopWatch = new StopWatch();
419
420 stopWatch.start();
421 }
422
423 _generateImagesIM(fileVersion, file, false);
424
425 if (_log.isInfoEnabled()) {
426 int previewFileCount = getPreviewFileCount(fileVersion);
427
428 _log.info(
429 "ImageMagick generated " + previewFileCount +
430 " preview pages for " + fileVersion.getTitle() +
431 " in " + stopWatch);
432 }
433 }
434
435 if (_isGenerateThumbnail(fileVersion)) {
436 StopWatch stopWatch = null;
437
438 if (_log.isInfoEnabled()) {
439 stopWatch = new StopWatch();
440
441 stopWatch.start();
442 }
443
444 _generateImagesIM(fileVersion, file, true);
445
446 if (_log.isInfoEnabled()) {
447 _log.info(
448 "ImageMagick generated a thumbnail for " +
449 fileVersion.getTitle() + " in " + stopWatch);
450 }
451 }
452 }
453
454 private void _generateImagesIM(
455 FileVersion fileVersion, File file, boolean thumbnail)
456 throws Exception {
457
458
459
460 String tempFileId = DLUtil.getTempFileId(
461 fileVersion.getFileEntryId(), fileVersion.getVersion());
462
463 IMOperation imOperation = new IMOperation();
464
465 imOperation.alpha("off");
466
467 imOperation.density(
468 PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_DPI,
469 PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_DPI);
470
471 if (PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_MAX_HEIGHT != 0) {
472 imOperation.adaptiveResize(
473 PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_MAX_WIDTH,
474 PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_MAX_HEIGHT);
475 }
476 else {
477 imOperation.adaptiveResize(
478 PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_MAX_WIDTH);
479 }
480
481 imOperation.depth(PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_DEPTH);
482
483 if (thumbnail) {
484 imOperation.addImage(file.getPath() + "[0]");
485 imOperation.addImage(getThumbnailTempFilePath(tempFileId));
486 }
487 else {
488 imOperation.addImage(file.getPath());
489 imOperation.addImage(getPreviewTempFilePath(tempFileId, -1));
490 }
491
492 if (_log.isInfoEnabled()) {
493 _log.info("Excecuting command 'convert " + imOperation + "'");
494 }
495
496 if (PropsValues.DL_FILE_ENTRY_PREVIEW_FORK_PROCESS_ENABLED) {
497 ProcessCallable<String> processCallable =
498 new ImageMagickProcessCallable(
499 _globalSearchPath, imOperation.getCmdArgs());
500
501 ProcessExecutor.execute(
502 processCallable, ClassPathUtil.getPortalClassPath());
503 }
504 else {
505 _convertCmd.run(imOperation);
506 }
507
508
509
510 if (thumbnail) {
511 File thumbnailTempFile = getThumbnailTempFile(tempFileId);
512
513 try {
514 storeThumbnailImages(fileVersion, thumbnailTempFile);
515 }
516 finally {
517 FileUtil.delete(thumbnailTempFile);
518 }
519 }
520 else {
521
522
523
524
525 File singlePagePreviewFile = getPreviewTempFile(tempFileId, -1);
526
527 if (singlePagePreviewFile.exists()) {
528 singlePagePreviewFile.renameTo(
529 getPreviewTempFile(tempFileId, 1));
530 }
531
532 int total = getPreviewTempFileCount(fileVersion);
533
534 for (int i = 0; i < total; i++) {
535 File previewTempFile = getPreviewTempFile(tempFileId, i + 1);
536
537 try {
538 addFileToStore(
539 fileVersion.getCompanyId(), PREVIEW_PATH,
540 getPreviewFilePath(fileVersion, i + 1),
541 previewTempFile);
542 }
543 finally {
544 FileUtil.delete(previewTempFile);
545 }
546 }
547 }
548 }
549
550 private void _generateImagesIM(
551 FileVersion fileVersion, InputStream inputStream)
552 throws Exception {
553
554 File file = FileUtil.createTempFile(inputStream);
555
556 try {
557 _generateImagesIM(fileVersion, file);
558 }
559 finally {
560 FileUtil.delete(file);
561 }
562 }
563
564 private void _generateImagesPB(FileVersion fileVersion, File file)
565 throws Exception {
566
567 _generateImagesPB(fileVersion, new FileInputStream(file));
568 }
569
570 private void _generateImagesPB(
571 FileVersion fileVersion, InputStream inputStream)
572 throws Exception {
573
574 boolean generatePreview = _isGeneratePreview(fileVersion);
575 boolean generateThumbnail = _isGenerateThumbnail(fileVersion);
576
577 PDDocument pdDocument = null;
578
579 try {
580 pdDocument = PDDocument.load(inputStream);
581
582 PDDocumentCatalog pdDocumentCatalog =
583 pdDocument.getDocumentCatalog();
584
585 List<PDPage> pdPages = pdDocumentCatalog.getAllPages();
586
587 for (int i = 0; i < pdPages.size(); i++) {
588 PDPage pdPage = pdPages.get(i);
589
590 if (generateThumbnail && (i == 0)) {
591 _generateImagesPB(fileVersion, pdPage, i);
592
593 if (_log.isInfoEnabled()) {
594 _log.info(
595 "PDFBox generated a thumbnail for " +
596 fileVersion.getFileVersionId());
597 }
598 }
599
600 if (!generatePreview) {
601 break;
602 }
603
604 _generateImagesPB(fileVersion, pdPage, i + 1);
605 }
606
607 if (_log.isInfoEnabled() && generatePreview) {
608 _log.info(
609 "PDFBox generated " +
610 getPreviewFileCount(fileVersion) +
611 " preview pages for " +
612 fileVersion.getFileVersionId());
613 }
614 }
615 finally {
616 if (pdDocument != null) {
617 pdDocument.close();
618 }
619 }
620 }
621
622 private void _generateImagesPB(
623 FileVersion fileVersion, PDPage pdPage, int index)
624 throws Exception {
625
626
627
628 RenderedImage renderedImage = pdPage.convertToImage(
629 BufferedImage.TYPE_INT_RGB,
630 PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_DPI);
631
632 if (PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_MAX_HEIGHT != 0) {
633 renderedImage = ImageToolUtil.scale(
634 renderedImage,
635 PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_MAX_WIDTH,
636 PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_MAX_HEIGHT);
637 }
638 else {
639 renderedImage = ImageToolUtil.scale(
640 renderedImage,
641 PropsValues.DL_FILE_ENTRY_PREVIEW_DOCUMENT_MAX_WIDTH);
642 }
643
644
645
646 if (index == 0) {
647 storeThumbnailImages(fileVersion, renderedImage);
648 }
649 else {
650 File tempFile = null;
651
652 try {
653 String tempFileId = DLUtil.getTempFileId(
654 fileVersion.getFileEntryId(), fileVersion.getVersion());
655
656 tempFile = getPreviewTempFile(tempFileId, index);
657
658 tempFile.createNewFile();
659
660 ImageIO.write(
661 renderedImage, PREVIEW_TYPE,
662 new FileOutputStream(tempFile));
663
664 addFileToStore(
665 fileVersion.getCompanyId(), PREVIEW_PATH,
666 getPreviewFilePath(fileVersion, index), tempFile);
667 }
668 finally {
669 FileUtil.delete(tempFile);
670 }
671 }
672 }
673
674 private boolean _hasImages(FileVersion fileVersion) throws Exception {
675 if (PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED) {
676 if (!DLStoreUtil.hasFile(
677 fileVersion.getCompanyId(), REPOSITORY_ID,
678 getPreviewFilePath(fileVersion, 1))) {
679
680 return false;
681 }
682 }
683
684 return hasThumbnails(fileVersion);
685 }
686
687 private boolean _isGeneratePreview(FileVersion fileVersion)
688 throws Exception {
689
690 String previewFilePath = getPreviewFilePath(fileVersion, 1);
691
692 if (PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED &&
693 !DLStoreUtil.hasFile(
694 fileVersion.getCompanyId(), REPOSITORY_ID, previewFilePath)) {
695
696 return true;
697 }
698 else {
699 return false;
700 }
701 }
702
703 private boolean _isGenerateThumbnail(FileVersion fileVersion)
704 throws Exception {
705
706 String thumbnailFilePath = getThumbnailFilePath(
707 fileVersion, THUMBNAIL_INDEX_DEFAULT);
708
709 if (PropsValues.DL_FILE_ENTRY_THUMBNAIL_ENABLED &&
710 !DLStoreUtil.hasFile(
711 fileVersion.getCompanyId(), REPOSITORY_ID, thumbnailFilePath)) {
712
713 return true;
714 }
715 else {
716 return false;
717 }
718 }
719
720 private void _queueGeneration(FileVersion fileVersion) {
721 if (_fileVersionIds.contains(fileVersion.getFileVersionId())) {
722 return;
723 }
724
725 boolean generateImages = false;
726
727 String extension = fileVersion.getExtension();
728
729 if (extension.equals("pdf")) {
730 generateImages = true;
731 }
732 else if (DocumentConversionUtil.isEnabled()) {
733 String[] conversions = DocumentConversionUtil.getConversions(
734 extension);
735
736 for (String conversion : conversions) {
737 if (conversion.equals("pdf")) {
738 generateImages = true;
739
740 break;
741 }
742 }
743 }
744
745 if (generateImages) {
746 _fileVersionIds.add(fileVersion.getFileVersionId());
747
748 if (PropsValues.DL_FILE_ENTRY_PROCESSORS_TRIGGER_SYNCHRONOUSLY) {
749 try {
750 MessageBusUtil.sendSynchronousMessage(
751 DestinationNames.DOCUMENT_LIBRARY_PDF_PROCESSOR,
752 fileVersion);
753 }
754 catch (MessageBusException mbe) {
755 if (_log.isWarnEnabled()) {
756 _log.warn(mbe, mbe);
757 }
758 }
759 }
760 else {
761 MessageBusUtil.sendMessage(
762 DestinationNames.DOCUMENT_LIBRARY_PDF_PROCESSOR,
763 fileVersion);
764 }
765 }
766 }
767
768 private static Log _log = LogFactoryUtil.getLog(PDFProcessorImpl.class);
769
770 private static PDFProcessorImpl _instance = new PDFProcessorImpl();
771
772 static {
773 InstancePool.put(PDFProcessorImpl.class.getName(), _instance);
774 }
775
776 private ConvertCmd _convertCmd;
777 private List<Long> _fileVersionIds = new Vector<Long>();
778 private String _globalSearchPath;
779 private boolean _warned;
780
781 private static class ImageMagickProcessCallable
782 implements ProcessCallable<String> {
783
784 public ImageMagickProcessCallable(
785 String globalSearchPath, LinkedList<String> commandArguments) {
786
787 _globalSearchPath = globalSearchPath;
788 _commandArguments = commandArguments;
789 }
790
791 public String call() throws ProcessException {
792 try {
793 LiferayConvertCmd.run(_globalSearchPath, _commandArguments);
794 }
795 catch (Exception e) {
796 throw new ProcessException(e);
797 }
798
799 return StringPool.BLANK;
800 }
801
802 private LinkedList<String> _commandArguments;
803 private String _globalSearchPath;
804
805 }
806
807 private static class Initializer {
808
809 private static PDFProcessorImpl _initializedInstance;
810
811 static {
812 _instance.initialize();
813
814 _initializedInstance = _instance;
815 }
816
817 }
818
819 }