blob: 2c125ff38048802c418068b03cdd55302d1f0d12 [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.automation;
import java.io.IOException;
import java.net.URL;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import org.amdatu.scheduling.Job;
import org.amdatu.scheduling.constants.Constants;
import org.apache.ace.client.repository.RepositoryAdmin;
import org.apache.ace.client.repository.RepositoryAdminLoginContext;
import org.apache.ace.client.repository.object.TargetObject;
import org.apache.ace.client.repository.stateful.StatefulTargetObject;
import org.apache.ace.client.repository.stateful.StatefulTargetRepository;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.log.LogService;
import org.osgi.service.useradmin.User;
import org.osgi.service.useradmin.UserAdmin;
/**
* Automatic target operator, when configured will automatically register, approve, auto-approve and commit targets to
* the repository. An LDAP filter can be used to filter for the correct targets.
*/
public class AutoTargetOperator implements ManagedService {
public static final String PID = "org.apache.ace.client.automation";
public static final String SCHEDULER_NAME = "org.apache.ace.client.processauditlog";
private volatile StatefulTargetRepository m_statefulTargetRepos;
private volatile RepositoryAdmin m_reposAdmin;
private volatile UserAdmin m_userAdmin;
private volatile BundleContext m_bundleContext;
private volatile LogService m_log;
private volatile Dictionary<String, ?> m_settings;
private volatile ServiceRegistration<Job> m_serviceReg;
// used for processing the auditlog (tell the repository about that)
private final AuditLogProcessTask m_task = new AuditLogProcessTask();
public void start() {
// get user
User user = m_userAdmin.getUser("username", getConfigValue(ConfigItem.USERNAME));
// login at Repository admin
try {
URL url = new URL(getConfigValue(ConfigItem.HOSTNAME) + getConfigValue(ConfigItem.ENDPOINT));
String customerName = getConfigValue(ConfigItem.CUSTOMER_NAME);
RepositoryAdminLoginContext loginContext = m_reposAdmin.createLoginContext(user);
loginContext
.add(loginContext.createShopRepositoryContext()
.setLocation(url).setCustomer(customerName).setName(getConfigValue(ConfigItem.STORE_REPOSITORY)))
.add(loginContext.createTargetRepositoryContext()
.setLocation(url).setCustomer(customerName).setName(getConfigValue(ConfigItem.TARGET_REPOSITORY)).setWriteable())
.add(loginContext.createDeploymentRepositoryContext()
.setLocation(url).setCustomer(customerName).setName(getConfigValue(ConfigItem.DEPLOYMENT_REPOSITORY)).setWriteable());
m_reposAdmin.login(loginContext);
// start refresh task
Dictionary<String, Object> props = new Hashtable<>();
props.put("name", SCHEDULER_NAME);
m_serviceReg = m_bundleContext.registerService(Job.class, m_task, props);
}
catch (IOException ioe) {
m_log.log(LogService.LOG_ERROR, "Unable to login at repository admin.", ioe);
}
}
public void stop() {
// service present, pull it
if (m_serviceReg != null) {
((ServiceRegistration<?>) m_serviceReg).unregister();
}
m_serviceReg = null;
// logout
try {
m_reposAdmin.logout(true);
}
catch (IOException ioe) {
// not a lot we can
System.err.println(ioe.getMessage());
}
}
/**
* Runnable that will synchronize audit log data with the server and tell the repository about the changes if
* applicable.
*/
private final class AuditLogProcessTask implements Job {
private final Object m_lock = new Object();
@Override
public void execute() {
// perform synchronous model actions
synchronized (m_lock) {
m_statefulTargetRepos.refresh();
boolean changed = false;
try {
checkoutModel();
changed |= registerTargets();
changed |= approveTargets();
changed |= setAutoApprove();
}
catch (IOException ioe) {
m_log.log(LogService.LOG_WARNING, "Checkout of model failed.", ioe);
}
catch (InvalidSyntaxException ise) {
m_log.log(LogService.LOG_WARNING, "Illegal register target filter.", ise);
}
// Commit any changes
try {
if (changed) {
m_reposAdmin.commit();
}
}
catch (IOException ioe) {
m_log.log(LogService.LOG_WARNING, "Commit of model failed", ioe);
}
}
}
}
private void checkoutModel() throws IOException {
// Do a checkout
if (!m_reposAdmin.isCurrent()) {
m_reposAdmin.checkout();
}
}
private boolean registerTargets() throws InvalidSyntaxException {
boolean changed = false;
String filter = "(&" + getConfigValue(ConfigItem.REGISTER_TARGET_FILTER) +
"(" + StatefulTargetObject.KEY_REGISTRATION_STATE + "=" + StatefulTargetObject.RegistrationState.Unregistered + "))";
List<StatefulTargetObject> stos = m_statefulTargetRepos.get(m_bundleContext.createFilter(filter));
for (StatefulTargetObject sto : stos) {
sto.register();
changed = true;
}
return changed;
}
private boolean setAutoApprove() throws InvalidSyntaxException {
boolean changed = false;
String filter = "(&" + getConfigValue(ConfigItem.AUTO_APPROVE_TARGET_FILTER) +
"(" + StatefulTargetObject.KEY_REGISTRATION_STATE + "=" + StatefulTargetObject.RegistrationState.Registered + ")" +
"(!(" + TargetObject.KEY_AUTO_APPROVE + "=true)))";
List<StatefulTargetObject> stos = m_statefulTargetRepos.get(m_bundleContext.createFilter(filter));
for (StatefulTargetObject sto : stos) {
sto.setAutoApprove(true);
changed = true;
}
return changed;
}
private boolean approveTargets() throws InvalidSyntaxException {
boolean changed = false;
String filter = "(&" + getConfigValue(ConfigItem.APPROVE_TARGET_FILTER) +
"(" + StatefulTargetObject.KEY_STORE_STATE + "=" + StatefulTargetObject.StoreState.Unapproved + "))";
List<StatefulTargetObject> stos = m_statefulTargetRepos.get(m_bundleContext.createFilter(filter));
for (StatefulTargetObject sto : stos) {
sto.approve();
changed = true;
}
return changed;
}
public void updated(Dictionary<String, ?> settings) throws ConfigurationException {
if (settings != null) {
for (ConfigItem item : ConfigItem.values()) {
String value = (String) settings.get(item.toString());
if ((value == null) || "".equals(value.trim())) {
throw new ConfigurationException(item.toString(), item.getErrorText());
}
}
// store configuration
m_settings = settings;
Long interval = null;
Object value = settings.get("interval");
if (value != null) {
try {
interval = Long.valueOf(value.toString());
}catch (NumberFormatException e) {
throw new ConfigurationException("interval", "Interval must be a valid Long value", e);
}
Dictionary<String, Object> serviceProps = new Hashtable<>();
serviceProps.put("name", SCHEDULER_NAME);
if (interval == null) {
serviceProps.remove(Constants.REPEAT_FOREVER);
serviceProps.remove(Constants.REPEAT_INTERVAL_PERIOD);
serviceProps.remove(Constants.REPEAT_INTERVAL_VALUE);
} else {
serviceProps.put(Constants.REPEAT_FOREVER, true);
serviceProps.put(Constants.REPEAT_INTERVAL_PERIOD, "milisecond");
serviceProps.put(Constants.REPEAT_INTERVAL_VALUE, interval);
}
m_serviceReg.setProperties(serviceProps);
}
}
}
/**
* @param item
* The configuration item (enum)
* @return The value stored in the configuration dictionary.
*/
private String getConfigValue(ConfigItem item) {
return (String) m_settings.get(item.toString());
}
/**
* Helper class used for target automation client configuration. ENUM (itemname, errormessage, filter true/false)
*
*/
private enum ConfigItem {
REGISTER_TARGET_FILTER("registerTargetFilter", "Register target filter missing", true),
APPROVE_TARGET_FILTER("approveTargetFilter", "Approve target filter missing", true),
AUTO_APPROVE_TARGET_FILTER("autoApproveTargetFilter", "Auto approve config value missing", true),
COMMIT_REPO("commitRepositories", "Commit value missing.", false),
TARGET_REPOSITORY("targetRepository", "TargetRepository id missing.", false),
DEPLOYMENT_REPOSITORY("deploymentRepository", "DeploymentRepository id missing.", false),
STORE_REPOSITORY("storeRepository", "Store Repository id missing.", false),
CUSTOMER_NAME("customerName", "Customer name missing", false),
HOSTNAME("hostName", "Hostname missing.", false),
ENDPOINT("endpoint", "Endpoint missing in config.", false),
USERNAME("userName", "UserName missing.", false);
private final String m_name;
private final String m_errorText;
private final boolean m_isFilter;
private ConfigItem(String name, String errorText, boolean isFilter) {
m_name = name;
m_errorText = errorText;
m_isFilter = isFilter;
}
@Override
public String toString() {
return m_name;
}
public String getErrorText() {
return m_errorText;
}
@SuppressWarnings("unused")
public boolean isFilter() {
return m_isFilter;
}
}
}