Author: [email protected]
Date: Mon Jan 30 16:43:34 2012
New Revision: 2026
Log:
AMDATU-529 Restored hostname based tenant resolver using tenant property.
Requires custom OCD.
Added:
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/HostNameTenantResolverConstants.java
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/osgi/Activator.java
- copied, changed from r2025,
/trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/osgi/HostnameTenantResolverActivator.java
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/service/HostnameTenantMappingRegistry.java
Modified:
trunk/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/tenantconf/TenantAutoConfResourceProcessor.java
trunk/amdatu-core/itest/base/src/main/java/org/amdatu/core/itest/base/CoreConfigs.java
trunk/amdatu-core/itest/tests/src/test/java/org/amdatu/core/itest/tests/DefaultCoreServicesTest.java
trunk/amdatu-core/itest/tests/src/test/java/org/amdatu/core/itest/tests/tenant/MultiTenantTest.java
trunk/amdatu-core/tenant-factory/src/main/java/org/amdatu/core/tenant/factory/TenantServiceFactory.java
trunk/amdatu-web/itest/base/src/main/java/org/amdatu/web/itest/base/WebConfigs.java
trunk/amdatu-web/itest/tests/src/test/java/org/amdatu/web/itest/tests/BenchmarkTest.java
trunk/amdatu-web/tenantresolver-hostname/pom.xml
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/osgi/HostnameTenantResolverActivator.java
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/service/HostnameTenantResolverExtenderFilter.java
Modified:
trunk/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/tenantconf/TenantAutoConfResourceProcessor.java
==============================================================================
---
trunk/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/tenantconf/TenantAutoConfResourceProcessor.java
(original)
+++
trunk/amdatu-core/deployment-tenantconf/src/main/java/org/amdatu/core/deployment/tenantconf/TenantAutoConfResourceProcessor.java
Mon Jan 30 16:43:34 2012
@@ -136,7 +136,7 @@
for (ConfigurationResourcesTask task : m_scheduledTasks) {
String resourceName = task.getResource().getName();
- String tenantId = "_PLATFORM";
+ String tenantId = TenantConstants.PID_VALUE_PLATFORM;
Pattern p = Pattern.compile("tenant-([^-\\.]+)-*(.*)\\.xml");
Matcher m = p.matcher(resourceName);
if (m.find()
Modified:
trunk/amdatu-core/itest/base/src/main/java/org/amdatu/core/itest/base/CoreConfigs.java
==============================================================================
---
trunk/amdatu-core/itest/base/src/main/java/org/amdatu/core/itest/base/CoreConfigs.java
(original)
+++
trunk/amdatu-core/itest/base/src/main/java/org/amdatu/core/itest/base/CoreConfigs.java
Mon Jan 30 16:43:34 2012
@@ -22,6 +22,8 @@
import java.util.List;
import java.util.Properties;
+import org.amdatu.core.tenant.TenantConstants;
+
/**
* Provides a mean to provide default configurations for certain core platform
services.
*/
@@ -112,8 +114,8 @@
*/
private static Properties tenantCnf() {
Properties properties = new Properties();
- properties.put("id", "Default");
- properties.put("name", "Default Tenant");
+ properties.put(TenantConstants.PID_KEY, "Default");
+ properties.put(TenantConstants.NAME_KEY, "Default Tenant");
return properties;
}
}
\ No newline at end of file
Modified:
trunk/amdatu-core/itest/tests/src/test/java/org/amdatu/core/itest/tests/DefaultCoreServicesTest.java
==============================================================================
---
trunk/amdatu-core/itest/tests/src/test/java/org/amdatu/core/itest/tests/DefaultCoreServicesTest.java
(original)
+++
trunk/amdatu-core/itest/tests/src/test/java/org/amdatu/core/itest/tests/DefaultCoreServicesTest.java
Mon Jan 30 16:43:34 2012
@@ -134,13 +134,13 @@
public void testTenantAwareUserAdminAvailable() throws Exception {
Properties properties = new Properties();
- properties.put("id", "tenant@localhost");
- properties.put("name", "Tenant localhost");
+ properties.put(TenantConstants.PID_KEY, "tenant@localhost");
+ properties.put(TenantConstants.NAME_KEY, "Tenant localhost");
m_testContext.updateFactoryConfig(CoreConfigs.TENANT.getPid(),
properties);
- properties.put("id", "[email protected]");
- properties.put("name", "Tenant 127.0.0.1");
+ properties.put(TenantConstants.PID_KEY, "[email protected]");
+ properties.put(TenantConstants.NAME_KEY, "Tenant 127.0.0.1");
m_testContext.updateFactoryConfig(CoreConfigs.TENANT.getPid(),
properties);
UserAdmin userAdmin1 =
Modified:
trunk/amdatu-core/itest/tests/src/test/java/org/amdatu/core/itest/tests/tenant/MultiTenantTest.java
==============================================================================
---
trunk/amdatu-core/itest/tests/src/test/java/org/amdatu/core/itest/tests/tenant/MultiTenantTest.java
(original)
+++
trunk/amdatu-core/itest/tests/src/test/java/org/amdatu/core/itest/tests/tenant/MultiTenantTest.java
Mon Jan 30 16:43:34 2012
@@ -343,8 +343,8 @@
*/
private Properties createTenantConfiguration(String tenantId) {
Properties properties = new Properties();
- properties.put("id", tenantId);
- properties.put("name", "Tenant " + tenantId);
+ properties.put(TenantConstants.PID_KEY, tenantId);
+ properties.put(TenantConstants.NAME_KEY, "Tenant " + tenantId);
return properties;
}
Modified:
trunk/amdatu-core/tenant-factory/src/main/java/org/amdatu/core/tenant/factory/TenantServiceFactory.java
==============================================================================
---
trunk/amdatu-core/tenant-factory/src/main/java/org/amdatu/core/tenant/factory/TenantServiceFactory.java
(original)
+++
trunk/amdatu-core/tenant-factory/src/main/java/org/amdatu/core/tenant/factory/TenantServiceFactory.java
Mon Jan 30 16:43:34 2012
@@ -16,6 +16,8 @@
package org.amdatu.core.tenant.factory;
import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Iterator;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
@@ -58,17 +60,19 @@
/**
* @see
org.osgi.service.cm.ManagedServiceFactory#updated(java.lang.String,
java.util.Dictionary)
*/
- public void updated(String pid, Dictionary/* <String, String> */properties)
+ public void updated(String pid, Dictionary/* <String, Object> */properties)
throws ConfigurationException {
- String id = (String) properties.get(ID_KEY);
- String name = (String) properties.get(NAME_KEY);
+ String id = (String) properties.get(TenantConstants.PID_KEY);
+ String name = (String) properties.get(TenantConstants.NAME_KEY);
+ Tenant tenant = new TenantService(id, name);
Properties serviceProperties = new Properties();
- serviceProperties.put(TenantConstants.PID_KEY, id);
- serviceProperties.put(TenantConstants.NAME_KEY, name);
-
- Tenant tenant = new TenantService(id, name);
+ Enumeration<String> keys = properties.keys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ serviceProperties.put(key, properties.get(key));
+ }
Component component = m_dependencyManager.createComponent()
.setInterface(Tenant.class.getName(), serviceProperties)
Modified:
trunk/amdatu-web/itest/base/src/main/java/org/amdatu/web/itest/base/WebConfigs.java
==============================================================================
---
trunk/amdatu-web/itest/base/src/main/java/org/amdatu/web/itest/base/WebConfigs.java
(original)
+++
trunk/amdatu-web/itest/base/src/main/java/org/amdatu/web/itest/base/WebConfigs.java
Mon Jan 30 16:43:34 2012
@@ -25,12 +25,15 @@
import org.amdatu.core.itest.base.Fixture;
import org.amdatu.core.itest.base.ProvisionedConfig;
import org.amdatu.core.itest.base.TestContext;
+import org.amdatu.core.tenant.TenantConstants;
+import org.amdatu.web.tenantresolver.hostname.HostNameTenantResolverConstants;
/**
* Provides a mean to provision web-configurations.
*/
public enum WebConfigs implements ProvisionedConfig {
- HTTPSERVICE("org.osgi.service.http", httpCnf(), false);
+ HTTPSERVICE("org.osgi.service.http", httpCnf(), false),
+ TENANT("org.amdatu.core.tenant.factory", tenantCnf(), true);
private final String m_pid;
private final Properties m_properties;
@@ -77,14 +80,18 @@
return m_pid;
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see org.amdatu.core.itest.base.ProvisionedConfig#getProperties()
*/
public Properties getProperties() {
return m_properties;
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see org.amdatu.core.itest.base.ProvisionedConfig#isFactory()
*/
public boolean isFactory() {
@@ -101,4 +108,12 @@
properties.put("org.apache.felix.log.storeDebug", "false");
return properties;
}
+
+ private static Properties tenantCnf() {
+ Properties properties = new Properties();
+ properties.put(TenantConstants.PID_KEY, "Default_Web");
+ properties.put(TenantConstants.NAME_KEY, "Default Tenant");
+ properties.put(HostNameTenantResolverConstants.HOSTNAMES_KEY, new
String[] { "localhost" });
+ return properties;
+ }
}
\ No newline at end of file
Modified:
trunk/amdatu-web/itest/tests/src/test/java/org/amdatu/web/itest/tests/BenchmarkTest.java
==============================================================================
---
trunk/amdatu-web/itest/tests/src/test/java/org/amdatu/web/itest/tests/BenchmarkTest.java
(original)
+++
trunk/amdatu-web/itest/tests/src/test/java/org/amdatu/web/itest/tests/BenchmarkTest.java
Mon Jan 30 16:43:34 2012
@@ -81,7 +81,7 @@
@Before
public void setUp() throws Exception {
m_testContext = new TestContext(m_bundleContext);
- CoreConfigs.provisionAll(m_testContext);
+ CoreConfigs.provisionAllExcluding(m_testContext, CoreConfigs.TENANT);
WebConfigs.provisionAll(m_testContext);
m_port =
Modified: trunk/amdatu-web/tenantresolver-hostname/pom.xml
==============================================================================
--- trunk/amdatu-web/tenantresolver-hostname/pom.xml (original)
+++ trunk/amdatu-web/tenantresolver-hostname/pom.xml Mon Jan 30 16:43:34 2012
@@ -52,7 +52,7 @@
<configuration>
<instructions>
<Private-Package>
org.amdatu.web.tenantresolver.hostname.*</Private-Package>
-
<Bundle-Activator>org.amdatu.web.tenantresolver.hostname.osgi.HostnameTenantResolverActivator</Bundle-Activator>
+
<Bundle-Activator>org.amdatu.web.tenantresolver.hostname.osgi.Activator</Bundle-Activator>
<Bundle-SymbolicName>org.amdatu.web.tenantresolver.hostname</Bundle-SymbolicName>
<Export-Package>
org.amdatu.web.tenantresolver.hostname;version=1.0.0
Added:
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/HostNameTenantResolverConstants.java
==============================================================================
--- (empty file)
+++
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/HostNameTenantResolverConstants.java
Mon Jan 30 16:43:34 2012
@@ -0,0 +1,24 @@
+/*
+ * 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.web.tenantresolver.hostname;
+
+/**
+ * @author <a href="mailto:[email protected]">Amdatu Project
Team</a>
+ *
+ */
+public interface HostNameTenantResolverConstants {
+ String HOSTNAMES_KEY = "org.amdatu.web.hostnames";
+}
\ No newline at end of file
Copied:
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/osgi/Activator.java
(from r2025,
/trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/osgi/HostnameTenantResolverActivator.java)
==============================================================================
---
/trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/osgi/HostnameTenantResolverActivator.java
(original)
+++
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/osgi/Activator.java
Mon Jan 30 16:43:34 2012
@@ -18,8 +18,10 @@
import java.util.Dictionary;
import java.util.Hashtable;
+import org.amdatu.core.tenant.Tenant;
import org.amdatu.web.dispatcher.DispatchExtenderFilter;
import org.amdatu.web.dispatcher.DispatcherService;
+import
org.amdatu.web.tenantresolver.hostname.service.HostnameTenantMappingRegistry;
import
org.amdatu.web.tenantresolver.hostname.service.HostnameTenantResolverExtenderFilter;
import org.apache.felix.dm.DependencyActivatorBase;
import org.apache.felix.dm.DependencyManager;
@@ -33,22 +35,26 @@
* @author <a href="mailto:[email protected]">Amdatu Project
Team</a>
*
*/
-public final class HostnameTenantResolverActivator extends
DependencyActivatorBase {
+public final class Activator extends DependencyActivatorBase {
@Override
public void init(BundleContext context, DependencyManager manager) throws
Exception {
+ HostnameTenantMappingRegistry registry = new
HostnameTenantMappingRegistry();
+ HostnameTenantResolverExtenderFilter filter = new
HostnameTenantResolverExtenderFilter(registry);
+
Dictionary<String, Object> properties = new Hashtable<String,
Object>();
properties.put(Constants.SERVICE_RANKING, 1000);
properties.put(DispatcherService.PATTERN_KEY, ".*");
+
manager.add(
createComponent()
.setInterface(DispatchExtenderFilter.class.getName(),
properties)
- .setImplementation(HostnameTenantResolverExtenderFilter.class)
- // Guards filter lifecycle
+ .setImplementation(filter)
.add(createServiceDependency()
- .setService(DispatcherService.class)
- .setRequired(true))
+ .setService(Tenant.class)
+ .setRequired(false)
+ .setCallbacks(registry, "updateMapping", "updateMapping",
"updateMapping"))
.add(createServiceDependency()
.setService(LogService.class)
.setRequired(false))
Added:
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/service/HostnameTenantMappingRegistry.java
==============================================================================
--- (empty file)
+++
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/service/HostnameTenantMappingRegistry.java
Mon Jan 30 16:43:34 2012
@@ -0,0 +1,140 @@
+/*
+ * 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.web.tenantresolver.hostname.service;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.amdatu.core.tenant.Tenant;
+import org.amdatu.web.tenantresolver.hostname.HostNameTenantResolverConstants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+
+/**
+ *
+ * TODO document
+ * TODO check for duplicates
+ * TODO test
+ *
+ * @author <a href="mailto:[email protected]">Amdatu Project
Team</a>
+ *
+ */
+public final class HostnameTenantMappingRegistry {
+
+ // maps current mappings
+ private final Map<ServiceReference, HostNameTenantMapping>
m_referenceToMapping =
+ new HashMap<ServiceReference, HostNameTenantMapping>();
+
+ // maps fast hostname lookup
+ private final Map<String, HostNameTenantMapping> m_hostNameToTenantMapping
=
+ new HashMap<String, HostNameTenantMapping>();
+
+ private final ReentrantReadWriteLock m_lock = new ReentrantReadWriteLock();
+
+ private volatile LogService m_logService;
+
+ public Tenant getTenant(String hostname) {
+ m_lock.readLock().lock();
+ try {
+ HostNameTenantMapping mapping =
m_hostNameToTenantMapping.get(hostname);
+ if (mapping != null) {
+ return mapping.getTenant();
+ }
+ }
+ finally {
+ m_lock.readLock().unlock();
+ }
+ return null;
+ }
+
+ public void updateMapping(ServiceReference reference, Tenant tenant) {
+ HostNameTenantMapping newMapping = createMapping(reference, tenant);
+ HostNameTenantMapping oldMapping = null;
+ m_lock.writeLock().lock();
+ try {
+ if (newMapping == null) {
+ oldMapping = m_referenceToMapping.remove(reference);
+ }
+ else {
+ oldMapping = m_referenceToMapping.put(reference, newMapping);
+ }
+ if (oldMapping != null) {
+ removeMapping(oldMapping);
+ }
+ if (newMapping != null) {
+ addMapping(newMapping);
+ }
+ }
+ finally {
+ m_lock.writeLock().unlock();
+ }
+ }
+
+ private void removeMapping(HostNameTenantMapping mapping) {
+ Set<String> removeHostnames = new HashSet<String>();
+ for (Entry<String, HostNameTenantMapping> entry :
m_hostNameToTenantMapping.entrySet()) {
+ if (entry.getValue() == mapping) {
+ removeHostnames.add(entry.getKey());
+ }
+ }
+ for (String hostname : removeHostnames) {
+ m_hostNameToTenantMapping.remove(hostname);
+ }
+ }
+
+ private void addMapping(HostNameTenantMapping mapping) {
+ if (mapping.getHostNames() == null) {
+ return;
+ }
+ for (String hostname : mapping.getHostNames()) {
+ m_hostNameToTenantMapping.put(hostname, mapping);
+ }
+ }
+
+ private HostNameTenantMapping createMapping(ServiceReference reference,
Tenant tenant) {
+ if (tenant == null) {
+ return null;
+ }
+ return new HostNameTenantMapping(tenant, getHostnames(reference));
+ }
+
+ private String[] getHostnames(ServiceReference reference) {
+ return (String[])
reference.getProperty(HostNameTenantResolverConstants.HOSTNAMES_KEY);
+ }
+
+ class HostNameTenantMapping {
+
+ private final Tenant m_tenant;
+ private final String[] m_hostnames;
+
+ public HostNameTenantMapping(Tenant tenant, String[] hostnames) {
+ m_tenant = tenant;
+ m_hostnames = hostnames;
+ }
+
+ public Tenant getTenant() {
+ return m_tenant;
+ }
+
+ public String[] getHostNames() {
+ return m_hostnames;
+ }
+ }
+}
\ No newline at end of file
Modified:
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/service/HostnameTenantResolverExtenderFilter.java
==============================================================================
---
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/service/HostnameTenantResolverExtenderFilter.java
(original)
+++
trunk/amdatu-web/tenantresolver-hostname/src/main/java/org/amdatu/web/tenantresolver/hostname/service/HostnameTenantResolverExtenderFilter.java
Mon Jan 30 16:43:34 2012
@@ -23,15 +23,10 @@
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import org.amdatu.core.tenant.Tenant;
-import org.amdatu.core.tenant.TenantConstants;
import org.amdatu.web.dispatcher.DispatchExtenderFilter;
import org.amdatu.web.dispatcher.DispatcherService;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;
/**
@@ -43,8 +38,13 @@
*/
public final class HostnameTenantResolverExtenderFilter implements
DispatchExtenderFilter {
+ private final HostnameTenantMappingRegistry m_registry;
+
private volatile LogService m_logService;
- private volatile BundleContext m_bundleContext;
+
+ public HostnameTenantResolverExtenderFilter(HostnameTenantMappingRegistry
registry) {
+ m_registry = registry;
+ }
public void init(FilterConfig filterConfig) throws ServletException {
m_logService.log(LogService.LOG_DEBUG, "Initializing hostname base
tenantresolver filter");
@@ -57,30 +57,15 @@
public void doFilter(ServletRequest servletRequest, ServletResponse
servletResponse, FilterChain filterChain)
throws IOException, ServletException {
- final HttpServletRequest httpServletRequest = (HttpServletRequest)
servletRequest;
- final String serverName = httpServletRequest.getServerName();
- boolean resolved = false;
-
- if (serverName != null) {
- try {
- ServiceReference[] refs =
-
m_bundleContext.getServiceReferences(Tenant.class.getName(), "(" +
TenantConstants.PID_KEY
- + "hostname=" + serverName + ")");
- if (refs != null && refs.length > 0) {
- Tenant tenant = (Tenant)
m_bundleContext.getService(refs[0]);
- if (tenant != null) {
-
servletRequest.setAttribute(DispatcherService.TENANT_REQUESTCONTEXT_KEY,
tenant);
-
servletRequest.setAttribute(DispatcherService.TENANTID_REQUESTCONTEXT_KEY,
tenant.getId());
- resolved = true;
- }
- }
- }
- catch (InvalidSyntaxException e) {
- m_logService.log(LogService.LOG_ERROR, "Illegal filter in
servicelookup... should never happen!", e);
- throw new ServletException("Illegal filter in servicelookup...
should never happen!", e);
- }
+ HttpServletRequest httpServletRequest = (HttpServletRequest)
servletRequest;
+ String serverName = httpServletRequest.getServerName();
+
+ Tenant tenant = m_registry.getTenant(serverName);
+ if (tenant != null) {
+
servletRequest.setAttribute(DispatcherService.TENANT_REQUESTCONTEXT_KEY,
tenant);
+
servletRequest.setAttribute(DispatcherService.TENANTID_REQUESTCONTEXT_KEY,
tenant.getId());
}
- if (!resolved) {
+ else {
// No INF/WARN/ERR cause there may be multiple resolvers in play
m_logService.log(LogService.LOG_DEBUG, "Failed to resolved request
to available tenant for hostname: "
+ serverName);
_______________________________________________
Amdatu-commits mailing list
[email protected]
http://lists.amdatu.org/mailman/listinfo/amdatu-commits