001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.softwarecatalog.service.impl;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.plugin.Version;
019    import com.liferay.portal.kernel.search.Indexable;
020    import com.liferay.portal.kernel.search.IndexableType;
021    import com.liferay.portal.kernel.util.OrderByComparator;
022    import com.liferay.portal.kernel.util.StringPool;
023    import com.liferay.portal.kernel.util.StringUtil;
024    import com.liferay.portal.kernel.util.Time;
025    import com.liferay.portal.kernel.util.Validator;
026    import com.liferay.portal.kernel.workflow.WorkflowConstants;
027    import com.liferay.portal.kernel.xml.Document;
028    import com.liferay.portal.kernel.xml.Element;
029    import com.liferay.portal.kernel.xml.SAXReaderUtil;
030    import com.liferay.portal.model.ResourceConstants;
031    import com.liferay.portal.model.User;
032    import com.liferay.portal.plugin.ModuleId;
033    import com.liferay.portal.service.ServiceContext;
034    import com.liferay.portal.util.PropsValues;
035    import com.liferay.portal.webserver.WebServerServletTokenUtil;
036    import com.liferay.portlet.softwarecatalog.DuplicateProductEntryModuleIdException;
037    import com.liferay.portlet.softwarecatalog.ProductEntryAuthorException;
038    import com.liferay.portlet.softwarecatalog.ProductEntryLicenseException;
039    import com.liferay.portlet.softwarecatalog.ProductEntryNameException;
040    import com.liferay.portlet.softwarecatalog.ProductEntryPageURLException;
041    import com.liferay.portlet.softwarecatalog.ProductEntryScreenshotsException;
042    import com.liferay.portlet.softwarecatalog.ProductEntryShortDescriptionException;
043    import com.liferay.portlet.softwarecatalog.ProductEntryTypeException;
044    import com.liferay.portlet.softwarecatalog.model.SCFrameworkVersion;
045    import com.liferay.portlet.softwarecatalog.model.SCLicense;
046    import com.liferay.portlet.softwarecatalog.model.SCProductEntry;
047    import com.liferay.portlet.softwarecatalog.model.SCProductScreenshot;
048    import com.liferay.portlet.softwarecatalog.model.SCProductVersion;
049    import com.liferay.portlet.softwarecatalog.service.base.SCProductEntryLocalServiceBaseImpl;
050    import com.liferay.util.xml.DocUtil;
051    
052    import java.util.Date;
053    import java.util.List;
054    import java.util.Map;
055    import java.util.Properties;
056    
057    /**
058     * @author Jorge Ferrer
059     * @author Brian Wing Shun Chan
060     * @author Raymond Aug??
061     */
062    public class SCProductEntryLocalServiceImpl
063            extends SCProductEntryLocalServiceBaseImpl {
064    
065            @Indexable(type = IndexableType.REINDEX)
066            @Override
067            public SCProductEntry addProductEntry(
068                            long userId, String name, String type, String tags,
069                            String shortDescription, String longDescription, String pageURL,
070                            String author, String repoGroupId, String repoArtifactId,
071                            long[] licenseIds, List<byte[]> thumbnails, List<byte[]> fullImages,
072                            ServiceContext serviceContext)
073                    throws PortalException {
074    
075                    // Product entry
076    
077                    User user = userPersistence.findByPrimaryKey(userId);
078                    long groupId = serviceContext.getScopeGroupId();
079                    tags = getTags(tags);
080                    repoGroupId = StringUtil.toLowerCase(repoGroupId.trim());
081                    repoArtifactId = StringUtil.toLowerCase(repoArtifactId.trim());
082                    Date now = new Date();
083    
084                    validate(
085                            0, name, type, shortDescription, pageURL, author, repoGroupId,
086                            repoArtifactId, licenseIds, thumbnails, fullImages);
087    
088                    long productEntryId = counterLocalService.increment();
089    
090                    SCProductEntry productEntry = scProductEntryPersistence.create(
091                            productEntryId);
092    
093                    productEntry.setGroupId(groupId);
094                    productEntry.setCompanyId(user.getCompanyId());
095                    productEntry.setUserId(user.getUserId());
096                    productEntry.setUserName(user.getFullName());
097                    productEntry.setCreateDate(now);
098                    productEntry.setModifiedDate(now);
099                    productEntry.setName(name);
100                    productEntry.setType(type);
101                    productEntry.setTags(tags);
102                    productEntry.setShortDescription(shortDescription);
103                    productEntry.setLongDescription(longDescription);
104                    productEntry.setPageURL(pageURL);
105                    productEntry.setAuthor(author);
106                    productEntry.setRepoGroupId(repoGroupId);
107                    productEntry.setRepoArtifactId(repoArtifactId);
108    
109                    scProductEntryPersistence.update(productEntry);
110    
111                    // Resources
112    
113                    if (serviceContext.isAddGroupPermissions() ||
114                            serviceContext.isAddGuestPermissions()) {
115    
116                            addProductEntryResources(
117                                    productEntry, serviceContext.isAddGroupPermissions(),
118                                    serviceContext.isAddGuestPermissions());
119                    }
120                    else {
121                            addProductEntryResources(
122                                    productEntry, serviceContext.getGroupPermissions(),
123                                    serviceContext.getGuestPermissions());
124                    }
125    
126                    // Licenses
127    
128                    scProductEntryPersistence.setSCLicenses(productEntryId, licenseIds);
129    
130                    // Product screenshots
131    
132                    saveProductScreenshots(productEntry, thumbnails, fullImages);
133    
134                    // Message boards
135    
136                    if (PropsValues.SC_PRODUCT_COMMENTS_ENABLED) {
137                            mbMessageLocalService.addDiscussionMessage(
138                                    userId, productEntry.getUserName(), groupId,
139                                    SCProductEntry.class.getName(), productEntryId,
140                                    WorkflowConstants.ACTION_PUBLISH);
141                    }
142    
143                    return productEntry;
144            }
145    
146            @Override
147            public void addProductEntryResources(
148                            long productEntryId, boolean addGroupPermissions,
149                            boolean addGuestPermissions)
150                    throws PortalException {
151    
152                    SCProductEntry productEntry =
153                            scProductEntryPersistence.findByPrimaryKey(productEntryId);
154    
155                    addProductEntryResources(
156                            productEntry, addGroupPermissions, addGuestPermissions);
157            }
158    
159            @Override
160            public void addProductEntryResources(
161                            long productEntryId, String[] groupPermissions,
162                            String[] guestPermissions)
163                    throws PortalException {
164    
165                    SCProductEntry productEntry =
166                            scProductEntryPersistence.findByPrimaryKey(productEntryId);
167    
168                    addProductEntryResources(
169                            productEntry, groupPermissions, guestPermissions);
170            }
171    
172            @Override
173            public void addProductEntryResources(
174                            SCProductEntry productEntry, boolean addGroupPermissions,
175                            boolean addGuestPermissions)
176                    throws PortalException {
177    
178                    resourceLocalService.addResources(
179                            productEntry.getCompanyId(), productEntry.getGroupId(),
180                            productEntry.getUserId(), SCProductEntry.class.getName(),
181                            productEntry.getProductEntryId(), false, addGroupPermissions,
182                            addGuestPermissions);
183            }
184    
185            @Override
186            public void addProductEntryResources(
187                            SCProductEntry productEntry, String[] groupPermissions,
188                            String[] guestPermissions)
189                    throws PortalException {
190    
191                    resourceLocalService.addModelResources(
192                            productEntry.getCompanyId(), productEntry.getGroupId(),
193                            productEntry.getUserId(), SCProductEntry.class.getName(),
194                            productEntry.getProductEntryId(), groupPermissions,
195                            guestPermissions);
196            }
197    
198            @Override
199            public void deleteProductEntries(long groupId) throws PortalException {
200                    List<SCProductEntry> productEntries =
201                            scProductEntryPersistence.findByGroupId(groupId);
202    
203                    for (SCProductEntry productEntry : productEntries) {
204                            scProductEntryLocalService.deleteProductEntry(productEntry);
205                    }
206            }
207    
208            @Indexable(type = IndexableType.DELETE)
209            @Override
210            public SCProductEntry deleteProductEntry(long productEntryId)
211                    throws PortalException {
212    
213                    SCProductEntry productEntry =
214                            scProductEntryPersistence.findByPrimaryKey(productEntryId);
215    
216                    return deleteProductEntry(productEntry);
217            }
218    
219            @Indexable(type = IndexableType.DELETE)
220            @Override
221            public SCProductEntry deleteProductEntry(SCProductEntry productEntry)
222                    throws PortalException {
223    
224                    // Product entry
225    
226                    scProductEntryPersistence.remove(productEntry);
227    
228                    // Resources
229    
230                    resourceLocalService.deleteResource(
231                            productEntry.getCompanyId(), SCProductEntry.class.getName(),
232                            ResourceConstants.SCOPE_INDIVIDUAL,
233                            productEntry.getProductEntryId());
234    
235                    // Subscriptions
236    
237                    subscriptionLocalService.deleteSubscriptions(
238                            productEntry.getCompanyId(), SCProductEntry.class.getName(),
239                            productEntry.getProductEntryId());
240    
241                    // Product screenshots
242    
243                    scProductScreenshotLocalService.deleteProductScreenshots(
244                            productEntry.getProductEntryId());
245    
246                    // Product versions
247    
248                    scProductVersionLocalService.deleteProductVersions(
249                            productEntry.getProductEntryId());
250    
251                    // Message boards
252    
253                    mbMessageLocalService.deleteDiscussionMessages(
254                            SCProductEntry.class.getName(), productEntry.getProductEntryId());
255    
256                    // Ratings
257    
258                    ratingsStatsLocalService.deleteStats(
259                            SCProductEntry.class.getName(), productEntry.getProductEntryId());
260    
261                    return productEntry;
262            }
263    
264            @Override
265            public List<SCProductEntry> getCompanyProductEntries(
266                    long companyId, int start, int end) {
267    
268                    return scProductEntryPersistence.findByCompanyId(companyId, start, end);
269            }
270    
271            @Override
272            public int getCompanyProductEntriesCount(long companyId) {
273                    return scProductEntryPersistence.countByCompanyId(companyId);
274            }
275    
276            @Override
277            public List<SCProductEntry> getProductEntries(
278                    long groupId, int start, int end) {
279    
280                    return scProductEntryPersistence.findByGroupId(groupId, start, end);
281            }
282    
283            @Override
284            public List<SCProductEntry> getProductEntries(
285                    long groupId, int start, int end,
286                    OrderByComparator<SCProductEntry> obc) {
287    
288                    return scProductEntryPersistence.findByGroupId(
289                            groupId, start, end, obc);
290            }
291    
292            @Override
293            public List<SCProductEntry> getProductEntries(
294                    long groupId, long userId, int start, int end) {
295    
296                    return scProductEntryPersistence.findByG_U(groupId, userId, start, end);
297            }
298    
299            @Override
300            public List<SCProductEntry> getProductEntries(
301                    long groupId, long userId, int start, int end,
302                    OrderByComparator<SCProductEntry> obc) {
303    
304                    return scProductEntryPersistence.findByG_U(
305                            groupId, userId, start, end, obc);
306            }
307    
308            @Override
309            public int getProductEntriesCount(long groupId) {
310                    return scProductEntryPersistence.countByGroupId(groupId);
311            }
312    
313            @Override
314            public int getProductEntriesCount(long groupId, long userId) {
315                    return scProductEntryPersistence.countByG_U(groupId, userId);
316            }
317    
318            @Override
319            public SCProductEntry getProductEntry(long productEntryId)
320                    throws PortalException {
321    
322                    return scProductEntryPersistence.findByPrimaryKey(productEntryId);
323            }
324    
325            @Override
326            public String getRepositoryXML(
327                    long groupId, String baseImageURL, Date oldestDate,
328                    int maxNumOfVersions, Properties repoSettings) {
329    
330                    return getRepositoryXML(
331                            groupId, null, baseImageURL, oldestDate, maxNumOfVersions,
332                            repoSettings);
333            }
334    
335            @Override
336            public String getRepositoryXML(
337                    long groupId, String version, String baseImageURL, Date oldestDate,
338                    int maxNumOfVersions, Properties repoSettings) {
339    
340                    Document doc = SAXReaderUtil.createDocument();
341    
342                    doc.setXMLEncoding(StringPool.UTF8);
343    
344                    Element root = doc.addElement("plugin-repository");
345    
346                    Element settingsEl = root.addElement("settings");
347    
348                    populateSettingsElement(settingsEl, repoSettings);
349    
350                    List<SCProductEntry> productEntries =
351                            scProductEntryPersistence.findByGroupId(groupId);
352    
353                    for (SCProductEntry productEntry : productEntries) {
354                            if (Validator.isNull(productEntry.getRepoGroupId()) ||
355                                    Validator.isNull(productEntry.getRepoArtifactId())) {
356    
357                                    continue;
358                            }
359    
360                            List<SCProductVersion> productVersions =
361                                    scProductVersionPersistence.findByProductEntryId(
362                                            productEntry.getProductEntryId());
363    
364                            for (int i = 0; i < productVersions.size(); i++) {
365                                    SCProductVersion productVersion = productVersions.get(i);
366    
367                                    if ((maxNumOfVersions > 0) && (maxNumOfVersions < (i + 1))) {
368                                            break;
369                                    }
370    
371                                    if (!productVersion.isRepoStoreArtifact()) {
372                                            continue;
373                                    }
374    
375                                    if ((oldestDate != null) &&
376                                            oldestDate.after(productVersion.getModifiedDate())) {
377    
378                                            continue;
379                                    }
380    
381                                    if (Validator.isNotNull(version) &&
382                                            !isVersionSupported(
383                                                    version, productVersion.getFrameworkVersions())) {
384    
385                                            continue;
386                                    }
387    
388                                    Element el = root.addElement("plugin-package");
389    
390                                    populatePluginPackageElement(
391                                            el, productEntry, productVersion, baseImageURL);
392                            }
393                    }
394    
395                    return doc.asXML();
396            }
397    
398            @Indexable(type = IndexableType.REINDEX)
399            @Override
400            public SCProductEntry updateProductEntry(
401                            long productEntryId, String name, String type, String tags,
402                            String shortDescription, String longDescription, String pageURL,
403                            String author, String repoGroupId, String repoArtifactId,
404                            long[] licenseIds, List<byte[]> thumbnails, List<byte[]> fullImages)
405                    throws PortalException {
406    
407                    // Product entry
408    
409                    tags = getTags(tags);
410                    repoGroupId = StringUtil.toLowerCase(repoGroupId.trim());
411                    repoArtifactId = StringUtil.toLowerCase(repoArtifactId.trim());
412                    Date now = new Date();
413    
414                    validate(
415                            productEntryId, name, type, shortDescription, pageURL, author,
416                            repoGroupId, repoArtifactId, licenseIds, thumbnails, fullImages);
417    
418                    SCProductEntry productEntry =
419                            scProductEntryPersistence.findByPrimaryKey(productEntryId);
420    
421                    productEntry.setModifiedDate(now);
422                    productEntry.setName(name);
423                    productEntry.setType(type);
424                    productEntry.setTags(tags);
425                    productEntry.setShortDescription(shortDescription);
426                    productEntry.setLongDescription(longDescription);
427                    productEntry.setPageURL(pageURL);
428                    productEntry.setAuthor(author);
429                    productEntry.setRepoGroupId(repoGroupId);
430                    productEntry.setRepoArtifactId(repoArtifactId);
431    
432                    scProductEntryPersistence.update(productEntry);
433    
434                    // Licenses
435    
436                    scProductEntryPersistence.setSCLicenses(productEntryId, licenseIds);
437    
438                    // Product screenshots
439    
440                    if (thumbnails.isEmpty()) {
441                            scProductScreenshotLocalService.deleteProductScreenshots(
442                                    productEntryId);
443                    }
444                    else {
445                            saveProductScreenshots(productEntry, thumbnails, fullImages);
446                    }
447    
448                    return productEntry;
449            }
450    
451            protected String getTags(String tags) {
452                    tags = StringUtil.toLowerCase(tags.trim());
453    
454                    return StringUtil.merge(StringUtil.split(tags), ", ");
455            }
456    
457            protected boolean isVersionSupported(
458                    String version, List<SCFrameworkVersion> frameworkVersions) {
459    
460                    Version currentVersion = Version.getInstance(version);
461    
462                    for (SCFrameworkVersion frameworkVersion : frameworkVersions) {
463                            Version supportedVersion = Version.getInstance(
464                                    frameworkVersion.getName());
465    
466                            if (supportedVersion.includes(currentVersion)) {
467                                    return true;
468                            }
469                    }
470    
471                    return false;
472            }
473    
474            protected void populatePluginPackageElement(
475                    Element el, SCProductEntry productEntry,
476                    SCProductVersion productVersion, String baseImageURL) {
477    
478                    DocUtil.add(el, "name", productEntry.getName());
479    
480                    String moduleId = ModuleId.toString(
481                            productEntry.getRepoGroupId(), productEntry.getRepoArtifactId(),
482                            productVersion.getVersion(), "war");
483    
484                    DocUtil.add(el, "module-id", moduleId);
485    
486                    DocUtil.add(
487                            el, "modified-date",
488                            Time.getRFC822(productVersion.getModifiedDate()));
489    
490                    Element typesEl = el.addElement("types");
491    
492                    DocUtil.add(typesEl, "type", productEntry.getType());
493    
494                    Element tagsEl = el.addElement("tags");
495    
496                    String[] tags = StringUtil.split(productEntry.getTags());
497    
498                    for (int i = 0; i < tags.length; i++) {
499                            DocUtil.add(tagsEl, "tag", tags[i]);
500                    }
501    
502                    DocUtil.add(
503                            el, "short-description", productEntry.getShortDescription());
504    
505                    if (Validator.isNotNull(productEntry.getLongDescription())) {
506                            DocUtil.add(
507                                    el, "long-description", productEntry.getLongDescription());
508                    }
509    
510                    if (Validator.isNotNull(productVersion.getChangeLog())) {
511                            DocUtil.add(el, "change-log", productVersion.getChangeLog());
512                    }
513    
514                    if (Validator.isNotNull(productVersion.getDirectDownloadURL())) {
515                            DocUtil.add(
516                                    el, "download-url", productVersion.getDirectDownloadURL());
517                    }
518    
519                    DocUtil.add(el, "author", productEntry.getAuthor());
520    
521                    Element screenshotsEl = el.addElement("screenshots");
522    
523                    for (SCProductScreenshot screenshot : productEntry.getScreenshots()) {
524                            long thumbnailId = screenshot.getThumbnailId();
525                            long fullImageId = screenshot.getFullImageId();
526    
527                            Element screenshotEl = screenshotsEl.addElement("screenshot");
528    
529                            DocUtil.add(
530                                    screenshotEl, "thumbnail-url",
531                                    baseImageURL + "?img_id=" + thumbnailId + "&t=" +
532                                            WebServerServletTokenUtil.getToken(thumbnailId));
533                            DocUtil.add(
534                                    screenshotEl, "large-image-url",
535                                    baseImageURL + "?img_id=" + fullImageId + "&t=" +
536                                            WebServerServletTokenUtil.getToken(fullImageId));
537                    }
538    
539                    Element licensesEl = el.addElement("licenses");
540    
541                    for (SCLicense license : productEntry.getLicenses()) {
542                            Element licenseEl = licensesEl.addElement("license");
543    
544                            licenseEl.addText(license.getName());
545                            licenseEl.addAttribute(
546                                    "osi-approved", String.valueOf(license.isOpenSource()));
547                    }
548    
549                    Element liferayVersionsEl = el.addElement("liferay-versions");
550    
551                    for (SCFrameworkVersion frameworkVersion :
552                                    productVersion.getFrameworkVersions()) {
553    
554                            DocUtil.add(
555                                    liferayVersionsEl, "liferay-version",
556                                    frameworkVersion.getName());
557                    }
558            }
559    
560            protected void populateSettingsElement(
561                    Element el, Properties repoSettings) {
562    
563                    if (repoSettings == null) {
564                            return;
565                    }
566    
567                    for (Map.Entry<Object, Object> entry : repoSettings.entrySet()) {
568                            String name = (String)entry.getKey();
569                            String value = (String)entry.getValue();
570    
571                            Element settingEl = el.addElement("setting");
572    
573                            settingEl.addAttribute("name", name);
574                            settingEl.addAttribute("value", value);
575                    }
576            }
577    
578            protected void saveProductScreenshots(
579                            SCProductEntry productEntry, List<byte[]> thumbnails,
580                            List<byte[]> fullImages)
581                    throws PortalException {
582    
583                    long productEntryId = productEntry.getProductEntryId();
584    
585                    List<SCProductScreenshot> productScreenshots =
586                            scProductScreenshotPersistence.findByProductEntryId(productEntryId);
587    
588                    if (thumbnails.size() < productScreenshots.size()) {
589                            for (int i = thumbnails.size(); i < productScreenshots.size();
590                                            i++) {
591    
592                                    SCProductScreenshot productScreenshot = productScreenshots.get(
593                                            i);
594    
595                                    scProductScreenshotLocalService.deleteProductScreenshot(
596                                            productScreenshot);
597                            }
598                    }
599    
600                    for (int i = 0; i < thumbnails.size(); i++) {
601                            int priority = i;
602    
603                            byte[] thumbnail = thumbnails.get(i);
604                            byte[] fullImage = fullImages.get(i);
605    
606                            SCProductScreenshot productScreenshot =
607                                    scProductScreenshotPersistence.fetchByP_P(
608                                            productEntryId, priority);
609    
610                            if (productScreenshot == null) {
611                                    long productScreenshotId = counterLocalService.increment();
612    
613                                    productScreenshot = scProductScreenshotPersistence.create(
614                                            productScreenshotId);
615    
616                                    productScreenshot.setCompanyId(productEntry.getCompanyId());
617                                    productScreenshot.setGroupId(productEntry.getGroupId());
618                                    productScreenshot.setProductEntryId(productEntryId);
619                                    productScreenshot.setThumbnailId(
620                                            counterLocalService.increment());
621                                    productScreenshot.setFullImageId(
622                                            counterLocalService.increment());
623                                    productScreenshot.setPriority(priority);
624    
625                                    scProductScreenshotPersistence.update(productScreenshot);
626                            }
627    
628                            imageLocalService.updateImage(
629                                    productScreenshot.getThumbnailId(), thumbnail);
630                            imageLocalService.updateImage(
631                                    productScreenshot.getFullImageId(), fullImage);
632                    }
633            }
634    
635            protected void validate(
636                            long productEntryId, String name, String type,
637                            String shortDescription, String pageURL, String author,
638                            String repoGroupId, String repoArtifactId, long[] licenseIds,
639                            List<byte[]> thumbnails, List<byte[]> fullImages)
640                    throws PortalException {
641    
642                    if (Validator.isNull(name)) {
643                            throw new ProductEntryNameException();
644                    }
645    
646                    if (Validator.isNull(type)) {
647                            throw new ProductEntryTypeException();
648                    }
649    
650                    if (Validator.isNull(shortDescription)) {
651                            throw new ProductEntryShortDescriptionException();
652                    }
653    
654                    if (Validator.isNull(pageURL)) {
655                            throw new ProductEntryPageURLException();
656                    }
657                    else if (!Validator.isUrl(pageURL)) {
658                            throw new ProductEntryPageURLException();
659                    }
660    
661                    if (Validator.isNull(author)) {
662                            throw new ProductEntryAuthorException();
663                    }
664    
665                    SCProductEntry productEntry = scProductEntryPersistence.fetchByRG_RA(
666                            repoGroupId, repoArtifactId);
667    
668                    if ((productEntry != null) &&
669                            (productEntry.getProductEntryId() != productEntryId)) {
670    
671                            throw new DuplicateProductEntryModuleIdException(
672                                    "{productEntryId=" + productEntryId + "}");
673                    }
674    
675                    if (licenseIds.length == 0) {
676                            throw new ProductEntryLicenseException();
677                    }
678    
679                    if (thumbnails.size() != fullImages.size()) {
680                            throw new ProductEntryScreenshotsException();
681                    }
682                    else {
683                            for (byte[] thumbnail : thumbnails) {
684                                    if (thumbnail == null) {
685                                            throw new ProductEntryScreenshotsException();
686                                    }
687                            }
688    
689                            for (byte[] fullImage : fullImages) {
690                                    if (fullImage == null) {
691                                            throw new ProductEntryScreenshotsException();
692                                    }
693                            }
694                    }
695            }
696    
697    }