/******************************************************************************
* Copyright (c) 2006, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
* is available at http://www.opensource.org/licenses/apache2.0.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* VMware Inc.
*****************************************************************************/
package org.eclipse.gemini.blueprint.util;
import java.util.Collection;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Utility class for creating OSGi filters. This class allows filter creation and concatenation from common parameters
* such as class names.
*
* @author Costin Leau
*/
public abstract class OsgiFilterUtils {
private static final char FILTER_BEGIN = '(';
private static final char FILTER_END = ')';
private static final String FILTER_AND_CONSTRAINT = "(&";
private static final String EQUALS = "=";
/**
* Adds the given class as an 'and'(&) {@link Constants#OBJECTCLASS} constraint to the given filter. At least
* one parameter must be valid (non-<code>null</code>).
*
* @param clazz class name (can be <code>null</code>)
* @param filter valid OSGi filter (can be <code>null</code>)
* @return OSGi filter containing the {@link Constants#OBJECTCLASS} constraint and the given filter
*/
public static String unifyFilter(String clazz, String filter) {
return unifyFilter(new String[] { clazz }, filter);
}
/**
* Adds the given class to the given filter. At least one parameter must be valid (non-<code>null</code>).
*
* @param clazz fully qualified class name (can be <code>null</code>)
* @param filter valid OSGi filter (can be <code>null</code>)
* @return an OSGi filter concatenating the given parameters
* @see #unifyFilter(String, String)
*/
public static String unifyFilter(Class<?> clazz, String filter) {
if (clazz != null)
return unifyFilter(clazz.getName(), filter);
return unifyFilter((String) null, filter);
}
/**
* Adds the given classes to the given filter. At least one parameter must be valid (non-<code>null</code>).
*
* @param classes array of fully qualified class names (can be <code>null</code>/empty)
* @param filter valid OSGi filter (can be <code>null</code>)
* @return an OSGi filter concatenating the given parameters
* @see #unifyFilter(String[], String)
*/
public static String unifyFilter(Class<?>[] classes, String filter) {
if (ObjectUtils.isEmpty(classes))
return unifyFilter(new String[0], filter);
String classNames[] = new String[classes.length];
for (int i = 0; i < classNames.length; i++) {
if (classes[i] != null)
classNames[i] = classes[i].getName();
}
return unifyFilter(classNames, filter);
}
/**
* Adds the given classes as an 'and'(&) {@link Constants#OBJECTCLASS} constraint to the given filter. At least
* one parameter must be valid (non-<code>null</code>).
*
* @param classes array of fully qualified class names (can be <code>null</code>/empty)
* @param filter valid OSGi filter (can be <code>null</code>)
* @return an OSGi filter concatenating the given parameters
*/
public static String unifyFilter(String[] classes, String filter) {
return unifyFilter(Constants.OBJECTCLASS, classes, filter);
}
/**
* Concatenates the given strings with an 'and'(&) constraint under the given key to the given filter. At least
* one of the items/filter parameters must be valid (non-<code>null</code>).
*
* @param key the key under which the items are being concatenated (required)
* @param items an array of strings concatenated to the existing filter
* @param filter valid OSGi filter (can be <code>null</code>)
* @return an OSGi filter concatenating the given parameters
*/
public static String unifyFilter(String key, String[] items, String filter) {
boolean filterHasText = StringUtils.hasText(filter);
if (items == null)
items = new String[0];
// number of valid (not-null) classes
int itemName = items.length;
for (int i = 0; i < items.length; i++) {
if (items[i] == null)
itemName--;
}
if (itemName == 0)
// just return the filter
if (filterHasText)
return filter;
else
throw new IllegalArgumentException("at least one parameter has to be not-null");
Assert.hasText(key, "key is required");
// do a simple filter check - starts with ( and ends with )
if (filterHasText && !(filter.charAt(0) == FILTER_BEGIN && filter.charAt(filter.length() - 1) == FILTER_END)) {
throw new IllegalArgumentException("invalid filter: " + filter);
}
// the item will be added in a sub-filter which does searching only
// after the key. For classes these will look like:
//
// i.e.
// (&(objectClass=java.lang.Object)(objectClass=java.lang.Cloneable))
//
// this sub filter will be added with a & constraint to the given filter
// if
// that one exists
// i.e. (&(&(objectClass=MegaObject)(objectClass=SuperObject))(<given
// filter>))
StringBuilder buffer = new StringBuilder();
// a. big & constraint
// (&
if (filterHasText)
buffer.append(FILTER_AND_CONSTRAINT);
boolean moreThenOneClass = itemName > 1;
// b. create key sub filter (only if we have more then one class
// (&(&
if (moreThenOneClass) {
buffer.append(FILTER_AND_CONSTRAINT);
}
// parse the classes and add the item name under the given key
for (int i = 0; i < items.length; i++) {
if (items[i] != null) {
// (objectClass=
buffer.append(FILTER_BEGIN);
buffer.append(key);
buffer.append(EQUALS);
// <actual value>
buffer.append(items[i]);
// )
buffer.append(FILTER_END);
}
}
// c. close the classes sub filter
// )
if (moreThenOneClass) {
buffer.append(FILTER_END);
}
// d. add the rest of the filter
if (filterHasText) {
buffer.append(filter);
// e. close the big filter
buffer.append(FILTER_END);
}
return buffer.toString();
}
/**
* Validates the given String as a OSGi filter.
*
* @param filter OSGi filter
* @return true if the filter is valid, false otherwise
*/
public static boolean isValidFilter(String filter) {
try {
createFilter(filter);
return true;
} catch (IllegalArgumentException ex) {
return false;
}
}
/**
* Creates an OSGi {@link Filter} from the given String. Translates the {@link InvalidSyntaxException} checked
* exception into an unchecked {@link IllegalArgumentException}.
*
* @param filter OSGi filter given as a String
* @return OSGi filter (as <code>Filter</code>)
*/
public static Filter createFilter(String filter) {
Assert.hasText(filter, "invalid filter");
try {
return FrameworkUtil.createFilter(filter);
} catch (InvalidSyntaxException ise) {
throw (RuntimeException) new IllegalArgumentException("invalid filter: " + ise.getFilter()).initCause(ise);
}
}
/**
* Creates a filter (as String) that matches the properties (expect the service id) of service reference.
*
* @param reference
* @return
*/
public static String getFilter(ServiceReference reference) {
String[] propertyKeys = reference.getPropertyKeys();
// allocate some space based on the array length
StringBuilder sb = new StringBuilder(propertyKeys.length << 3);
sb.append("(&");
for (String key : propertyKeys) {
if (!Constants.SERVICE_ID.equals(key)) {
Object value = reference.getProperty(key);
Class<?> cl = value.getClass();
Iterable it;
// array
if (cl.isArray()) {
Object[] array = ObjectUtils.toObjectArray(value);
for (Object item : array) {
sb.append("(");
sb.append(key);
sb.append("=");
sb.append(item);
sb.append(")");
}
}
// collection
else if (Collection.class.isAssignableFrom(cl)) {
Collection<?> c = (Collection) value;
for (Object item : c) {
sb.append("(");
sb.append(key);
sb.append("=");
sb.append(item);
sb.append(")");
}
}
// scalar/primitive
else {
sb.append("(");
sb.append(key);
sb.append("=");
sb.append(value);
sb.append(")");
}
}
}
sb.append(")");
return sb.toString();
}
}