Added: felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java?rev=1451167&view=auto ============================================================================== --- felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java (added) +++ felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java Thu Feb 28 10:27:21 2013 @@ -0,0 +1,315 @@ +/* + * 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.felix.ipojo.handler.temporal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.List; + +import org.apache.felix.ipojo.ConfigurationException; +import org.apache.felix.ipojo.PrimitiveHandler; +import org.apache.felix.ipojo.metadata.Element; +import org.apache.felix.ipojo.parser.FieldMetadata; +import org.apache.felix.ipojo.parser.MethodMetadata; +import org.apache.felix.ipojo.parser.PojoMetadata; +import org.apache.felix.ipojo.util.DependencyModel; +import org.apache.felix.ipojo.util.DependencyStateListener; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; + +/** +* Temporal dependency handler. +* A temporal dependency waits (block) for the availability of the service. +* If no provider arrives in the specified among of time, a runtime exception is thrown. +* @author <a href="mailto:[email protected]">Felix Project Team</a> +*/ +public class TemporalHandler extends PrimitiveHandler implements DependencyStateListener { + + /** + * Default timeout if not specified. + */ + public static final int DEFAULT_TIMEOUT = 3000; + + /** + * No policy. + */ + public static final int NO_POLICY = 0; + /** + * Uses a nullable object. + */ + public static final int NULLABLE = 1; + /** + * Uses a default-implementation object. + */ + public static final int DEFAULT_IMPLEMENTATION = 2; + /** + * Uses an empty array. + */ + public static final int EMPTY = 3; + /** + * Uses {@code null}. + */ + public static final int NULL = 4; + + /** + * The handler namespace. + */ + public static final String NAMESPACE = "org.apache.felix.ipojo.handler.temporal"; + + /** + * The list of managed dependencies. + */ + private List/*<deps>*/ m_dependencies = new ArrayList(1); + + /** + * Start method. Starts managed dependencies. + * @see org.apache.felix.ipojo.Handler#start() + */ + public void start() { + for (int i = 0; i < m_dependencies.size(); i++) { + ((TemporalDependency) m_dependencies.get(i)).start(); + } + } + + /** + * Stop method. Stops managed dependencies. + * @see org.apache.felix.ipojo.Handler#stop() + */ + public void stop() { + for (int i = 0; i < m_dependencies.size(); i++) { + ((TemporalDependency) m_dependencies.get(i)).stop(); + } + m_dependencies.clear(); + } + + /** + * Configure method. Creates managed dependencies. + * @param meta the component type metadata. + * @param dictionary the instance configuration. + * @throws ConfigurationException if the dependency is not configured correctly + * @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.metadata.Element, java.util.Dictionary) + */ + public void configure(Element meta, Dictionary dictionary) throws ConfigurationException { + PojoMetadata manipulation = getFactory().getPojoMetadata(); + Element[] deps = meta.getElements("requires", NAMESPACE); + + // Also check with temporal is no requires. + if (deps == null || deps.length == 0) { + deps = meta.getElements("temporal", NAMESPACE); + } + + // Get instance filters. + Dictionary filtersConfiguration = getRequiresFilters(dictionary.get("temporal.filters")); + if(filtersConfiguration == null || filtersConfiguration.isEmpty()) { + // Fall back on the Requires handler configuration, if any + filtersConfiguration = getRequiresFilters(dictionary.get("requires.filters")); + } + // Get from filters if any. + Dictionary fromConfiguration = getRequiresFilters(dictionary.get("temporal.from")); + if(fromConfiguration == null || fromConfiguration.isEmpty()) { + // Fall back on the Requires handler configuration, if any + fromConfiguration = getRequiresFilters(dictionary.get("requires.from")); + } + + + for (int i = 0; i < deps.length; i++) { + if (!deps[i].containsAttribute("field") || m_dependencies.contains(deps[i].getAttribute("field"))) { + error("One temporal dependency must be attached to a field or the field is already used"); + return; + } + String field = deps[i].getAttribute("field"); + + String id = field; + if (deps[i].containsAttribute("id")) { + id = deps[i].getAttribute("id"); + } + + FieldMetadata fieldmeta = manipulation.getField(field); + if (fieldmeta == null) { + error("The field " + field + " does not exist in the class " + getInstanceManager().getClassName()); + return; + } + + boolean agg = false; + boolean collection = false; + String spec = fieldmeta.getFieldType(); + if (spec.endsWith("[]")) { + agg = true; + spec = spec.substring(0, spec.length() - 2); + } else if (Collection.class.getName().equals(spec)) { + agg = true; + collection = true; + // Collection detected. Check for the specification attribute + spec = deps[i].getAttribute("specification"); + if (spec == null) { + error("A dependency injected inside a Collection must contain the 'specification' attribute"); + } + } + + // Determine the filter + String fil = deps[i].getAttribute("filter"); + // Override the filter if filter configuration if available in the instance configuration + if (filtersConfiguration != null && id != null && filtersConfiguration.get(id) != null) { + fil = (String) filtersConfiguration.get(id); + } + + // Check the from attribute + String from = deps[i].getAttribute("from"); + if (fromConfiguration != null && id != null && fromConfiguration.get(id) != null) { + from = (String) fromConfiguration.get(id); + } + + if (from != null) { + String fromFilter = "(|(instance.name=" + from + ")(service.pid=" + from + "))"; + if (agg) { + warn("The 'from' attribute is incompatible with aggregate requirements: only one provider will " + + "match : " + fromFilter); + } + if (fil != null) { + fil = "(&" + fromFilter + fil + ")"; // Append the two filters + } else { + fil = fromFilter; + } + } + + Filter filter = null; + if (fil != null) { + try { + filter = getInstanceManager().getContext().createFilter(fil); + } catch (InvalidSyntaxException e) { + throw new ConfigurationException("A requirement filter is invalid : " + filter + " - " + e.getMessage()); + } + } + + String prox = deps[i].getAttribute("proxy"); + // Use proxy by default except for array: + boolean proxy = prox == null || prox.equals("true"); + + if (prox == null && proxy) { // Proxy set because of the default. + if (agg && ! collection) { // Aggregate and array + proxy = false; + } + } + + if (proxy && agg) { + if (! collection) { + error("Proxied aggregate temporal dependencies cannot be an array. Only collections are supported"); + } + } + + long timeout = DEFAULT_TIMEOUT; + if (deps[i].containsAttribute("timeout")) { + String to = deps[i].getAttribute("timeout"); + if (to.equalsIgnoreCase("infinite") || to.equalsIgnoreCase("-1")) { + timeout = Long.MAX_VALUE; // Infinite wait time ... + } else { + timeout = new Long(deps[i].getAttribute("timeout")).longValue(); + } + } + + int policy = NO_POLICY; + String di = null; + String onTimeout = deps[i].getAttribute("onTimeout"); + if (onTimeout != null) { + if (onTimeout.equalsIgnoreCase("nullable")) { + policy = NULLABLE; + } else if (onTimeout.equalsIgnoreCase("empty-array") || onTimeout.equalsIgnoreCase("empty")) { + policy = EMPTY; + if (! agg) { + // The empty array policy can only be used on aggregate dependencies + error("Cannot use the empty array policy for " + field + " : non aggregate dependency."); + } + } else if (onTimeout.equalsIgnoreCase("null")) { + policy = NULL; + } else if (onTimeout.length() > 0) { + di = onTimeout; + policy = DEFAULT_IMPLEMENTATION; + } + } + + Class specification = DependencyModel.loadSpecification(spec, getInstanceManager().getContext()); + TemporalDependency dep = new TemporalDependency(specification, agg, collection, proxy, filter, getInstanceManager().getContext(), timeout, policy, di, this); + m_dependencies.add(dep); + + if (! proxy) { // Register method interceptor only if are not a proxy + MethodMetadata[] methods = manipulation.getMethods(); + for (int k = 0; k < methods.length; k++) { + getInstanceManager().register(methods[k], dep); + } + } + + getInstanceManager().register(fieldmeta, dep); + } + } + + /** + * Gets the requires filter configuration from the given object. + * The given object must come from the instance configuration. + * This method was made to fix FELIX-2688. It supports filter configuration using + * an array: + * <code>{"myFirstDep", "(property1=value1)", "mySecondDep", "(property2=value2)"});</code> + * + * Copied from DependencyHandler#getRequiresFilters(Object) + * + * @param requiresFiltersValue the value contained in the instance + * configuration. + * @return the dictionary. If the object in already a dictionary, just returns it, + * if it's an array, builds the dictionary. + * @throws ConfigurationException the dictionary cannot be built + */ + private Dictionary getRequiresFilters(Object requiresFiltersValue) + throws ConfigurationException { + if (requiresFiltersValue != null + && requiresFiltersValue.getClass().isArray()) { + String[] filtersArray = (String[]) requiresFiltersValue; + if (filtersArray.length % 2 != 0) { + throw new ConfigurationException( + "A requirement filter is invalid : " + + requiresFiltersValue); + } + Dictionary requiresFilters = new Hashtable(); + for (int i = 0; i < filtersArray.length; i += 2) { + requiresFilters.put(filtersArray[i], filtersArray[i + 1]); + } + return requiresFilters; + } + + return (Dictionary) requiresFiltersValue; + } + + /** + * Nothing to do. + * A temporal dependency is always valid. + * @param dependencymodel dependency. + * @see org.apache.felix.ipojo.util.DependencyStateListener#invalidate(org.apache.felix.ipojo.util.DependencyModel) + */ + public void invalidate(DependencyModel dependencymodel) { } + + /** + * Nothing to do. + * A temporal dependency is always valid. + * @param dependencymodel dependency. + * @see org.apache.felix.ipojo.util.DependencyStateListener#validate(org.apache.felix.ipojo.util.DependencyModel) + */ + public void validate(DependencyModel dependencymodel) { } + + +}
Added: felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/resources/metadata.xml URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/resources/metadata.xml?rev=1451167&view=auto ============================================================================== --- felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/resources/metadata.xml (added) +++ felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/resources/metadata.xml Thu Feb 28 10:27:21 2013 @@ -0,0 +1,28 @@ +<!-- + 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. +--> +<ipojo> + <handler + classname="org.apache.felix.ipojo.handler.temporal.TemporalHandler" + name="requires" namespace="org.apache.felix.ipojo.handler.temporal"> + </handler> + <handler + classname="org.apache.felix.ipojo.handler.temporal.TemporalHandler" + name="temporal" namespace="org.apache.felix.ipojo.handler.temporal"> + </handler> +</ipojo> \ No newline at end of file Added: felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/resources/temporal.xsd URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/resources/temporal.xsd?rev=1451167&view=auto ============================================================================== --- felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/resources/temporal.xsd (added) +++ felix/trunk/ipojo/handler/temporal/temporal-dependency-handler/src/main/resources/temporal.xsd Thu Feb 28 10:27:21 2013 @@ -0,0 +1,63 @@ +<!-- + 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. +--> +<xs:schema targetNamespace="org.apache.felix.ipojo.handler.temporal" + xmlns="org.apache.felix.ipojo.handler.temporal" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified"> + <xs:element name="requires" type="TemporalServiceDependencyType"></xs:element> + <xs:element name="temporal" type="TemporalServiceDependencyType"></xs:element> + + + <xs:complexType name="TemporalServiceDependencyType"> + + <xs:annotation> + <xs:documentation>Description of a temporal dependency.</xs:documentation> + </xs:annotation> + <xs:attribute name="field" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation>The implementation field supporting the dependency.</xs:documentation> + </xs:annotation></xs:attribute> + <xs:attribute name="id" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation>The dependency id</xs:documentation> + </xs:annotation></xs:attribute> + <xs:attribute name="filter" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation>Filter use to discover matching filter.</xs:documentation></xs:annotation></xs:attribute> + <xs:attribute name="timeout" type="xs:int" use="optional"> + <xs:annotation> + <xs:documentation>Specifies the timeout after which the onTimeout policy is executed. The value is the time in ms to wait. -1 is used to indicate an infinite wait.</xs:documentation> + </xs:annotation></xs:attribute> + <xs:attribute name="onTimeout" use="optional" type="xs:string"> + <xs:annotation> + <xs:documentation>Specifies the onTimeout policy. This determines the object to inject when the service stills unavailable when the timeout expires. Several values are supported: 'nullable' means that a Nullable object will be injected, 'empty-array' injects an empty array (only for aggregate dependency), 'null' injects Null, any other value are interpreted as the default implementation class to use. If the onTimetout attribute is not specified, a RuntimeException is thrown when the timeout is reached.</xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="proxy" use="optional" type="xs:boolean"> + <xs:annotation> + <xs:documentation>Enables or Disables the proxy injection</xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="specification" use="optional" type="xs:string"> + <xs:annotation> + <xs:documentation>Specifies the looked service specification. This attribute is mandatory when injecting in a Collection</xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> +</xs:schema> \ No newline at end of file
