/*******************************************************************************
* Copyright (c) 2008 Cambridge Semantics Incorporated.
* 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
*
* File: $Source$
* Created by: Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>)
* Created on: Jul 17, 2008
* Revision: $Id$
*
* Contributors:
* Cambridge Semantics Incorporated - initial API and implementation
*******************************************************************************/
package org.openanzo.osgi;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.openanzo.osgi.attributes.CombusAttributes;
import org.openanzo.services.ServicesDictionary;
import org.osgi.framework.BundleContext;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.metatype.AttributeDefinition;
import org.osgi.service.metatype.ObjectClassDefinition;
/**
* OSGI configuration utilities
*
* @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com</a>)
*
*/
public class OsgiConfigurationUtils {
private final static Set<String> systemProperties = new HashSet<String>();
static {
systemProperties.add(ServicesDictionary.KEY_SERVICE_USER);
systemProperties.add(ServicesDictionary.KEY_SERVICE_PASSWORD);
systemProperties.add(CombusAttributes.Host.getID());
systemProperties.add(CombusAttributes.Port.getID());
}
/**
* Validate that the provided config properties are valid based on the given class def
*
* @param classDef
* classdef to verify config against
* @param configProperties
* config properties to verify
* @throws ConfigurationException
*/
public static void validateConfiguration(ObjectClassDefinition classDef, Dictionary<? extends Object, ? extends Object> configProperties) throws ConfigurationException {
for (AttributeDefinition ad : classDef.getAttributeDefinitions(ObjectClassDefinition.REQUIRED)) {
Object value = configProperties.get(ad.getID());
if (value == null) {
if (!systemProperties.contains(ad.getID())) {
throw new ConfigurationException(ad.getID(), classDef.getID() + "'s " + ad.getName() + " value cannot be null.");
}
} else {
String result = ad.validate(value.toString());
if (result != null && result.length() > 0) {
throw new ConfigurationException(ad.getID(), classDef.getID() + "'s " + ad.getName() + " not of valid form:" + result);
}
}
}
for (AttributeDefinition ad : classDef.getAttributeDefinitions(ObjectClassDefinition.OPTIONAL)) {
Object value = configProperties.get(ad.getID());
if (value != null) {
String result = ad.validate(value.toString());
if (result != null && result.length() > 0) {
throw new ConfigurationException(ad.getID(), classDef.getID() + "'s " + ad.getName() + " not of valid form:" + result);
}
}
}
}
/**
* Validate that the provided config properties are valid based on the given class def
*
* @param classDef
* classdef to verify config against
* @param oldConfigProperties
* config properties to verify
* @param newConfigProperties
* config properties to verify
* @return true if changing these properties requires a restart
* @throws ConfigurationException
*/
public static Set<String> isRestartRequired(ObjectClassDefinition classDef, Dictionary<? extends Object, ? extends Object> oldConfigProperties, Dictionary<? extends Object, ? extends Object> newConfigProperties) throws ConfigurationException {
HashMap<String, Object> oldProps = new HashMap<String, Object>();
for (Enumeration<? extends Object> keys = oldConfigProperties.keys(); keys.hasMoreElements();) {
Object key = keys.nextElement();
Object value = oldConfigProperties.get(key);
oldProps.put(key.toString(), value);
}
HashMap<String, Object> newProps = new HashMap<String, Object>();
for (Enumeration<? extends Object> keys = newConfigProperties.keys(); keys.hasMoreElements();) {
Object key = keys.nextElement();
Object value = newConfigProperties.get(key);
Object oldValue = oldProps.remove(key);
if (oldValue == null || !oldValue.equals(value)) {
newProps.put(key.toString(), value);
}
}
for (AttributeDefinition ad : classDef.getAttributeDefinitions(ObjectClassDefinition.ALL)) {
Object value = newProps.get(ad.getID());
if (value != null) {
if (ad instanceof AnzoAttributeDefinition) {
if (((AnzoAttributeDefinition) ad).isRestartRequired()) {
return null;
}
} else {
return null;
}
}
value = oldProps.get(ad.getID());
if (value != null) {
if (ad instanceof AnzoAttributeDefinition) {
if (((AnzoAttributeDefinition) ad).isRestartRequired()) {
return null;
}
} else {
return null;
}
}
}
HashSet<String> changedProps = new HashSet<String>();
changedProps.addAll(newProps.keySet());
changedProps.addAll(oldProps.keySet());
return changedProps;
}
/**
* Create a new array that is the union of the provided arrays
*
* @param arrays
* arrays to union together
* @return union of the provided arrays
*/
public static AttributeDefinition[] union(AttributeDefinition[]... arrays) {
if (arrays.length == 1)
return arrays[0];
AttributeDefinition[] result = null;
for (AttributeDefinition[] array : arrays) {
if (array.length > 0) {
AttributeDefinition[] temp = new AttributeDefinition[(result != null ? result.length : 0) + array.length];
if (result != null)
System.arraycopy(result, 0, temp, 0, result.length);
System.arraycopy(array, 0, temp, (result != null ? result.length : 0), array.length);
result = temp;
}
}
return result;
}
/**
* Update the config properties based on the current context. This means that templated values within the config will be updated, ie ${anzoHome}
*
* @param configProperties
* config properties to augment
* @param context
* context for which to use to update properties
*/
@SuppressWarnings("unchecked")
public static void updateConfigProperties(Dictionary configProperties, BundleContext context) {
for (Enumeration<? extends Object> keys = configProperties.keys(); keys.hasMoreElements();) {
Object key = keys.nextElement();
Object value = configProperties.get(key);
if (value instanceof String) {
value = preprocessString((String) value, context);
configProperties.put(key, value);
}
}
}
/**
* Replace templated value with computed value based on context
*
* <br/>
* <code> ${bundleLocation}</code> <br/>
* <code> ${bundleResourcePrefix}</code>
*
* @param value
* value to augment
* @param context
* context for which to use to update value
* @return augmented value
*/
public static String preprocessString(String value, BundleContext context) {
String result = value;
while ((result).contains("${")) {
int index = (result).indexOf("${");
String val = (result).substring(0, index);
int endIndex = (result).indexOf("}", index);
if (endIndex < 0)
endIndex = (result).length();
String replacement = (result).substring(index + 2, endIndex);
if (replacement.equals("bundleLocation")) {
val = val.concat(context.getDataFile(".").getAbsolutePath());
} else if (replacement.equals("bundleResourcePrefix")) {
String bp = context.getProperty("BUNDLE_RESOURCE_PREFIX");
if (bp != null) {
val = val.concat(bp);
}
} else if (replacement.startsWith("system.")) {
String prop = replacement.substring("system.".length());
if (System.getProperty(prop) != null) {
val = val.concat(System.getProperty(prop));
} else if (System.getenv(prop) != null) {
val = val.concat(System.getenv(prop));
} else if (prop.equals("ANZO_HOME")) {
val = ".";
} else if (prop.equals("ANZO_SERVER_HOME")) {
val = ".";
}
}
if (endIndex < (result).length()) {
val = val.concat((result).substring(endIndex + 1));
}
result = val;
}
return result;
}
/**
* The minimum number of server port number.
*/
public static final int MIN_PORT_NUMBER = 1;
/**
* The maximum number of server port number.
*/
public static final int MAX_PORT_NUMBER = 65536;
/**
* Checks to see if a specific port is available.
*
* @param port
* the port to check for availability
*/
public static boolean networkPortAvailable(int port, InetAddress address) {
if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
throw new IllegalArgumentException("Invalid start port: " + port);
}
ServerSocket ss = null;
DatagramSocket ds = null;
try {
ss = new ServerSocket(port, 0, address);
ss.setReuseAddress(true);
ds = new DatagramSocket(port, address);
ds.setReuseAddress(true);
return true;
} catch (IOException e) {
} finally {
if (ds != null) {
ds.close();
}
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
/* should not be thrown */
}
}
}
return false;
}
}