001package co.codewizards.cloudstore.client;
002
003import java.io.File;
004import java.net.MalformedURLException;
005import java.net.URL;
006import java.util.ArrayList;
007import java.util.Collection;
008import java.util.HashMap;
009import java.util.List;
010import java.util.Map;
011import java.util.UUID;
012
013import org.kohsuke.args4j.Argument;
014import org.kohsuke.args4j.Option;
015import org.slf4j.Logger;
016import org.slf4j.LoggerFactory;
017
018import co.codewizards.cloudstore.core.progress.LoggerProgressMonitor;
019import co.codewizards.cloudstore.core.repo.local.LocalRepoManager;
020import co.codewizards.cloudstore.core.repo.local.LocalRepoManagerFactory;
021import co.codewizards.cloudstore.core.repo.local.LocalRepoRegistry;
022import co.codewizards.cloudstore.core.repo.local.LocalRepoTransaction;
023import co.codewizards.cloudstore.core.repo.sync.RepoToRepoSync;
024import co.codewizards.cloudstore.local.persistence.RemoteRepository;
025import co.codewizards.cloudstore.local.persistence.RemoteRepositoryDAO;
026
027public class SyncSubCommand extends SubCommandWithExistingLocalRepo {
028        private static final Logger logger = LoggerFactory.getLogger(SyncSubCommand.class);
029
030        @Argument(metaVar="<remote>", index=1, required=false, usage="An ID or URL of a remote repository. If none is specified, all remote repositories are synced.")
031        private String remote;
032
033        private UUID remoteRepositoryId;
034        private URL remoteRoot;
035
036        @Option(name="-localOnly", required=false, usage="Synchronise locally only. Do not communicate with any remote repository.")
037        private boolean localOnly;
038
039        @Override
040        public String getSubCommandDescription() {
041                return "Synchronise a local repository. Depending on the parameters, it synchronises only locally or with one or more remote repositories.";
042        }
043
044        @Override
045        public void prepare() throws Exception {
046                super.prepare();
047                remoteRepositoryId = null;
048                remoteRoot = null;
049                if (remote != null && !remote.isEmpty()) {
050                        try {
051                                remoteRepositoryId = UUID.fromString(remote);
052                        } catch (IllegalArgumentException x) {
053                                try {
054                                        remoteRoot = new URL(remote);
055                                } catch (MalformedURLException y) {
056                                        throw new IllegalArgumentException(String.format("<remote> '%s' is neither a valid repositoryId nor a valid URL!", remote));
057                                }
058                        }
059                }
060        }
061
062        @Override
063        protected void assertLocalRootNotNull() {
064                if (!isAll())
065                        super.assertLocalRootNotNull();
066        }
067
068        @Override
069        public void run() throws Exception {
070                if (isAll()) {
071                        for (UUID repositoryId : LocalRepoRegistry.getInstance().getRepositoryIds())
072                                sync(repositoryId);
073                }
074                else
075                        sync(localRoot);
076        }
077
078        private void sync(UUID repositoryId) {
079                File localRoot = LocalRepoRegistry.getInstance().getLocalRootOrFail(repositoryId);
080                sync(localRoot);
081        }
082
083        private void sync(File localRoot) {
084                List<URL> remoteRoots = new ArrayList<URL>();
085                Map<UUID, URL> filteredRemoteRepositoryId2RemoteRoot = new HashMap<UUID, URL>();
086                UUID repositoryId;
087                LocalRepoManager localRepoManager = LocalRepoManagerFactory.Helper.getInstance().createLocalRepoManagerForExistingRepository(localRoot);
088                try {
089                        if (localOnly) {
090                                localRepoManager.localSync(new LoggerProgressMonitor(logger));
091                                return;
092                        }
093
094                        repositoryId = localRepoManager.getRepositoryId();
095                        localRoot = localRepoManager.getLocalRoot();
096                        LocalRepoTransaction transaction = localRepoManager.beginReadTransaction();
097                        try {
098                                Collection<RemoteRepository> remoteRepositories = transaction.getDAO(RemoteRepositoryDAO.class).getObjects();
099                                for (RemoteRepository remoteRepository : remoteRepositories) {
100                                        if (remoteRepository.getRemoteRoot() == null)
101                                                continue;
102
103                                        remoteRoots.add(remoteRepository.getRemoteRoot());
104                                        if ((remoteRepositoryId == null && remoteRoot == null)
105                                                        || (remoteRepositoryId != null && remoteRepositoryId.equals(remoteRepository.getRepositoryId()))
106                                                        || (remoteRoot != null && remoteRoot.equals(remoteRepository.getRemoteRoot())))
107                                                filteredRemoteRepositoryId2RemoteRoot.put(remoteRepository.getRepositoryId(), remoteRepository.getRemoteRoot());
108                                }
109
110                                transaction.commit();
111                        } finally {
112                                transaction.rollbackIfActive();
113                        }
114                } finally {
115                        localRepoManager.close();
116                }
117
118                if (remoteRoots.isEmpty())
119                        System.err.println(String.format("WARNING: The repository %s ('%s') is not connected to any remote repository as client!", repositoryId, localRoot));
120                else if (filteredRemoteRepositoryId2RemoteRoot.isEmpty())
121                        System.err.println(String.format("WARNING: The repository %s ('%s') is not connected to the specified remote repository ('%s')!", repositoryId, localRoot, remote));
122                else {
123                        for (Map.Entry<UUID, URL> me : filteredRemoteRepositoryId2RemoteRoot.entrySet()) {
124                                UUID remoteRepositoryId = me.getKey();
125                                URL remoteRoot = me.getValue();
126                                System.out.println("********************************************************************************");
127                                System.out.println(String.format("Syncing %s ('%s') with %s ('%s').", repositoryId, localRoot, remoteRepositoryId, remoteRoot));
128                                System.out.println("********************************************************************************");
129                                RepoToRepoSync repoToRepoSync = new RepoToRepoSync(localRoot, remoteRoot);
130                                try {
131                                        repoToRepoSync.sync(new LoggerProgressMonitor(logger));
132                                } finally {
133                                        repoToRepoSync.close();
134                                }
135                        }
136                }
137        }
138
139        private boolean isAll() {
140                return "ALL".equals(local);
141        }
142}