package fr.openwide.maven.artifact.notifier.core.business.sync.service; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.commons.lang3.mutable.MutableInt; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.google.common.collect.Lists; import fr.openwide.core.jpa.exception.SecurityServiceException; import fr.openwide.core.jpa.exception.ServiceException; import fr.openwide.core.spring.property.service.IPropertyService; import fr.openwide.maven.artifact.notifier.core.business.artifact.model.Artifact; import fr.openwide.maven.artifact.notifier.core.business.artifact.model.ArtifactNotificationRule; import fr.openwide.maven.artifact.notifier.core.business.artifact.model.ArtifactStatus; import fr.openwide.maven.artifact.notifier.core.business.artifact.model.ArtifactVersion; import fr.openwide.maven.artifact.notifier.core.business.artifact.model.ArtifactVersionNotification; import fr.openwide.maven.artifact.notifier.core.business.artifact.model.ArtifactVersionNotificationStatus; import fr.openwide.maven.artifact.notifier.core.business.artifact.model.FollowedArtifact; import fr.openwide.maven.artifact.notifier.core.business.artifact.service.IArtifactNotificationRuleService; import fr.openwide.maven.artifact.notifier.core.business.artifact.service.IArtifactService; import fr.openwide.maven.artifact.notifier.core.business.artifact.service.IArtifactVersionNotificationService; import fr.openwide.maven.artifact.notifier.core.business.artifact.service.IArtifactVersionService; import fr.openwide.maven.artifact.notifier.core.business.artifact.service.IFollowedArtifactService; import fr.openwide.maven.artifact.notifier.core.business.notification.service.INotificationService; import fr.openwide.maven.artifact.notifier.core.business.project.model.Project; import fr.openwide.maven.artifact.notifier.core.business.project.model.ProjectVersion; import fr.openwide.maven.artifact.notifier.core.business.project.model.ProjectVersionStatus; import fr.openwide.maven.artifact.notifier.core.business.project.service.IProjectService; import fr.openwide.maven.artifact.notifier.core.business.project.service.IProjectVersionService; import fr.openwide.maven.artifact.notifier.core.business.search.model.ArtifactVersionBean; import fr.openwide.maven.artifact.notifier.core.business.statistics.model.Statistic.StatisticEnumKey; import fr.openwide.maven.artifact.notifier.core.business.statistics.service.IStatisticService; import fr.openwide.maven.artifact.notifier.core.business.user.model.User; import fr.openwide.maven.artifact.notifier.core.business.user.service.IUserService; import fr.openwide.maven.artifact.notifier.core.config.application.MavenArtifactNotifierConfigurer; import fr.openwide.maven.artifact.notifier.core.property.MavenArtifactNotifierCorePropertyIds; @Service("mavenSynchronizationService") public class MavenSynchronizationServiceImpl implements IMavenSynchronizationService { private static final int BATCH_PARTITION_SIZE = 50; @Autowired private IUserService userService; @Autowired private IArtifactService artifactService; @Autowired private IArtifactVersionService artifactVersionService; @Autowired private IArtifactVersionNotificationService artifactVersionNotificationService; @Autowired private IFollowedArtifactService followedArtifactService; @Autowired private INotificationService notificationService; @Autowired private IArtifactVersionProviderService artifactVersionProviderService; @Autowired private IArtifactNotificationRuleService artifactNotificationRuleService; @Autowired private IStatisticService statisticService; @Autowired private IProjectService projectService; @Autowired private IProjectVersionService projectVersionService; @Autowired private MavenArtifactNotifierConfigurer configurer; @Autowired private IPropertyService propertyService; @Override public void initializeAllArtifacts() throws ServiceException, SecurityServiceException, InterruptedException { synchronizeArtifacts(artifactService.listIdsByStatus(ArtifactStatus.NOT_INITIALIZED)); } @Override public void synchronizeArtifactsAndNotifyUsers(List<Long> artifactIds) throws ServiceException, SecurityServiceException, InterruptedException { synchronizeArtifacts(artifactIds); int notificationsSent = 0; for (Map.Entry<User, List<ArtifactVersionNotification>> entry : artifactVersionNotificationService.listNotificationsToSend().entrySet()) { List<ArtifactVersionNotification> notifications = entry.getValue(); Collections.sort(notifications); notificationService.sendNewVersionNotification(notifications, entry.getKey()); for (ArtifactVersionNotification notification : notifications) { notification.setStatus(ArtifactVersionNotificationStatus.SENT); artifactVersionNotificationService.update(notification); } notificationsSent += entry.getValue().size(); } statisticService.feed(StatisticEnumKey.NOTIFICATIONS_SENT_PER_DAY, notificationsSent); } @Override public void synchronizeAllArtifactsAndNotifyUsers() throws ServiceException, SecurityServiceException, InterruptedException { synchronizeArtifactsAndNotifyUsers(artifactService.listIds()); propertyService.set(MavenArtifactNotifierCorePropertyIds.LAST_SYNCHRONIZATION_DATE, new Date()); } @Override public void refreshAllLatestVersions() throws ServiceException, SecurityServiceException { for (Artifact artifact : artifactService.list()) { artifact.refreshCachedVersions(); artifactService.update(artifact); } } private void synchronizeArtifacts(List<Long> artifactIds) throws ServiceException, SecurityServiceException, InterruptedException { Integer pauseDelayInMilliseconds = configurer.getSynchronizationPauseDelayBetweenRequestsInMilliseconds(); MutableInt versionsReleasedCount = new MutableInt(0); List<List<Long>> artifactIdsPartitions = Lists.partition(artifactIds, BATCH_PARTITION_SIZE); for (List<Long> artifactIdsPartition : artifactIdsPartitions) { for (Long artifactId : artifactIdsPartition) { Artifact artifact = artifactService.getById(artifactId); synchronizeArtifact(artifact, versionsReleasedCount); if (pauseDelayInMilliseconds != null) { Thread.sleep(pauseDelayInMilliseconds); } } artifactService.flush(); artifactService.clear(); } statisticService.feed(StatisticEnumKey.VERSIONS_RELEASED_PER_DAY, versionsReleasedCount.toInteger()); } private void synchronizeArtifact(Artifact artifact, MutableInt versionsReleasedCount) throws ServiceException, SecurityServiceException { List<ArtifactVersionBean> versions = artifactVersionProviderService.getArtifactVersions(artifact.getGroup().getGroupId(), artifact.getArtifactId()); int newVersionsCount = addNewVersions(artifact, versions); if (ArtifactStatus.NOT_INITIALIZED.equals(artifact.getStatus())) { if (artifact.getMostRecentVersion() != null) { Date lastUpdateDate = artifact.getMostRecentVersion().getLastUpdateDate(); List<FollowedArtifact> followedArtifacts = followedArtifactService.listByArtifact(artifact); for (FollowedArtifact followedArtifact : followedArtifacts) { followedArtifact.setLastNotifiedVersionDate(lastUpdateDate); } } artifact.setStatus(ArtifactStatus.INITIALIZED); artifactService.update(artifact); } else { if (newVersionsCount > 0) { versionsReleasedCount.add(newVersionsCount); createNotificationsForUsers(artifact); } } } private int addNewVersions(Artifact artifact, List<ArtifactVersionBean> versions) throws ServiceException, SecurityServiceException { int newVersionsCount = 0; for (ArtifactVersionBean versionBean : versions) { ArtifactVersion artifactVersion = artifactVersionService.getByArtifactAndVersion(artifact, versionBean.getVersion()); if (artifactVersion == null) { artifactVersion = new ArtifactVersion(versionBean.getVersion(), new Date(versionBean.getTimestamp())); // it is necessary to create the version before adding it to the artifact because, otherwise, we have a cascading problem with latestVersion artifactVersionService.create(artifactVersion); artifact.addVersion(artifactVersion); artifactService.update(artifact); // we optionally push the version into the project pushNewVersionIntoProject(artifact, artifactVersion); ++newVersionsCount; } } return newVersionsCount; } private void pushNewVersionIntoProject(Artifact artifact, ArtifactVersion artifactVersion) throws ServiceException, SecurityServiceException { if (artifact.getProject() != null) { Project project = artifact.getProject(); ProjectVersion projectVersion = projectVersionService.getByProjectAndVersion(artifact.getProject(), artifactVersion.getVersion()); if (projectVersion == null) { projectVersion = new ProjectVersion(artifactVersion.getVersion()); projectVersion.setStatus(ProjectVersionStatus.PUBLISHED_ON_MAVEN_CENTRAL); projectVersionService.create(projectVersion); project.addVersion(projectVersion); projectService.update(project); } else if (ProjectVersionStatus.IN_PROGRESS.equals(projectVersion.getStatus())) { projectVersion.setStatus(ProjectVersionStatus.PUBLISHED_ON_MAVEN_CENTRAL); } projectVersion.setLastUpdateDate(artifactVersion.getLastUpdateDate()); projectVersionService.update(projectVersion); artifactVersion.setProjectVersion(projectVersion); artifactVersionService.update(artifactVersion); } } private void createNotificationsForUsers(Artifact artifact) throws ServiceException, SecurityServiceException { List<FollowedArtifact> followedArtifacts = followedArtifactService.listByArtifact(artifact); for (FollowedArtifact followedArtifact : followedArtifacts) { followedArtifactService.update(followedArtifact); createNotifications(followedArtifact); } } private void createNotifications(FollowedArtifact followedArtifact) throws ServiceException, SecurityServiceException { if (followedArtifact.getLastNotifiedVersionDate() == null) { // this case shouldn't happen but it's better to be on the safe side rather than sending a bunch of unwanted notifications... followedArtifact.setLastNotifiedVersionDate(new Date()); followedArtifactService.update(followedArtifact); return; } User follower = followedArtifact.getUser(); if (!follower.isActive()) { return; } List<ArtifactVersion> lastVersions = artifactService.listArtifactVersionsAfterDate (followedArtifact.getArtifact(), followedArtifact.getLastNotifiedVersionDate()); List<ArtifactNotificationRule> rules = followedArtifactService.listArtifactNotificationRules(followedArtifact); List<ArtifactVersionNotification> notifications = Lists.newArrayList(); for (ArtifactVersion version : lastVersions) { // Rules check if (!artifactNotificationRuleService.checkRulesForVersion(version.getVersion(), rules)) { continue; } ArtifactVersionNotification notification = new ArtifactVersionNotification(version); artifactVersionNotificationService.create(notification); follower.addNotification(notification); notifications.add(notification); if (followedArtifact.getLastNotifiedVersionDate().before(version.getLastUpdateDate())) { followedArtifact.setLastNotifiedVersionDate(version.getLastUpdateDate()); } } if (!lastVersions.isEmpty()) { userService.update(follower); } } }