/* * 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.tuscany.sca.assembly.xml; import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; import org.apache.tuscany.sca.assembly.Base; import org.apache.tuscany.sca.assembly.ComponentType; import org.apache.tuscany.sca.assembly.Implementation; import org.apache.tuscany.sca.contribution.processor.ContributionReadException; import org.apache.tuscany.sca.contribution.processor.ContributionResolveException; import org.apache.tuscany.sca.contribution.processor.ContributionWriteException; import org.apache.tuscany.sca.contribution.processor.ProcessorContext; import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor; import org.apache.tuscany.sca.contribution.resolver.ModelResolver; import org.apache.tuscany.sca.core.FactoryExtensionPoint; import org.apache.tuscany.sca.policy.PolicySubject; /** * Default Model Processor for beans. * * @version $Rev$ $Date$ */ public class DefaultBeanModelProcessor<T> extends BaseAssemblyProcessor implements StAXArtifactProcessor<T> { private QName artifactType; private Class<T> modelClass; private Object modelFactory; private Method factoryMethod; private Map<String, Method> setterMethods = new HashMap<String, Method>(); private Map<String, Method> getterMethods = new HashMap<String, Method>(); public DefaultBeanModelProcessor(FactoryExtensionPoint modeFactories, QName artifactType, Class<T> modelClass, Object modelFactory) { super(modeFactories, null); this.artifactType = artifactType; this.modelClass = modelClass; this.modelFactory = modelFactory; // Introspect the factory class and bean model class if (modelFactory != null) { // Find the model create method for (Method method: modelFactory.getClass().getMethods()) { if (method.getName().startsWith("create") && method.getReturnType() == modelClass) { factoryMethod = method; break; } } } // Index the bean's setter methods for (Method method: modelClass.getMethods()) { Method getter; String name = method.getName(); if (name.startsWith("set") && name.length() > 3) { // Get the corresponding getter method try { getter = modelClass.getMethod("get" + name.substring(3)); } catch (Exception e) { getter = null; continue; } // Get the property name name = name.substring(3); if (name.length() > 1) { if (!name.toUpperCase().equals(name)) { name = name.substring(0, 1).toLowerCase() + name.substring(1); } } } else { continue; } // Map an uppercase property name to a lowercase attribute name if (name.toUpperCase().equals(name)) { name = name.toLowerCase(); } // Trim trailing _ from property names if (name.endsWith("_")) { name = name.substring(0, name.length()-1); } setterMethods.put(name, method); getterMethods.put(name, getter); } } public T read(XMLStreamReader reader, ProcessorContext context) throws ContributionReadException, XMLStreamException { // Read an element try { // Create a new instance of the model Object model; if (modelFactory != null) { // Invoke the factory create method model = factoryMethod.invoke(modelFactory); } else { // Invoke the model bean class default constructor model = modelClass.newInstance(); } // Initialize the bean properties with the attributes found in the // XML element for (int i = 0, n = reader.getAttributeCount(); i < n; i++) { String attributeName = reader.getAttributeLocalName(i); Method setter = setterMethods.get(attributeName); if (setter != null) { String value = null; if (attributeName.equals("uri")){ value = getURIString(reader, "uri"); } else { value = reader.getAttributeValue(i); } setter.invoke(model, value); } } // Read policies policyProcessor.readPolicies(model, reader); // FIXME read extension elements // By default mark the model object unresolved if (model instanceof Base) { ((Base)model).setUnresolved(true); } // Skip to end element while (reader.hasNext()) { if (reader.next() == END_ELEMENT && artifactType.equals(reader.getName())) { break; } } return (T) model; } catch (Exception e) { ContributionReadException ce = new ContributionReadException(e); error(context.getMonitor(), "ContributionReadException", reader, ce); throw ce; } } public void write(T bean, XMLStreamWriter writer, ProcessorContext context) throws ContributionWriteException, XMLStreamException { try { // Write the bean properties as attributes List<XAttr> attrs = new ArrayList<XAttr>(); for (Map.Entry<String, Method> entry: getterMethods.entrySet()) { if (entry.getValue().getReturnType() == String.class) { String value = (String)entry.getValue().invoke(bean); attrs.add(new XAttr(entry.getKey(), value)); } } // Write element writeStart(writer, artifactType.getNamespaceURI(), artifactType.getLocalPart(), policyProcessor.writePolicies(bean), new XAttr(null, attrs)); writeEnd(writer); } catch (Exception e) { ContributionWriteException ce = new ContributionWriteException(e); error(context.getMonitor(), "ContributionWriteException", writer, ce); throw ce; } } public void resolve(T bean, ModelResolver resolver, ProcessorContext context) throws ContributionResolveException { // Resolve and merge the component type associated with an // implementation model if (bean instanceof Implementation) { Implementation implementation = (Implementation)bean; String uri = implementation.getURI(); if (uri != null) { int d = uri.lastIndexOf('.'); if (d != -1) { uri = uri.substring(0, d) + ".componentType"; // Resolve the component type ComponentType componentType = assemblyFactory.createComponentType(); componentType.setURI(uri); componentType.setUnresolved(true); componentType = resolver.resolveModel(ComponentType.class, componentType, context); if (componentType != null && !componentType.isUnresolved()) { // We found a component type, merge it into the implementation model implementation.getServices().addAll(componentType.getServices()); implementation.getReferences().addAll(componentType.getReferences()); implementation.getProperties().addAll(componentType.getProperties()); if (implementation instanceof PolicySubject && componentType instanceof PolicySubject ) { PolicySubject policiedImpl = (PolicySubject)implementation; PolicySubject policiedCompType = (PolicySubject)componentType; if ( policiedImpl.getPolicySets() != null) { policiedImpl.getPolicySets().addAll(policiedCompType.getPolicySets()); } if (policiedImpl.getRequiredIntents() != null) { policiedImpl.getRequiredIntents().addAll(policiedCompType.getRequiredIntents()); } } } } } } // Mark the model resolved if (bean instanceof Base) { ((Base)bean).setUnresolved(false); } } public QName getArtifactType() { return artifactType; } public Class<T> getModelType() { return modelClass; } }