blob: e46de29d72dacf4136320a339541da7cea62ea68 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.ace.client.repository.impl;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.ace.client.repository.ObjectRepository;
import org.apache.ace.client.repository.PreCommitMember;
import org.apache.ace.client.repository.RepositoryAdmin;
import org.apache.ace.client.repository.RepositoryAdminLoginContext;
import org.apache.ace.client.repository.RepositoryObject;
import org.apache.ace.client.repository.RepositoryObject.WorkingState;
import org.apache.ace.client.repository.SessionFactory;
import org.apache.ace.client.repository.helper.ArtifactHelper;
import org.apache.ace.client.repository.impl.RepositoryAdminLoginContextImpl.RepositorySetDescriptor;
import org.apache.ace.client.repository.object.Artifact2FeatureAssociation;
import org.apache.ace.client.repository.object.ArtifactObject;
import org.apache.ace.client.repository.object.DeploymentVersionObject;
import org.apache.ace.client.repository.object.Distribution2TargetAssociation;
import org.apache.ace.client.repository.object.DistributionObject;
import org.apache.ace.client.repository.object.Feature2DistributionAssociation;
import org.apache.ace.client.repository.object.FeatureObject;
import org.apache.ace.client.repository.object.TargetObject;
import org.apache.ace.client.repository.repository.Artifact2FeatureAssociationRepository;
import org.apache.ace.client.repository.repository.ArtifactRepository;
import org.apache.ace.client.repository.repository.DeploymentVersionRepository;
import org.apache.ace.client.repository.repository.Distribution2TargetAssociationRepository;
import org.apache.ace.client.repository.repository.DistributionRepository;
import org.apache.ace.client.repository.repository.Feature2DistributionAssociationRepository;
import org.apache.ace.client.repository.repository.FeatureRepository;
import org.apache.ace.client.repository.repository.RepositoryConfiguration;
import org.apache.ace.client.repository.repository.TargetRepository;
import org.apache.ace.connectionfactory.ConnectionFactory;
import org.apache.ace.repository.Repository;
import org.apache.ace.repository.ext.BackupRepository;
import org.apache.ace.repository.ext.CachedRepository;
import org.apache.ace.repository.ext.impl.CachedRepositoryImpl;
import org.apache.ace.repository.ext.impl.FilebasedBackupRepository;
import org.apache.ace.repository.ext.impl.RemoteRepository;
import org.apache.felix.dm.Component;
import org.apache.felix.dm.DependencyManager;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.osgi.service.log.LogService;
import org.osgi.service.prefs.Preferences;
import org.osgi.service.prefs.PreferencesService;
import org.osgi.service.useradmin.User;
/**
* An implementation of RepositoryAdmin, responsible for managing <code>ObjectRepositoryImpl</code> descendants.<br>
* The actual repository managing is delegated to <code>RepositorySet</code>s, while the logic for binding these sets
* together is located in this class. Set actual <code>RepositorySet</code>s to be used are defined in
* <code>login(...)</code>.<br>
*/
public class RepositoryAdminImpl implements RepositoryAdmin {
private final static String PREFS_LOCAL_FILE_ROOT = "ClientRepositoryAdmin";
private final static String PREFS_LOCAL_FILE_LOCATION = "FileLocation";
private final static String PREFS_LOCAL_FILE_CURRENT = "current";
private final static String PREFS_LOCAL_FILE_BACKUP = "backup";
private static final String KEY_CAUSE = "cause";
private static final String CAUSE_COMMIT = "commit";
private static final String CAUSE_REVERT = "revert";
private static final String CAUSE_CHECKOUT = "checkout";
/**
* Maps from interface classes of the ObjectRepositories to their implementations.
*/
private Map<Class<? extends ObjectRepository<?>>, ObjectRepositoryImpl<?, ?>> m_repositories;
private final String m_sessionID;
private final Properties m_sessionProps;
private final RepositoryConfiguration m_repositoryConfig;
private final ChangeNotifier m_changeNotifier;
private final Object m_lock = new Object();
// Injected by dependency manager
private volatile DependencyManager m_dm;
private volatile BundleContext m_context;
private volatile PreferencesService m_preferences;
private volatile LogService m_log;
private User m_user;
private RepositorySet[] m_repositorySets;
private List<PreCommitMember> m_preCommitMembers;
private List<Component[]> m_services;
private ArtifactRepositoryImpl m_artifactRepositoryImpl;
private FeatureRepositoryImpl m_featureRepositoryImpl;
private Artifact2FeatureAssociationRepositoryImpl m_artifact2FeatureAssociationRepositoryImpl;
private DistributionRepositoryImpl m_distributionRepositoryImpl;
private Feature2DistributionAssociationRepositoryImpl m_feature2DistributionAssociationRepositoryImpl;
private TargetRepositoryImpl m_targetRepositoryImpl;
private Distribution2TargetAssociationRepositoryImpl m_distribution2TargetAssociationRepositoryImpl;
private DeploymentVersionRepositoryImpl m_deploymentVersionRepositoryImpl;
private ChangeNotifierManager m_changeNotifierManager;
public RepositoryAdminImpl(String sessionID, RepositoryConfiguration repoConfig) {
m_sessionID = sessionID;
m_repositoryConfig = repoConfig;
m_preCommitMembers = new ArrayList<>();
m_sessionProps = new Properties();
m_sessionProps.put(SessionFactory.SERVICE_SID, sessionID);
m_changeNotifierManager = new ChangeNotifierManager();
m_changeNotifier = m_changeNotifierManager.getConfiguredNotifier(RepositoryAdmin.PRIVATE_TOPIC_ROOT, RepositoryAdmin.PUBLIC_TOPIC_ROOT, RepositoryAdmin.TOPIC_ENTITY_ROOT, m_sessionID);
}
public Properties getSessionProps() {
return m_sessionProps;
}
/**
* Returns a list of instances that make up this composition. Instances are used to inject dependencies into.
*
* @return list of instances
*/
public Object[] getInstances() {
return new Object[] { this, m_changeNotifierManager };
}
public void start() {
synchronized (m_lock) {
initialize(publishRepositories());
}
}
public void stop() {
pullRepositories();
synchronized (m_lock) {
if (loggedIn()) {
try {
logout(true);
}
catch (IOException ioe) {
m_log.log(LogService.LOG_ERROR, "Failed to log out of the repositories.", ioe);
}
}
}
}
void initialize(Map<Class<? extends ObjectRepository<?>>, ObjectRepositoryImpl<?, ?>> repositories) {
m_repositories = repositories;
}
private ChangeNotifier createChangeNotifier(String topic) {
return m_changeNotifierManager.getConfiguredNotifier(topic, m_sessionID);
}
private Map<Class<? extends ObjectRepository<?>>, ObjectRepositoryImpl<?, ?>> publishRepositories() {
// create the repository objects, if this is the first time this method is called. In case a repository
// implementation needs some form of (runtime) configuration, adapt the RepositoryConfiguration object and pass
// this object to the repository...
if (m_artifactRepositoryImpl == null) {
m_artifactRepositoryImpl = new ArtifactRepositoryImpl(createChangeNotifier(ArtifactObject.TOPIC_ENTITY_ROOT), m_repositoryConfig);
m_featureRepositoryImpl = new FeatureRepositoryImpl(createChangeNotifier(FeatureObject.TOPIC_ENTITY_ROOT), m_repositoryConfig);
m_distributionRepositoryImpl = new DistributionRepositoryImpl(createChangeNotifier(DistributionObject.TOPIC_ENTITY_ROOT), m_repositoryConfig);
m_targetRepositoryImpl = new TargetRepositoryImpl(createChangeNotifier(TargetObject.TOPIC_ENTITY_ROOT), m_repositoryConfig);
//
m_deploymentVersionRepositoryImpl = new DeploymentVersionRepositoryImpl(createChangeNotifier(DeploymentVersionObject.TOPIC_ENTITY_ROOT), m_repositoryConfig);
//
m_artifact2FeatureAssociationRepositoryImpl = new Artifact2FeatureAssociationRepositoryImpl(m_artifactRepositoryImpl, m_featureRepositoryImpl, createChangeNotifier(Artifact2FeatureAssociation.TOPIC_ENTITY_ROOT), m_repositoryConfig);
m_feature2DistributionAssociationRepositoryImpl = new Feature2DistributionAssociationRepositoryImpl(m_featureRepositoryImpl, m_distributionRepositoryImpl, createChangeNotifier(Feature2DistributionAssociation.TOPIC_ENTITY_ROOT), m_repositoryConfig);
m_distribution2TargetAssociationRepositoryImpl = new Distribution2TargetAssociationRepositoryImpl(m_distributionRepositoryImpl, m_targetRepositoryImpl, createChangeNotifier(Distribution2TargetAssociation.TOPIC_ENTITY_ROOT), m_repositoryConfig);
}
// first, register the artifact repository manually; it needs some special care.
Component artifactRepoService = m_dm.createComponent()
.setInterface(ArtifactRepository.class.getName(), m_sessionProps)
.setImplementation(m_artifactRepositoryImpl)
.add(m_dm.createServiceDependency().setService(ConnectionFactory.class).setRequired(true))
.add(m_dm.createServiceDependency().setService(LogService.class).setRequired(false))
.add(m_dm.createServiceDependency().setService(ArtifactHelper.class).setRequired(false).setAutoConfig(false).setCallbacks(this, "addArtifactHelper", "removeArtifactHelper"));
Dictionary<String, Object> topic = new Hashtable<>();
topic.put(EventConstants.EVENT_FILTER, "(" + SessionFactory.SERVICE_SID + "=" + m_sessionID + ")");
topic.put(EventConstants.EVENT_TOPIC, new String[] {});
Component artifactHandlerService = m_dm.createComponent()
.setInterface(EventHandler.class.getName(), topic)
.setImplementation(m_artifactRepositoryImpl);
m_dm.add(artifactRepoService);
m_dm.add(artifactHandlerService);
m_services = new ArrayList<>();
m_services.add(new Component[] { artifactRepoService, artifactHandlerService });
// register all repositories are services. Keep the service objects around, we need them to pull the services
// later.
m_services.add(registerRepository(Artifact2FeatureAssociationRepository.class, m_artifact2FeatureAssociationRepositoryImpl, new String[] { createPrivateObjectTopic(ArtifactObject.TOPIC_ENTITY_ROOT),
createPrivateObjectTopic(FeatureObject.TOPIC_ENTITY_ROOT) }));
m_services.add(registerRepository(FeatureRepository.class, m_featureRepositoryImpl, new String[] {}));
m_services.add(registerRepository(Feature2DistributionAssociationRepository.class, m_feature2DistributionAssociationRepositoryImpl, new String[] { createPrivateObjectTopic(FeatureObject.TOPIC_ENTITY_ROOT),
createPrivateObjectTopic(DistributionObject.TOPIC_ENTITY_ROOT) }));
m_services.add(registerRepository(DistributionRepository.class, m_distributionRepositoryImpl, new String[] {}));
m_services.add(registerRepository(Distribution2TargetAssociationRepository.class, m_distribution2TargetAssociationRepositoryImpl, new String[] { createPrivateObjectTopic(DistributionObject.TOPIC_ENTITY_ROOT),
createPrivateObjectTopic(TargetObject.TOPIC_ENTITY_ROOT) }));
m_services.add(registerRepository(TargetRepository.class, m_targetRepositoryImpl, new String[] {}));
m_services.add(registerRepository(DeploymentVersionRepository.class, m_deploymentVersionRepositoryImpl, new String[] {}));
// prepare the results.
Map<Class<? extends ObjectRepository<?>>, ObjectRepositoryImpl<?, ?>> result = new HashMap<>();
result.put(ArtifactRepository.class, m_artifactRepositoryImpl);
result.put(Artifact2FeatureAssociationRepository.class, m_artifact2FeatureAssociationRepositoryImpl);
result.put(FeatureRepository.class, m_featureRepositoryImpl);
result.put(Feature2DistributionAssociationRepository.class, m_feature2DistributionAssociationRepositoryImpl);
result.put(DistributionRepository.class, m_distributionRepositoryImpl);
result.put(Distribution2TargetAssociationRepository.class, m_distribution2TargetAssociationRepositoryImpl);
result.put(TargetRepository.class, m_targetRepositoryImpl);
result.put(DeploymentVersionRepository.class, m_deploymentVersionRepositoryImpl);
return result;
}
/**
* Pulls all repository services; is used to make sure the repositories go away before the RepositoryAdmin does.
*/
private void pullRepositories() {
for (Component[] services : m_services) {
for (Component service : services) {
m_dm.remove(service);
}
}
}
private <T extends RepositoryObject> Component[] registerRepository(Class<? extends ObjectRepository<T>> iface, ObjectRepositoryImpl<?, T> implementation, String[] topics) {
Component repositoryService = m_dm.createComponent()
.setInterface(iface.getName(), m_sessionProps)
.setImplementation(implementation)
.add(m_dm.createServiceDependency().setService(LogService.class).setRequired(false));
Dictionary<String, Object> topic = new Hashtable<>();
topic.put(EventConstants.EVENT_TOPIC, topics);
topic.put(EventConstants.EVENT_FILTER, "(" + SessionFactory.SERVICE_SID + "=" + m_sessionID + ")");
Component handlerService = m_dm.createComponent()
.setInterface(EventHandler.class.getName(), topic)
.setImplementation(implementation);
m_dm.add(repositoryService);
m_dm.add(handlerService);
return new Component[] { repositoryService, handlerService };
}
/**
* Helper method for use in publishRepositories
*/
private static String createPrivateObjectTopic(String entityRoot) {
return RepositoryObject.PRIVATE_TOPIC_ROOT + entityRoot + RepositoryObject.TOPIC_ALL_SUFFIX;
}
public void checkout() throws IOException {
synchronized (m_lock) {
ensureLogin();
m_changeNotifier.notifyChanged(TOPIC_HOLDUNTILREFRESH_SUFFIX, null);
for (PreCommitMember member : m_preCommitMembers) {
member.reset();
}
for (RepositorySet set : m_repositorySets) {
set.checkout();
}
Properties props = new Properties();
props.put(KEY_CAUSE, CAUSE_CHECKOUT);
m_changeNotifier.notifyChanged(TOPIC_REFRESH_SUFFIX, props);
}
}
public void commit() throws IOException {
synchronized (m_lock) {
ensureLogin();
for (PreCommitMember member : m_preCommitMembers) {
member.preCommit();
}
for (RepositorySet set : m_repositorySets) {
set.commit();
}
Properties props = new Properties();
props.put(KEY_CAUSE, CAUSE_COMMIT);
m_changeNotifier.notifyChanged(TOPIC_REFRESH_SUFFIX, props);
}
}
public void flush() throws IOException {
synchronized (m_lock) {
ensureLogin();
for (RepositorySet set : m_repositorySets) {
set.writeLocal();
set.savePreferences();
}
m_changeNotifier.notifyChanged(TOPIC_FLUSHED_SUFFIX, null);
}
}
public void revert() throws IOException {
synchronized (m_lock) {
ensureLogin();
m_changeNotifier.notifyChanged(TOPIC_HOLDUNTILREFRESH_SUFFIX, null);
for (PreCommitMember member : m_preCommitMembers) {
member.reset();
}
for (RepositorySet set : m_repositorySets) {
set.revert();
}
Properties props = new Properties();
props.put(KEY_CAUSE, CAUSE_REVERT);
m_changeNotifier.notifyChanged(TOPIC_REFRESH_SUFFIX, props);
}
}
public boolean isCurrent() throws IOException {
synchronized (m_lock) {
ensureLogin();
boolean result = true;
for (RepositorySet set : m_repositorySets) {
result &= (set.isCurrent() || !set.writeAccess());
}
return result;
}
}
public boolean isModified() {
synchronized (m_lock) {
ensureLogin();
for (PreCommitMember member : m_preCommitMembers) {
if (member.hasChanges()) {
return true;
}
}
for (RepositorySet set : m_repositorySets) {
if (set.isModified()) {
return true;
}
}
return false;
}
}
public RepositoryAdminLoginContext createLoginContext(User user) {
if (user == null) {
throw new IllegalArgumentException("User may not be null.");
}
return new RepositoryAdminLoginContextImpl(user, m_sessionID);
}
public void login(RepositoryAdminLoginContext context) throws IOException {
if (!(context instanceof RepositoryAdminLoginContextImpl)) {
throw new IllegalArgumentException("Only the RepositoryAdminLoginContext returned by createLoginContext can be used.");
}
RepositoryAdminLoginContextImpl impl = ((RepositoryAdminLoginContextImpl) context);
RepositorySet[] repositorySets = getRepositorySets(impl);
synchronized (m_lock) {
login(impl.getUser(), repositorySets);
}
}
/**
* Helper method for login; also allows injection of custom RepositorySet objects for testing purposes.
*
* @throws IOException
*/
private void login(User user, RepositorySet[] sets) throws IOException {
synchronized (m_lock) {
if (m_user != null) {
throw new IllegalStateException("Another user is logged in.");
}
m_user = user;
m_repositorySets = sets;
m_changeNotifier.notifyChanged(TOPIC_HOLDUNTILREFRESH_SUFFIX, null);
for (RepositorySet set : m_repositorySets) {
set.readLocal();
set.loadPreferences();
}
}
m_changeNotifier.notifyChanged(TOPIC_LOGIN_SUFFIX, null);
}
public void logout(boolean force) throws IOException {
IOException exception = null;
synchronized (m_lock) {
ensureLogin();
try {
flush();
}
catch (IOException e) {
if (!force) {
throw e;
}
else {
exception = e;
}
}
for (RepositorySet set : m_repositorySets) {
set.clearRepositories();
set.unregisterHandler();
// set.deleteLocal();
}
unloadRepositorySet(m_user);
m_user = null;
// m_repositorySets = new RepositorySet[0];
}
m_changeNotifier.notifyChanged(TOPIC_LOGOUT_SUFFIX, null);
if (exception != null) {
throw exception;
}
}
public void deleteLocal() {
synchronized (m_lock) {
if (m_user != null && m_repositorySets != null) {
for (RepositorySet set : m_repositorySets) {
set.deleteLocal();
}
}
}
}
private boolean loggedIn() {
return m_user != null;
}
/**
* Helper method to make sure a user is logged in.
*
* @throws IllegalStateException
*/
private void ensureLogin() throws IllegalStateException {
if (!loggedIn()) {
throw new IllegalStateException("This operation requires a user to be logged in.");
}
}
/**
* Helper method, creates RepositorySets based on the Login context.
*/
private RepositorySet[] getRepositorySets(RepositoryAdminLoginContextImpl context) throws IOException {
List<RepositorySetDescriptor> descriptors = context.getDescriptors();
// First, some sanity checks on the list of descriptors.
for (RepositorySetDescriptor rsd : descriptors) {
for (Class<?> c : rsd.m_objectRepositories) {
// Do we have an impl for each repository class?
if (!m_repositories.containsKey(c)) {
throw new IllegalArgumentException(rsd.toString() + " references repository class " + c.getName() + " for which no implementation is available.");
}
}
}
RepositorySet[] result = new RepositorySet[descriptors.size()];
/*
* Create the lists of repositories and topics, and create and register the sets with these.
*/
for (int i = 0; i < result.length; i++) {
RepositorySetDescriptor rsd = descriptors.get(i);
ObjectRepositoryImpl<?, ?>[] impls = new ObjectRepositoryImpl[rsd.m_objectRepositories.length];
String[] topics = new String[rsd.m_objectRepositories.length];
for (int j = 0; j < impls.length; j++) {
impls[j] = m_repositories.get(rsd.m_objectRepositories[j]);
topics[j] = impls[j].getTopicAll(false);
}
result[i] = loadRepositorySet(context.getUser(), rsd, impls);
result[i].registerHandler(m_context, m_sessionID, topics);
}
return result;
}
/**
* Helper method for login.
*/
private Preferences getRepositoryPrefs(Preferences userPrefs, URL location, String customer, String name) {
// Note: we can only use the getAuthority part of the URL for indexing, because the full URL will contain
// in the protocol part.
Preferences repoPref = userPrefs.node(location.getAuthority() + location.getPath());
Preferences customerPref = repoPref.node(customer);
return customerPref.node(name);
}
/**
* Helper method for login.
*
* @throws IOException
*/
private File getFileFromPreferences(Preferences repositoryPrefs, String type) throws IOException {
String sessionLocation = PREFS_LOCAL_FILE_LOCATION + m_sessionID;
String directory = repositoryPrefs.get(sessionLocation, "");
if (directory == "") {
File directoryFile = null;
try {
File bundleDataDir = m_context.getDataFile(PREFS_LOCAL_FILE_ROOT);
if (!bundleDataDir.isDirectory()) {
if (!bundleDataDir.mkdir()) {
throw new IOException("Error creating the local repository root directory.");
}
}
directoryFile = File.createTempFile("repo", "", bundleDataDir);
if (!directoryFile.delete()) {
throw new IOException("Cannot delete temporary file: " + directoryFile.getName());
}
}
catch (IOException e) {
// We cannot create or delete the temp file? Then something is seriously wrong, so rethrow.
throw e;
}
repositoryPrefs.put(sessionLocation, directoryFile.getName());
return new File(directoryFile + "-" + type);
}
else {
// Get the given file from that location.
return m_context.getDataFile(PREFS_LOCAL_FILE_ROOT + "/" + directory + "-" + type);
}
}
/**
* Helper method for login.
*
* @throws IOException
*/
private BackupRepository getBackupFromPreferences(Preferences repositoryPrefs) throws IOException {
File current = getFileFromPreferences(repositoryPrefs, PREFS_LOCAL_FILE_CURRENT);
File backup = getFileFromPreferences(repositoryPrefs, PREFS_LOCAL_FILE_BACKUP);
return new FilebasedBackupRepository(current, backup);
}
/**
* Helper method for login.
*
* @throws IOException
*/
private CachedRepository getCachedRepositoryFromPreferences(Repository repository, Preferences repositoryPrefs) throws IOException {
long mostRecentVersion = repositoryPrefs.getLong("version", CachedRepositoryImpl.UNCOMMITTED_VERSION);
return new CachedRepositoryImpl(repository, getBackupFromPreferences(repositoryPrefs), mostRecentVersion);
}
/**
* Helper method for login, which loads a set of repositories.
*
* @param user
* A <code>User</code> object
* @param rsd
* A RepositorySetDescriptor, defining the set to be created.
* @param repos
* An array of <code>ObjectRepositoryImpl</code> which this set should manage. Each
* @return The newly created repository set.
* @throws IOException
*/
public RepositorySet loadRepositorySet(User user, RepositorySetDescriptor rsd, ObjectRepositoryImpl<?, ?>[] repos) throws IOException {
Repository repo = new RemoteRepository(rsd.m_location, rsd.m_customer, rsd.m_name);
// Expose the repository itself as component so its dependencies get managed...
m_dm.add(m_dm.createComponent()
.setImplementation(repo)
.add(m_dm.createServiceDependency()
.setService(ConnectionFactory.class)
.setRequired(true)));
Preferences prefs = m_preferences.getUserPreferences(user.getName());
prefs = prefs.node(m_sessionID);
Preferences repoPrefs = getRepositoryPrefs(prefs, rsd.m_location, rsd.m_customer, rsd.m_name);
return new RepositorySet(m_changeNotifier, m_log, user, repoPrefs, repos, getCachedRepositoryFromPreferences(repo, repoPrefs), rsd.m_name, rsd.m_writeAccess);
}
private void unloadRepositorySet(User user) {
Preferences prefs = m_preferences.getUserPreferences(user.getName());
prefs.remove(m_sessionID);
}
public int getNumberWithWorkingState(Class<? extends RepositoryObject> clazz, WorkingState state) {
int result = 0;
synchronized (m_lock) {
for (RepositorySet set : m_repositorySets) {
result += set.getNumberWithWorkingState(clazz, state);
}
}
return result;
}
public WorkingState getWorkingState(RepositoryObject object) {
WorkingState result = null;
synchronized (m_lock) {
for (RepositorySet set : m_repositorySets) {
result = set.getWorkingState(object);
if (result != null) {
break;
}
}
}
return (result == null) ? WorkingState.Unchanged : result;
}
public void addArtifactHelper(ServiceReference<ArtifactHelper> ref, ArtifactHelper helper) {
String mimetype = (String) ref.getProperty(ArtifactHelper.KEY_MIMETYPE);
m_artifactRepositoryImpl.addHelper(mimetype, helper);
}
public synchronized void removeArtifactHelper(ServiceReference<ArtifactHelper> ref, ArtifactHelper helper) {
String mimetype = (String) ref.getProperty(ArtifactHelper.KEY_MIMETYPE);
m_artifactRepositoryImpl.removeHelper(mimetype, helper);
}
void addPreCommitMember(PreCommitMember member) {
synchronized (m_lock) {
m_preCommitMembers.add(member);
}
}
}