blob: afa72930ce8d848c5625993fbb31af3a276b48a5 [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.sling.installer.factories.configuration.impl;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import org.apache.sling.installer.api.InstallableResource;
import org.apache.sling.installer.api.ResourceChangeListener;
import org.apache.sling.installer.api.tasks.ChangeStateTask;
import org.apache.sling.installer.api.tasks.InstallTask;
import org.apache.sling.installer.api.tasks.InstallTaskFactory;
import org.apache.sling.installer.api.tasks.RegisteredResource;
import org.apache.sling.installer.api.tasks.ResourceState;
import org.apache.sling.installer.api.tasks.ResourceTransformer;
import org.apache.sling.installer.api.tasks.TaskResource;
import org.apache.sling.installer.api.tasks.TaskResourceGroup;
import org.apache.sling.installer.api.tasks.TransformationResult;
import org.apache.sling.installer.factories.configuration.ConfigurationConstants;
import org.osgi.framework.Constants;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.ConfigurationEvent;
import org.osgi.service.cm.ConfigurationListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Task creator for configurations.
*/
public class ConfigTaskCreator
implements InstallTaskFactory, ConfigurationListener, ResourceTransformer {
/** Logger. */
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/** Configuration admin. */
private final ConfigurationAdmin configAdmin;
/** Resource change listener. */
private final ResourceChangeListener changeListener;
public ConfigTaskCreator(final ResourceChangeListener listener, final ConfigurationAdmin configAdmin) {
this.changeListener = listener;
this.configAdmin = configAdmin;
}
/**
* Create a task to install or uninstall a configuration.
*
* @see org.apache.sling.installer.api.tasks.InstallTaskFactory#createTask(org.apache.sling.installer.api.tasks.TaskResourceGroup)
*/
@Override
public InstallTask createTask(final TaskResourceGroup group) {
final TaskResource toActivate = group.getActiveResource();
if ( !toActivate.getType().equals(InstallableResource.TYPE_CONFIG) ) {
return null;
}
final InstallTask result;
if (toActivate.getState() == ResourceState.UNINSTALL) {
// if this is an uninstall, check if we have to install an older version
// in this case we should do an update instead of uninstall/install (!)
final TaskResource second = group.getNextActiveResource();
if ( second != null
&& ( second.getState() == ResourceState.IGNORED || second.getState() == ResourceState.INSTALLED || second.getState() == ResourceState.INSTALL )
&& ( second.getDictionary() == null || second.getDictionary().get(InstallableResource.RESOURCE_IS_TEMPLATE) == null)) {
result = new ChangeStateTask(group, ResourceState.UNINSTALLED);
} else {
result = new ConfigRemoveTask(group, this.configAdmin);
}
} else {
result = new ConfigInstallTask(group, this.configAdmin);
}
return result;
}
/**
* @see org.osgi.service.cm.ConfigurationListener#configurationEvent(org.osgi.service.cm.ConfigurationEvent)
*/
@Override
public void configurationEvent(final ConfigurationEvent event) {
synchronized ( Coordinator.SHARED ) {
final String id;
final String pid;
if (event.getFactoryPid() == null ) {
id = event.getPid();
pid = id;
} else {
pid = (event.getPid().startsWith(event.getFactoryPid() + '.') ?
event.getPid().substring(event.getFactoryPid().length() + 1) : event.getPid());
id = event.getFactoryPid() + '.' + event.getPid();
}
if ( event.getType() == ConfigurationEvent.CM_DELETED ) {
final Coordinator.Operation op = Coordinator.SHARED.get(event.getPid(), event.getFactoryPid(), true);
if ( op == null ) {
this.changeListener.resourceRemoved(InstallableResource.TYPE_CONFIG, id);
} else {
this.logger.debug("Ignoring configuration event for {}:{}", event.getPid(), event.getFactoryPid());
}
} else if ( event.getType() == ConfigurationEvent.CM_UPDATED ) {
try {
final Configuration config = ConfigUtil.getConfiguration(configAdmin,
event.getFactoryPid(),
event.getPid());
final Coordinator.Operation op = Coordinator.SHARED.get(event.getPid(), event.getFactoryPid(), false);
if ( config != null && op == null ) {
final boolean persist = ConfigUtil.toBoolean(config.getProperties().get(ConfigurationConstants.PROPERTY_PERSISTENCE), true);
final Dictionary<String, Object> dict = ConfigUtil.cleanConfiguration(config.getProperties());
final Map<String, Object> attrs = new HashMap<String, Object>();
if ( !persist ) {
attrs.put(ResourceChangeListener.RESOURCE_PERSIST, Boolean.FALSE);
}
attrs.put(Constants.SERVICE_PID, event.getPid());
if ( event.getFactoryPid() == null ) {
attrs.put(InstallableResource.RESOURCE_URI_HINT, pid);
} else {
attrs.put(InstallableResource.RESOURCE_URI_HINT, event.getFactoryPid() + '-' + pid);
}
if ( config.getBundleLocation() != null ) {
attrs.put(InstallableResource.INSTALLATION_HINT, config.getBundleLocation());
}
// Factory?
if (event.getFactoryPid() != null) {
attrs.put(ConfigurationAdmin.SERVICE_FACTORYPID, event.getFactoryPid());
}
this.changeListener.resourceAddedOrUpdated(InstallableResource.TYPE_CONFIG, id, null, dict, attrs);
} else {
this.logger.debug("Ignoring configuration event for {}:{}", event.getPid(), event.getFactoryPid());
}
} catch ( final Exception ignore) {
// ignore for now
}
}
}
}
/**
* @see org.apache.sling.installer.api.tasks.ResourceTransformer#transform(org.apache.sling.installer.api.tasks.RegisteredResource)
*/
@Override
public TransformationResult[] transform(final RegisteredResource resource) {
if ( resource.getType().equals(InstallableResource.TYPE_PROPERTIES) ) {
return checkConfiguration(resource);
}
return null;
}
/**
* Check if the registered resource is a configuration
* @param resource The resource
*/
private TransformationResult[] checkConfiguration(final RegisteredResource resource) {
final String url = separatorsToUnix(resource.getURL());
String lastIdPart = url;
final int pos = lastIdPart.lastIndexOf('/');
if ( pos != -1 ) {
lastIdPart = lastIdPart.substring(pos + 1);
}
final String pid;
// remove extension if known
if ( isConfigExtension(getExtension(lastIdPart)) ) {
final int lastDot = lastIdPart.lastIndexOf('.');
pid = lastIdPart.substring(0, lastDot);
} else {
pid = lastIdPart;
}
// split pid and factory pid alias
final String factoryPid;
final String configPid;
int n = pid.indexOf('-');
if (n > 0) {
// quick check if this is an existing configuration
final String fString = pid.substring(0, n);
final String cString = pid.substring(n + 1);
boolean useExtendedPid = false;
try {
if ( ConfigUtil.getConfiguration(this.configAdmin, fString, fString + '.' + cString) != null ) {
useExtendedPid = true;
}
} catch ( final Exception ignore) {
// ignore this
}
if ( useExtendedPid ) {
configPid = fString + '.' + cString;
} else {
configPid = pid.substring(n + 1);
}
factoryPid = pid.substring(0, n);
} else {
factoryPid = null;
configPid = pid;
}
final Map<String, Object> attr = new HashMap<String, Object>();
attr.put(Constants.SERVICE_PID, configPid);
// Factory?
if (factoryPid != null) {
attr.put(ConfigurationAdmin.SERVICE_FACTORYPID, factoryPid);
}
final TransformationResult tr = new TransformationResult();
final String id = (factoryPid == null ? "" : factoryPid + ".") + configPid;
tr.setId(id);
tr.setResourceType(InstallableResource.TYPE_CONFIG);
tr.setAttributes(attr);
return new TransformationResult[] {tr};
}
/**
* Compute the extension
*/
private static String getExtension(String url) {
final int pos = url.lastIndexOf('.');
return (pos < 0 ? "" : url.substring(pos+1));
}
private static boolean isConfigExtension(String extension) {
if ( extension.equals("cfg")
|| extension.equals("config")
|| extension.equals("xml")
|| extension.equals("properties")) {
return true;
}
return false;
}
/**
* Converts all separators to the Unix separator of forward slash.
*
* @param path the path to be changed, null ignored
* @return the updated path
*/
private static String separatorsToUnix(String path) {
if (path == null || path.indexOf('\\') == -1) {
return path;
}
return path.replace('\\', '/');
}
}