/******************************************************************************* * Copyright (c) 2006 - 2006 Mylar eclipse.org project and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Mylar project committers - initial API and implementation *******************************************************************************/ /******************************************************************************* * Copyright (c) 2007, 2008 - 2007 IT Solutions, Inc. and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Chris Hane - adapted Trac implementation for Mantis * David Carver - STAR - Migrated to Mylyn 3.0 *******************************************************************************/ package com.itsolut.mantis.core; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.eclipse.core.runtime.IPath; import org.eclipse.mylyn.internal.tasks.core.IRepositoryChangeListener; import org.eclipse.mylyn.internal.tasks.core.IRepositoryConstants; import org.eclipse.mylyn.internal.tasks.core.TaskRepositoryChangeEvent; import org.eclipse.mylyn.internal.tasks.core.TaskRepositoryDelta; import org.eclipse.mylyn.internal.tasks.core.TaskRepositoryDelta.Type; import org.eclipse.mylyn.tasks.core.IRepositoryListener; import org.eclipse.mylyn.tasks.core.TaskRepository; import com.google.inject.Inject; import com.google.inject.Singleton; import com.itsolut.mantis.core.exception.MantisException; /** * Caches {@link IMantisClient} objects. * * @author Robert Munteanu */ @Singleton public class MantisClientManager implements IRepositoryListener, IRepositoryChangeListener, IMantisClientManager, IShutdown { private Map<String, IMantisClient> clientByUrl = new HashMap<String, IMantisClient>(); private final PersistedState state; private final MantisClientFactory clientFactory; private final Tracer tracer; @Inject public MantisClientManager(@RepositoryPersistencePath IPath repositoryPersistencePath, MantisClientFactory clientFactory, Tracer tracer) { this.clientFactory = clientFactory; state = new PersistedState(repositoryPersistencePath.toFile()); this.tracer = tracer; } public synchronized void onShutdown() { state.write(); } public synchronized IMantisClient getRepository(TaskRepository taskRepository) throws MantisException { IMantisClient client = clientByUrl.get(taskRepository.getRepositoryUrl()); if (client == null) client = newMantisClient(taskRepository); return client; } private IMantisClient newMantisClient(TaskRepository taskRepository) throws MantisException { IMantisClient repository = clientFactory.createClient(taskRepository); String repositoryUrl = taskRepository.getUrl(); MantisCacheData cacheData = state.get(repositoryUrl); if (cacheData != null) { repository.setCacheData(cacheData); } else { state.add(repositoryUrl, repository.getCacheData()); } clientByUrl.put(taskRepository.getRepositoryUrl(), repository); return repository; } public synchronized void repositoryAdded(TaskRepository repository) { if (!MantisCorePlugin.REPOSITORY_KIND.equals(repository.getConnectorKind())) return; // make sure there is no stale client still in the cache, bug #149939 clientByUrl.remove(repository.getRepositoryUrl()); state.remove(repository.getRepositoryUrl()); } public synchronized void repositoryRemoved(TaskRepository repository) { if (!MantisCorePlugin.REPOSITORY_KIND.equals(repository.getConnectorKind())) return; clientByUrl.remove(repository.getRepositoryUrl()); state.remove(repository.getRepositoryUrl()); } public synchronized void repositoryChanged(TaskRepositoryChangeEvent event) { TaskRepository repository = event.getRepository(); TaskRepositoryDelta delta = event.getDelta(); if (!MantisCorePlugin.REPOSITORY_KIND.equals(repository.getConnectorKind())) return; tracer.trace(TraceLocation.CLIENT_MANAGER, "repositoryChanged : {0} , {1} = {2} .", repository.getUrl(), delta.getType(), delta.getKey()); boolean credentialsChanged = delta.getType() == Type.CREDENTIALS; boolean urlChanged = delta.getType() == Type.PROPERTY && delta.getKey().equals(IRepositoryConstants.PROPERTY_URL); if ( !credentialsChanged && !urlChanged ) return; tracer.trace(TraceLocation.CLIENT_MANAGER, "Clearing repository state; credentialsChanged: {0}, urlChanged: {1}", credentialsChanged, urlChanged);; clientByUrl.remove(repository.getRepositoryUrl()); state.remove(repository.getRepositoryUrl()); } public void repositorySettingsChanged(TaskRepository repository) { // handled in repositoryChanged } public void repositoryUrlChanged(TaskRepository repository, String oldUrl) { // handled in repositoryChanged } static class PersistedState implements Serializable { private static final long serialVersionUID = 1L; private Map<String, MantisCacheData> _cacheDataByUrl = new HashMap<String, MantisCacheData>(); private File cacheFile; private boolean read; public PersistedState(File cacheFile) { this.cacheFile = cacheFile; } public void add(String url, MantisCacheData data) { ensureRead(); add0(url, data); } private void ensureRead() { if ( !read ) read(); } private void add0(String url, MantisCacheData data) { _cacheDataByUrl.put(url, data); } public void remove(String url) { ensureRead(); _cacheDataByUrl.remove(url); } public MantisCacheData get(String url) { ensureRead(); return _cacheDataByUrl.get(url); } private void read() { ObjectInputStream in = null; try { in = new ObjectInputStream(new FileInputStream(cacheFile)); int size = in.readInt(); for (int i = 0; i < size; i++) { String url = (String) in.readObject(); MantisCacheData data = (MantisCacheData) in.readObject(); add0(url, data); } } catch ( FileNotFoundException cacheDoesNotExist) { // possible, deal with it } catch (Throwable e) { cleanCache(e); } finally { read = true; closeSilently(in); } } public void cleanCache(Throwable reason) { if ( MantisCorePlugin.getDefault() != null ) MantisCorePlugin.warn("Removing invalid cache file", reason); cacheFile.delete(); } public void write() { ensureRead(); ObjectOutputStream out = null; try { out = new ObjectOutputStream(new FileOutputStream(cacheFile)); out.writeInt(_cacheDataByUrl.size()); for (String url : _cacheDataByUrl.keySet()) { out.writeObject(url); out.writeObject(_cacheDataByUrl.get(url)); } } catch (Throwable e) { MantisCorePlugin.error("Failed writing persistent state.", e); } finally { closeSilently(out); } } private void closeSilently(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (IOException ignored) { // ignore } } } } }