/******************************************************************************* * Copyright (c) 2004, 2006 * Thomas Hallgren, Kenneth Olwing, Mitch Sonies * Pontus Rydin, Nils Unden, Peer Torngren * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the individual * copyright holders listed above, as Initial Contributors under such license. * The text of such license is available at www.eclipse.org. *******************************************************************************/ package org.eclipse.buckminster.cvspkg.internal; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.UUID; import org.eclipse.buckminster.cvspkg.CVSPlugin; import org.eclipse.buckminster.cvspkg.Messages; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.IOUtils; import org.eclipse.buckminster.runtime.Logger; import org.eclipse.buckminster.runtime.MonitorUtils; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.osgi.util.NLS; import org.eclipse.team.internal.ccvs.core.CVSException; import org.eclipse.team.internal.ccvs.core.CVSTag; import org.eclipse.team.internal.ccvs.core.client.Command; import org.eclipse.team.internal.ccvs.core.client.RLog; import org.eclipse.team.internal.ccvs.core.client.Session; @SuppressWarnings("restriction") public class RepositoryMetaData implements Serializable { private static final long serialVersionUID = 6869163410872011769L; private static final HashMap<UUID, RepositoryMetaData> metaDataCache = new HashMap<UUID, RepositoryMetaData>(); public static RepositoryMetaData getMetaData(CVSSession cvsSession, CVSTag fixedTag, IProgressMonitor monitor) throws CoreException { String repository = cvsSession.getRepository(); RepositoryMetaData result = RepositoryMetaData.load(fixedTag, repository); Date now = new Date(System.currentTimeMillis()); if (result != null) { Date resultTime = result.getTimestamp(); if (fixedTag != null && fixedTag.getType() == CVSTag.DATE && CVSReader.getTagDate(fixedTag).compareTo(resultTime) <= 0) // // Meta-data is newer then requested fixed date. // return result; // TODO: This value should be configurable. 5 minutes is reasonable // but // it might be too much in some cases. // if (now.getTime() - resultTime.getTime() < 300000) return result; } monitor.beginTask(null, 100); monitor.subTask(NLS.bind(Messages.obtaining_meta_data_for_repository_0, repository)); Session session = cvsSession.getReaderSession(MonitorUtils.subMonitor(monitor, 10)); try { Logger logger = CVSPlugin.getLogger(); ArrayList<Command.LocalOption> opts = new ArrayList<Command.LocalOption>(); if (result == null) { logger.debug("Initial metadata fetch for %s", repository); //$NON-NLS-1$ if (fixedTag != null && !fixedTag.equals(CVSTag.DEFAULT)) opts.add(RLog.getCurrentTag(fixedTag)); } else { if (fixedTag != null) { switch (fixedTag.getType()) { case CVSTag.DATE: // No use scanning longer then this. // now = fixedTag.asDate(); if (fixedTag.asDate().compareTo(result.getTimestamp()) <= 0) return result; fixedTag = null; break; case CVSTag.BRANCH: case CVSTag.VERSION: opts.add(RLog.getCurrentTag(fixedTag)); break; } } logger.debug("Metadata refetch from " + result.getTimestamp() + " for " + repository); //$NON-NLS-1$ //$NON-NLS-2$ opts.add(RLog.makeTagOption(new CVSTag(result.getTimestamp()), new CVSTag(now))); opts.add(RLog.ONLY_INCLUDE_CHANGES); } String[] args = new String[] { cvsSession.getModuleName() }; MetaDataCollector collector = new MetaDataCollector(); IStatus status = new RLog().execute(session, Command.NO_GLOBAL_OPTIONS, opts.toArray(new Command.LocalOption[opts.size()]), args, collector, new SubProgressMonitor(monitor, 90, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)); if (!status.isOK()) throw new CVSException(status); if (result == null) { if (collector.getLastModificationTime() == null) throw new CVSException(NLS.bind(Messages.found_no_metadata_for_0, repository)); result = new RepositoryMetaData(collector, now); } else result = result.merge(collector, now); result.store(fixedTag, repository); return result; } finally { monitor.done(); } } public static RepositoryMetaData load(CVSTag fixedTag, String repository) throws CoreException { UUID id = getRepositoryId(fixedTag, repository); RepositoryMetaData rmd = metaDataCache.get(id); if (rmd != null) return rmd; ObjectInputStream input = null; try { input = new ObjectInputStream(new FileInputStream(getStateFile(id))); rmd = (RepositoryMetaData) input.readObject(); metaDataCache.put(id, rmd); return rmd; } catch (FileNotFoundException e) { return null; } catch (InvalidClassException e) { // This happens if the serial version UUID has changed. We // allow that but consider the old data non-existent. // return null; } catch (ClassNotFoundException e) { throw BuckminsterException.wrap(e); } catch (IOException e) { throw BuckminsterException.wrap(e); } finally { IOUtils.close(input); } } private static Set<String> concat(String[] names, Set<String> moreNames) { HashSet<String> result = new HashSet<String>(names.length + moreNames.size()); for (String name : names) result.add(name); result.addAll(moreNames); return result; } private static UUID getRepositoryId(CVSTag fixedTag, String repository) { if (fixedTag != null && fixedTag.getType() != CVSTag.DATE) repository = repository + '#' + fixedTag; return UUID.nameUUIDFromBytes(repository.getBytes()); } private static File getStateFile(UUID id) { return CVSPlugin.getDefault().getStateLocation().append(id.toString()).toFile(); } private static String[] orderedArray(Set<String> names) { String[] ordered = names.toArray(new String[names.size()]); Arrays.sort(ordered); return ordered; } private final String[] branchNames; private final String[] tagNames; private final Date lastModification; private final Date timestamp; public RepositoryMetaData(MetaDataCollector collector, Date timestamp) { this(collector.getBranchNames(), collector.getTagNames(), collector.getLastModificationTime(), timestamp); } private RepositoryMetaData(Set<String> branches, Set<String> tags, Date lastModification, Date timestamp) { this.branchNames = orderedArray(branches); this.tagNames = orderedArray(tags); this.lastModification = lastModification; this.timestamp = timestamp; } public final String[] getBranchNames() { return branchNames; } public final Date getLastModification() { return lastModification; } public final String[] getTagNames() { return tagNames; } public final Date getTimestamp() { return timestamp; } public RepositoryMetaData merge(MetaDataCollector collector, Date ts) { Date lastModTime = collector.getLastModificationTime(); if (lastModTime == null) lastModTime = lastModification; return new RepositoryMetaData(concat(branchNames, collector.getBranchNames()), concat(tagNames, collector.getTagNames()), lastModTime, ts); } public void store(CVSTag fixedTag, String repository) throws CoreException { UUID id = getRepositoryId(fixedTag, repository); ObjectOutputStream output = null; try { output = new ObjectOutputStream(new FileOutputStream(getStateFile(id))); output.writeObject(this); metaDataCache.put(id, this); } catch (IOException e) { throw BuckminsterException.wrap(e); } finally { IOUtils.close(output); } } }