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