blob: 97a73ce6353565cbab2aaa6cb3fdcb69941a756e [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.repository.impl;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import org.apache.ace.repository.Repository;
import org.apache.ace.repository.RepositoryReplication;
import org.apache.ace.repository.impl.constants.RepositoryConstants;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.service.Service;
import org.osgi.framework.BundleContext;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedServiceFactory;
import org.osgi.service.log.LogService;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;
import org.osgi.service.prefs.PreferencesService;
/**
* A <code>ManagedServiceFactory</code> responsible for creating a (<code>Replication</code>)<code>Repository</code>
* instance for each valid configuration that is received from the <code>ConfigurationAdmin</code>.
*/
public class RepositoryFactory implements ManagedServiceFactory {
private volatile LogService m_log; /* injected by dependency manager */
private volatile BundleContext m_context; /* injected by dependency manager */
private volatile PreferencesService m_prefsService; /* injected by dependency manager */
private File m_tempDir;
private File m_baseDir;
private Preferences m_prefs;
private final DependencyManager m_manager;
private final Map<String, Service> m_instances = new HashMap<String, Service>();
public RepositoryFactory(DependencyManager manager) {
m_manager = manager;
}
public synchronized void deleted(String pid) {
// pull service
Service service = m_instances.remove(pid);
if (service != null) {
m_manager.remove(service);
}
// remove persisted data
File dir = new File(m_baseDir, pid);
if (dir.isDirectory()) {
File[] files = dir.listFiles();
for (int i = 0; (files != null) && (i < files.length); i++) {
files[i].delete();
}
if (!dir.delete()) {
m_log.log(LogService.LOG_WARNING, "Unable to clean up files ( in " + dir.getAbsolutePath() + ") after removing repository");
}
}
try {
m_prefs.node(pid).removeNode();
m_prefs.sync();
}
catch (BackingStoreException e) {
// Not much we can do
}
}
public String getName() {
return "RepositoryFactory";
}
public synchronized void init() {
m_tempDir = m_context.getDataFile("tmp");
if ((m_tempDir != null) && !m_tempDir.isDirectory() && !m_tempDir.mkdirs()) {
throw new IllegalArgumentException("Unable to create temp directory (" + m_tempDir.getAbsolutePath() + ")");
}
m_baseDir = m_context.getDataFile("repos");
if ((m_baseDir != null) && !m_baseDir.isDirectory() && !m_baseDir.mkdirs()) {
throw new IllegalArgumentException("Unable to create base directory (" + m_baseDir.getAbsolutePath() + ")");
}
}
/**
* Creates a new instance if the supplied dictionary contains a valid configuration. A configuration is valid if
* <code>RepositoryConstants.REPOSITORY_NAME</code> and <code>RepositoryConstants.REPOSITORY_CUSTOMER</code> properties
* are present, not empty and the combination of the two is unique in respect to other previously created instances.
* Finally a property <code>RepositoryConstants.REPOSITORY_MASTER</code> should be present and be either <code>true</code>
* or <code>false</code>.
*
* @param pid A unique identifier for the instance, generated by <code>ConfigurationAdmin</code> normally.
* @param dict The configuration properties for the instance, see description above.
* @throws ConfigurationException If any of the above explanation fails <b>or</b>when there is an internal error creating the repository.
*/
@SuppressWarnings("unchecked")
public synchronized void updated(String pid, Dictionary dict) throws ConfigurationException {
String name = (String) dict.get(RepositoryConstants.REPOSITORY_NAME);
if ((name == null) || "".equals(name)) {
throw new ConfigurationException(RepositoryConstants.REPOSITORY_NAME, "Repository name has to be specified.");
}
String customer = (String) dict.get(RepositoryConstants.REPOSITORY_CUSTOMER);
if ((customer == null) || "".equals(customer)) {
throw new ConfigurationException(RepositoryConstants.REPOSITORY_CUSTOMER, "Repository customer has to be specified.");
}
String master = (String) dict.get(RepositoryConstants.REPOSITORY_MASTER);
if (!("false".equalsIgnoreCase(master.trim()) || "true".equalsIgnoreCase(master.trim()))) {
throw new ConfigurationException(RepositoryConstants.REPOSITORY_MASTER, "Have to specify whether the repository is the master or a slave.");
}
boolean isMaster = Boolean.parseBoolean(master);
String initialContents = (String) dict.get(RepositoryConstants.REPOSITORY_INITIAL_CONTENT);
if (m_prefs == null) {
m_prefs = m_prefsService.getSystemPreferences();
}
String[] nodes;
try {
nodes = m_prefs.childrenNames();
}
catch (BackingStoreException e) {
throw new ConfigurationException("none", "Internal error while validating configuration.");
}
for (int i = 0; i < nodes.length; i++) {
Preferences node = m_prefs.node(nodes[i]);
if (name.equalsIgnoreCase(node.get(RepositoryConstants.REPOSITORY_NAME, "")) && name.equalsIgnoreCase(node.get(RepositoryConstants.REPOSITORY_CUSTOMER, ""))) {
throw new ConfigurationException("name and customer", "Name and customer combination already exists");
}
}
Preferences node = m_prefs.node(pid);
node.put(RepositoryConstants.REPOSITORY_NAME, name);
node.put(RepositoryConstants.REPOSITORY_CUSTOMER, customer);
Service service = m_instances.get(pid);
if (service == null) {
// new instance
File dir = new File(m_baseDir, pid);
RepositoryImpl store = new RepositoryImpl(dir, m_tempDir, isMaster);
if ((initialContents != null) && isMaster) {
try {
store.commit(new ByteArrayInputStream(initialContents.getBytes()), 0);
}
catch (IOException e) {
m_log.log(LogService.LOG_ERROR, "Unable to set initial contents of the repository.", e);
}
}
service = m_manager.createComponent()
.setInterface(new String[] {RepositoryReplication.class.getName(), Repository.class.getName()}, dict)
.setImplementation(store)
.add(m_manager.createServiceDependency().setService(LogService.class).setRequired(false));
m_manager.add(service);
m_instances.put(pid, service);
} else {
// update existing instance
RepositoryImpl store = (RepositoryImpl) service.getService();
store.updated(isMaster);
}
}
}