Author: [email protected] Date: Mon Dec 5 17:10:04 2011 New Revision: 1817
Log: [sandbox] Adding a tenant aware autoconf tha can handle unavailibility (AMDATU-468) Added: sandbox/bdekruijff/amdatu-core/deployment-tenantconf/ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/LICENSE sandbox/bdekruijff/amdatu-core/deployment-tenantconf/NOTICE sandbox/bdekruijff/amdatu-core/deployment-tenantconf/pom.xml sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/org/ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/tenantconf/ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/tenantconf/Activator.java sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/tenantconf/TenantAutoConfResourceProcessor.java Added: sandbox/bdekruijff/amdatu-core/deployment-tenantconf/LICENSE ============================================================================== --- (empty file) +++ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/LICENSE Mon Dec 5 17:10:04 2011 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. Added: sandbox/bdekruijff/amdatu-core/deployment-tenantconf/NOTICE ============================================================================== --- (empty file) +++ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/NOTICE Mon Dec 5 17:10:04 2011 @@ -0,0 +1,18 @@ +Amdatu Core Fileinstall Metatype +Copyright 2010, 2011 The Amdatu Foundation + +I. Included Software + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). +Licensed under the Apache License 2.0. + +II. Used Software + +This product uses software developed at +The OSGi Alliance (http://www.osgi.org/). +Copyright (c) OSGi Alliance (2000, 2007). +Licensed under the Apache License 2.0. + +III. License Summary +- Apache License 2.0 Added: sandbox/bdekruijff/amdatu-core/deployment-tenantconf/pom.xml ============================================================================== --- (empty file) +++ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/pom.xml Mon Dec 5 17:10:04 2011 @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2010, 2011 The Amdatu Foundation + + Licensed 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.verning permissions and limitations + under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.amdatu.core</groupId> + <artifactId>org.amdatu.core</artifactId> + <version>0.3.0-SNAPSHOT</version> + </parent> + + <packaging>bundle</packaging> + <name>Amdatu Core - Deployment Tenant Configuration processor</name> + <description></description> + <url>http://www.amdatu.org</url> + <artifactId>org.amdatu.core.deployment.tenantconf</artifactId> + + <dependencies> + <dependency> + <groupId>org.amdatu.core</groupId> + <artifactId>org.amdatu.core.tenant</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.deployment.rp.autoconf</artifactId> + <version>0.1.0</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.metatype</artifactId> + <version>1.0.4</version> + <scope>provided</scope> + <exclusions> + <exclusion> + <groupId>org.apache.felix</groupId> + <artifactId>org.osgi.core</artifactId> + </exclusion> + <exclusion> + <groupId>org.apache.felix</groupId> + <artifactId>org.osgi.compendium</artifactId> + </exclusion> + </exclusions> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-Activator>org.amdatu.core.deployment.tenantconf.Activator</Bundle-Activator> + <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName> + <Embed-Dependency>*;scope=compile;inline=!org/osgi/**</Embed-Dependency> + <Embed-Transitive>false</Embed-Transitive> + <Private-Package> + org.amdatu.core.deployment.tenantconf + </Private-Package> + <Export-Package> + </Export-Package> + <Import-Package> + * + </Import-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> +</project> Added: sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/tenantconf/Activator.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/tenantconf/Activator.java Mon Dec 5 17:10:04 2011 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2010, 2011 The Amdatu Foundation + * + * Licensed 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.amdatu.core.deployment.tenantconf; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.apache.felix.dm.DependencyActivatorBase; +import org.apache.felix.dm.DependencyManager; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.deploymentadmin.spi.ResourceProcessor; +import org.osgi.service.log.LogService; +import org.osgi.service.metatype.MetaTypeService; + +/** + * Bundle activator for the TenantConf Resource Processor Customizer bundle + */ +public class Activator extends DependencyActivatorBase { + + /** + * @see org.apache.felix.dm.DependencyActivatorBase#init(org.osgi.framework.BundleContext, org.apache.felix.dm.DependencyManager) + */ + @Override + public void init(BundleContext context, DependencyManager manager) throws Exception { + Dictionary<String, Object> properties = new Hashtable<String, Object>(); + properties.put(Constants.SERVICE_PID, "org.osgi.deployment.rp.autoconf"); + + manager.add(createComponent() + .setInterface(ResourceProcessor.class.getName(), properties) + .setImplementation(TenantAutoConfResourceProcessor.class) + .add(createServiceDependency() + .setService(MetaTypeService.class) + .setInstanceBound(true) + .setRequired(true)) + .add(createServiceDependency() + .setService(ConfigurationAdmin.class) + .setCallbacks("addConfigurationAdmin", "removeConfigurationAdmin") + .setRequired(false)) + .add(createServiceDependency() + .setService(LogService.class) + .setRequired(false))); + } + + /** + * @see org.apache.felix.dm.DependencyActivatorBase#destroy(org.osgi.framework.BundleContext, org.apache.felix.dm.DependencyManager) + */ + @Override + public void destroy(BundleContext context, DependencyManager manager) throws Exception { + // do nothing + } +} \ No newline at end of file Added: sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/tenantconf/TenantAutoConfResourceProcessor.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/tenantconf/TenantAutoConfResourceProcessor.java Mon Dec 5 17:10:04 2011 @@ -0,0 +1,727 @@ +/* + * Copyright (c) 2010, 2011 The Amdatu Foundation + * + * Licensed 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.amdatu.core.deployment.tenantconf; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.amdatu.core.tenant.Tenant; +import org.apache.felix.deployment.rp.autoconf.AutoConfResource; +import org.apache.felix.deployment.rp.autoconf.ObjectClassDefinitionImpl; +import org.apache.felix.deployment.rp.autoconf.PersistencyManager; +import org.apache.felix.metatype.Attribute; +import org.apache.felix.metatype.Designate; +import org.apache.felix.metatype.MetaData; +import org.apache.felix.metatype.MetaDataReader; +import org.apache.felix.metatype.OCD; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.deploymentadmin.spi.DeploymentSession; +import org.osgi.service.deploymentadmin.spi.ResourceProcessor; +import org.osgi.service.deploymentadmin.spi.ResourceProcessorException; +import org.osgi.service.log.LogService; +import org.osgi.service.metatype.AttributeDefinition; +import org.osgi.service.metatype.MetaTypeInformation; +import org.osgi.service.metatype.MetaTypeService; +import org.osgi.service.metatype.ObjectClassDefinition; +import org.xmlpull.v1.XmlPullParserException; + +/** + * Tenant ware autoconf processor, inspired on the default autoconf processor from + * Apache Felix, with two important tweaks: + * + * 1) This processor maps resources to tenant specific configurationAdmin services + * based on the filename pattern: /tenant-<tenanid>-<classifier>.xml/. + * + * 2) This processor does not fail immediately when a configurationAdmin is not + * available. It caches updates and tries to deliver them later. + * + * + * FIXME this is a first an naive implementation! Both the matching scheme and (non) + * persistent delivery need attention and tweaking. + * + */ +public final class TenantAutoConfResourceProcessor implements ResourceProcessor { + + public static final String ORIGINAL_PID_KEY = "org.apache.felix.fileinstall.metatype.orgPid"; + + // injected by dependency management + private volatile BundleContext m_bundleContext; + private volatile MetaTypeService m_metaTypeService; + private volatile LogService m_logService; + + // session scoped state + private DeploymentSession m_session = null; + private final Map<String, List<AutoConfResource>> m_toBeInstalled = new HashMap<String, List<AutoConfResource>>(); + private final Map<String, List<AutoConfResource>> m_toBeDeleted = new HashMap<String, List<AutoConfResource>>(); + private final List<ConfigurationResourcesTask> m_updateTasks = + new LinkedList<ConfigurationResourcesTask>(); + + // service scoped state + private final Map<ServiceReference, ConfigurationAdmin> m_configurationAdmins = + new HashMap<ServiceReference, ConfigurationAdmin>(); + private final List<ConfigurationResourcesTask> m_scheduledTasks = + new LinkedList<ConfigurationResourcesTask>(); + + private PersistencyManager m_persistencyManager; + + public void start() throws IOException { + m_logService.log(LogService.LOG_DEBUG, "Starting"); + + File root = m_bundleContext.getDataFile(""); + if (root == null) { + throw new IOException("No file system support"); + } + m_persistencyManager = new PersistencyManager(root); + } + + public void stop() { + m_logService.log(LogService.LOG_DEBUG, "Stopping"); + } + + public void addConfigurationAdmin(ServiceReference reference, ConfigurationAdmin admin) { + m_logService.log(LogService.LOG_INFO, "ConfigurationAdmin added... "); + synchronized (m_configurationAdmins) { + m_configurationAdmins.put(reference, admin); + } + runUpdateTasks(); + } + + public void removeConfigurationAdmin(ServiceReference reference, ConfigurationAdmin admin) { + m_logService.log(LogService.LOG_INFO, "ConfigurationAdmin removed... "); + synchronized (m_configurationAdmins) { + m_configurationAdmins.remove(reference); + } + } + + public void runUpdateTasks() { + m_logService.log(LogService.LOG_INFO, "Start running update tasks... "); + synchronized (m_scheduledTasks) { + List<ConfigurationResourcesTask> toBeRemoved = + new LinkedList<ConfigurationResourcesTask>(); + for (ConfigurationResourcesTask task : m_scheduledTasks) { + + String resourceName = task.getResource().getName(); + String tenantId = "_PLATFORM"; + Pattern p = Pattern.compile("tenant-([^-\\.]+)-*(.*)\\.xml"); + Matcher m = p.matcher(resourceName); + if (m.find() + && m.group(1) != null && !"".equals(m.group(1)) + && m.group(2) != null && !"".equals(m.group(2))) { + tenantId = m.group(1); + } + + synchronized (m_configurationAdmins) { + for (Entry<ServiceReference, ConfigurationAdmin> entry : m_configurationAdmins.entrySet()) { + ServiceReference reference = entry.getKey(); + Object prop = reference.getProperty(Tenant.TENANT_ID_SERVICEPROPERTY); + if (prop != null && tenantId.equals(prop)) { + try { + m_logService.log(LogService.LOG_INFO, "Running update task for tenant... " + tenantId); + task.run(entry.getValue()); + } + catch (Exception e) { + e.printStackTrace(); + } + toBeRemoved.add(task); + } + } + } + } + m_scheduledTasks.removeAll(toBeRemoved); + } + m_logService.log(LogService.LOG_INFO, "Stop running update tasks... "); + } + + /** + * @see org.osgi.service.deploymentadmin.spi.ResourceProcessor#begin(org.osgi.service.deploymentadmin.spi.DeploymentSession) + */ + public synchronized void begin(DeploymentSession session) { + m_logService.log(LogService.LOG_INFO, "Beginning session... "); + m_session = session; + m_toBeInstalled.clear(); + m_toBeDeleted.clear(); + m_updateTasks.clear(); + } + + /** + * @see org.osgi.service.deploymentadmin.spi.ResourceProcessor#process(java.lang.String, java.io.InputStream) + */ + public synchronized void process(String name, InputStream stream) throws ResourceProcessorException { + if (m_session == null) { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Can not process resource without a Deployment Session"); + } + m_logService.log(LogService.LOG_INFO, "Start processing ... " + name); + + MetaDataReader reader = new MetaDataReader(); + MetaData data = null; + try { + data = reader.parse(stream); + } + catch (IOException e) { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Unable to process resource.", e); + } + catch (XmlPullParserException e) { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Supplied configuration is not conform the metatype xml specification.", e); + } + if (data == null) { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Supplied configuration is not conform the metatype xml specification."); + } + + Map designates = data.getDesignates(); + if (designates == null) { + m_logService + .log(LogService.LOG_INFO, "No designates found in the resource, so there's nothing to process."); + return; + } + + Map localOcds = data.getObjectClassDefinitions(); + if (localOcds == null) { + localOcds = Collections.EMPTY_MAP; + } + + Iterator i = designates.keySet().iterator(); + while (i.hasNext()) { + Designate designate = (Designate) designates.get(i.next()); + + // determine OCD + ObjectClassDefinition ocd = null; + String ocdRef = designate.getObject().getOcdRef(); + OCD localOcd = (OCD) localOcds.get(ocdRef); + // ask meta type service for matching OCD if no local OCD has been defined + ocd = (localOcd != null) ? new ObjectClassDefinitionImpl(localOcd) : getMetaTypeOCD(data, designate); + if (ocd == null) { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "No Object Class Definition found with id=" + ocdRef); + } + + // determine configuration data based on the values and their type definition + Dictionary dict = getProperties(designate, ocd); + if (dict == null) { + // designate does not match it's definition, but was marked optional, ignore it + continue; + } + + // add to session data + if (!m_toBeInstalled.containsKey(name)) { + m_toBeInstalled.put(name, new ArrayList()); + } + List resources = (List) m_toBeInstalled.get(name); + resources.add(new AutoConfResource(name, designate.getPid(), designate.getFactoryPid(), designate + .getBundleLocation(), designate.isMerge(), dict)); + } + m_logService.log(LogService.LOG_INFO, "Stop processing ... " + name); + } + + /** + * @see org.osgi.service.deploymentadmin.spi.ResourceProcessor#dropped(java.lang.String) + */ + public synchronized void dropped(String name) throws ResourceProcessorException { + if (m_session == null) { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Can not process resource without a Deployment Session"); + } + m_logService.log(LogService.LOG_INFO, "Start dropping ... " + name); + + try { + List resources = m_persistencyManager.load(name); + if (!m_toBeDeleted.containsKey(name)) { + m_toBeDeleted.put(name, new ArrayList()); + } + ((List) m_toBeDeleted.get(name)).addAll(resources); + } + catch (IOException ioe) { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Unable to drop resource: " + name, ioe); + } + m_logService.log(LogService.LOG_INFO, "Stop dropping ... " + name); + } + + /** + * @see org.osgi.service.deploymentadmin.spi.ResourceProcessor#dropAllResources() + */ + public synchronized void dropAllResources() throws ResourceProcessorException { + if (m_session == null) { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Can not drop all resources without a Deployment Session"); + } + + m_logService.log(LogService.LOG_INFO, "Start dropping all... "); + + try { + Map loadAll = m_persistencyManager.loadAll(); + for (Iterator i = loadAll.keySet().iterator(); i.hasNext();) { + String name = (String) i.next(); + dropped(name); + } + } + catch (IOException ioe) { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Unable to drop all resources.", ioe); + } + + File basedir = m_bundleContext.getDataFile(""); + if (basedir != null && basedir.isDirectory()) { + String[] files = basedir.list(); + for (int i = 0; i < files.length; i++) { + dropped(files[i]); + } + } + else { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Unable to drop resources, data area is not accessible"); + } + m_logService.log(LogService.LOG_INFO, "Stop dropping all ... "); + } + + /** + * @see org.osgi.service.deploymentadmin.spi.ResourceProcessor#prepare() + */ + public synchronized void prepare() throws ResourceProcessorException { + if (m_session == null) { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Can not process resource without a Deployment Session"); + } + + try { + + // delete dropped resources + for (Entry<String, List<AutoConfResource>> entry : m_toBeDeleted.entrySet()) { + String name = entry.getKey(); + List<AutoConfResource> resources = entry.getValue(); + for (AutoConfResource resource : resources) { + m_updateTasks.add(new DropConfigurationResourceTask(resource)); + } + m_persistencyManager.delete(name); + } + + for (Entry<String, List<AutoConfResource>> entry : m_toBeInstalled.entrySet()) { + String name = entry.getKey(); + List<AutoConfResource> resources = entry.getValue(); + List<AutoConfResource> existingResources = m_persistencyManager.load(name); + + for (AutoConfResource resource : resources) { + AutoConfResource updatesExisting = null; + for (AutoConfResource existingResource : existingResources) { + if (resource.equalsTargetConfiguration(existingResource)) { + updatesExisting = existingResource; + } + } + if (updatesExisting != null) { + existingResources.remove(updatesExisting); + } + + resource.setGeneratedPid(resource.getPid()); + m_updateTasks.add(new InstallorUpdateConfigurationResourceTask(resource)); + } + + for (AutoConfResource resource : existingResources) { + m_updateTasks.add(new DropConfigurationResourceTask(resource)); + } + m_persistencyManager.store(name, resources); + } + } + catch (IOException ioe) { + m_toBeInstalled.clear(); + throw new ResourceProcessorException(ResourceProcessorException.CODE_PREPARE, + "Unable to prepare for commit for resource", ioe); + } + } + + /** + * @see org.osgi.service.deploymentadmin.spi.ResourceProcessor#commit() + */ + public synchronized void commit() { + synchronized (m_scheduledTasks) { + m_scheduledTasks.addAll(m_updateTasks); + } + + m_toBeInstalled.clear(); + m_toBeDeleted.clear(); + m_updateTasks.clear(); + m_session = null; + + runUpdateTasks(); + } + + /** + * @see org.osgi.service.deploymentadmin.spi.ResourceProcessor#rollback() + */ + public synchronized void rollback() { + Set keys = m_toBeInstalled.keySet(); + for (Iterator i = keys.iterator(); i.hasNext();) { + List configs = (List) m_toBeInstalled.get(i.next()); + for (Iterator j = configs.iterator(); j.hasNext();) { + AutoConfResource resource = (AutoConfResource) j.next(); + String name = resource.getName(); + try { + dropped(name); + } + catch (ResourceProcessorException e) { + m_logService.log(LogService.LOG_ERROR, + "Unable to roll back resource '" + name + "', reason: " + e.getMessage() + ", caused by: " + + e.getCause().getMessage()); + } + break; + } + } + m_toBeInstalled.clear(); + m_toBeDeleted.clear(); + m_session = null; + } + + /** + * @see org.osgi.service.deploymentadmin.spi.ResourceProcessor#cancel() + */ + public synchronized void cancel() { + rollback(); + } + + /** + * Determines the actual configuration data based on the specified designate and object class definition + * + * @param designate The designate object containing the values for the properties + * @param ocd The object class definition + * @return A dictionary containing data as described in the designate and ocd objects, or <code>null</code> if the designate does not match it's + * definition and the designate was marked as optional. + * @throws ResourceProcessorException If the designate does not match the ocd and the designate is not marked as optional. + */ + private Dictionary getProperties(Designate designate, ObjectClassDefinition ocd) throws ResourceProcessorException { + Dictionary properties = new Hashtable(); + AttributeDefinition[] attributeDefs = ocd.getAttributeDefinitions(ObjectClassDefinition.ALL); + List attributes = designate.getObject().getAttributes(); + + for (Iterator i = attributes.iterator(); i.hasNext();) { + Attribute attribute = (Attribute) i.next(); + + String adRef = attribute.getAdRef(); + boolean found = false; + for (int j = 0; j < attributeDefs.length; j++) { + AttributeDefinition ad = attributeDefs[j]; + if (adRef.equals(ad.getID())) { + // found attribute definition + Object value = getValue(attribute, ad); + if (value == null) { + if (designate.isOptional()) { + properties = null; + break; + } + else { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Could not match attribute to it's definition: adref=" + adRef); + } + } + properties.put(adRef, value); + found = true; + break; + } + } + if (!found) { + if (designate.isOptional()) { + properties = null; + break; + } + else { + throw new ResourceProcessorException(ResourceProcessorException.CODE_OTHER_ERROR, + "Could not find attribute definition: adref=" + adRef); + } + } + } + + return properties; + } + + /** + * Determines the object class definition matching the specified designate. + * + * @param data The meta data containing 'local' object class definitions. + * @param designate The designate whose object class definition should be determined. + * @return + * @throws ResourceProcessorException + */ + private ObjectClassDefinition getMetaTypeOCD(MetaData data, Designate designate) { + + ObjectClassDefinition ocd = null; + String ocdRef = designate.getObject().getOcdRef(); + + String bundleLocation = designate.getBundleLocation(); + boolean allBundles = (bundleLocation == null || "".equals(bundleLocation) || "*".equals(bundleLocation)); + + String pid = isFactoryConfig(designate) ? pid = designate.getFactoryPid() : designate.getPid(); + + Bundle[] bundles = m_bundleContext.getBundles(); + for (int i = 0; i < bundles.length; i++) { + if (allBundles || bundleLocation.equals(bundles[i].getLocation())) { + MetaTypeInformation mti = m_metaTypeService.getMetaTypeInformation(bundles[i]); + if (mti != null) { + ObjectClassDefinition tempOcd = mti.getObjectClassDefinition(pid, null); + if (tempOcd != null && ocdRef.equals(tempOcd.getID())) { + ocd = tempOcd; + break; + } + } + } + } + return ocd; + } + + private boolean isFactoryConfig(Designate designate) { + String factoryPid = designate.getFactoryPid(); + return (factoryPid != null && !"".equals(factoryPid)); + } + + /** + * Determines the value of an attribute based on an attribute definition + * + * @param attribute The attribute containing value(s) + * @param ad The attribute definition + * @return An <code>Object</code> reflecting what was specified in the attribute and it's definition or <code>null</code> if + * the value did not match it's definition. + */ + private Object getValue(Attribute attribute, AttributeDefinition ad) { + if (attribute == null || ad == null || !attribute.getAdRef().equals(ad.getID())) { + // wrong attribute or definition + return null; + } + String[] content = attribute.getContent(); + + // verify correct type of the value(s) + int type = ad.getType(); + Object[] typedContent = null; + try { + for (int i = 0; i < content.length; i++) { + String value = content[i]; + switch (type) { + case AttributeDefinition.BOOLEAN: + typedContent = (typedContent == null) ? new Boolean[content.length] : typedContent; + typedContent[i] = Boolean.valueOf(value); + break; + case AttributeDefinition.BYTE: + typedContent = (typedContent == null) ? new Byte[content.length] : typedContent; + typedContent[i] = Byte.valueOf(value); + break; + case AttributeDefinition.CHARACTER: + typedContent = (typedContent == null) ? new Character[content.length] : typedContent; + char[] charArray = value.toCharArray(); + if (charArray.length == 1) { + typedContent[i] = new Character(charArray[0]); + } + else { + return null; + } + break; + case AttributeDefinition.DOUBLE: + typedContent = (typedContent == null) ? new Double[content.length] : typedContent; + typedContent[i] = Double.valueOf(value); + break; + case AttributeDefinition.FLOAT: + typedContent = (typedContent == null) ? new Float[content.length] : typedContent; + typedContent[i] = Float.valueOf(value); + break; + case AttributeDefinition.INTEGER: + typedContent = (typedContent == null) ? new Integer[content.length] : typedContent; + typedContent[i] = Integer.valueOf(value); + break; + case AttributeDefinition.LONG: + typedContent = (typedContent == null) ? new Long[content.length] : typedContent; + typedContent[i] = Long.valueOf(value); + break; + case AttributeDefinition.SHORT: + typedContent = (typedContent == null) ? new Short[content.length] : typedContent; + typedContent[i] = Short.valueOf(value); + break; + case AttributeDefinition.STRING: + typedContent = (typedContent == null) ? new String[content.length] : typedContent; + typedContent[i] = value; + break; + default: + // unsupported type + return null; + } + } + } + catch (NumberFormatException nfe) { + return null; + } + + // verify cardinality of value(s) + int cardinality = ad.getCardinality(); + Object result = null; + if (cardinality == 0) { + if (typedContent.length == 1) { + result = typedContent[0]; + } + else { + result = null; + } + } + else if (cardinality == Integer.MIN_VALUE) { + result = new Vector(Arrays.asList(typedContent)); + } + else if (cardinality == Integer.MAX_VALUE) { + result = typedContent; + } + else if (cardinality < 0) { + if (typedContent.length <= Math.abs(cardinality)) { + result = new Vector(Arrays.asList(typedContent)); + } + else { + result = null; + } + } + else if (cardinality > 0) { + if (typedContent.length <= cardinality) { + result = typedContent; + } + else { + result = null; + } + } + return result; + } +} + +interface ConfigurationResourcesTask { + AutoConfResource getResource(); + + void run(ConfigurationAdmin admin) throws Exception; +} + +abstract class BaseConfigurationResourceTask implements ConfigurationResourcesTask { + + protected AutoConfResource m_resource; + + public BaseConfigurationResourceTask(AutoConfResource resource) { + m_resource = resource; + } + + public AutoConfResource getResource() { + return m_resource; + } +} + +class DropConfigurationResourceTask extends BaseConfigurationResourceTask { + + public DropConfigurationResourceTask(AutoConfResource resource) { + super(resource); + } + + public void run(ConfigurationAdmin configAdmin) throws Exception { + if (m_resource.isFactoryConfig()) { + + Configuration configuration = null; + Configuration[] configurations = + configAdmin.listConfigurations("(" + TenantAutoConfResourceProcessor.ORIGINAL_PID_KEY + "=" + + m_resource.getPid() + ")"); + if (configurations != null && configurations.length > 0) { + configuration = configurations[0]; + } + if (configuration != null) { + configuration.delete(); + } + } + else { + Configuration configuration = + configAdmin.getConfiguration(m_resource.getPid(), m_resource.getBundleLocation()); + if (configuration != null) { + configuration.delete(); + } + } + } +} + +class InstallorUpdateConfigurationResourceTask extends BaseConfigurationResourceTask { + + public InstallorUpdateConfigurationResourceTask(AutoConfResource resource) { + super(resource); + } + + public void run(ConfigurationAdmin configAdmin) throws Exception { + + Configuration configuration = null; + if (m_resource.isFactoryConfig()) { + Configuration[] configurations = + configAdmin.listConfigurations("(" + TenantAutoConfResourceProcessor.ORIGINAL_PID_KEY + "=" + + m_resource.getPid() + ")"); + if (configurations != null && configurations.length > 0) { + configuration = configurations[0]; + } + if (configuration == null) { + configuration = + configAdmin.createFactoryConfiguration(m_resource.getFactoryPid(), + m_resource.getBundleLocation()); + } + m_resource.setGeneratedPid(configuration.getPid()); + } + else { + configuration = configAdmin.getConfiguration(m_resource.getPid(), m_resource.getBundleLocation()); +// if (m_resource.getBundleLocation() != configuration.getBundleLocation()) { +// // an existing configuration exists that is bound to a different location, which is not allowed +// throw new ResourceProcessorException(ResourceProcessorException.CODE_PREPARE, +// "Existing configuration was not unbound and not bound to the specified bundlelocation"); +// } + } + + Dictionary<Object, Object> properties = new Hashtable<Object, Object>(); + if (m_resource.isMerge()) { + Dictionary<Object, Object> existingProperties = configuration.getProperties(); + if (existingProperties != null) { + Enumeration keys = existingProperties.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + properties.put(key, existingProperties.get(key)); + } + } + } + + Dictionary<Object, Object> newProperties = m_resource.getProperties(); + if (newProperties != null) { + Enumeration keys = newProperties.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + properties.put(key, newProperties.get(key)); + } + } + + properties.put(TenantAutoConfResourceProcessor.ORIGINAL_PID_KEY, m_resource.getPid()); + + configuration.update(properties); + } +} _______________________________________________ Amdatu-commits mailing list [email protected] http://lists.amdatu.org/mailman/listinfo/amdatu-commits
