METAMODEL-1099: Added ServiceLoader based registry of DataContexts.
authorKasper Sørensen <i.am.kasper.sorensen@gmail.com>
Mon, 11 Jul 2016 04:18:34 +0000 (21:18 -0700)
committerKasper Sørensen <i.am.kasper.sorensen@gmail.com>
Mon, 11 Jul 2016 04:18:34 +0000 (21:18 -0700)
Fixes #113

33 files changed:
CHANGES.md
core/src/main/java/org/apache/metamodel/ConnectionException.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/ClasspathResourceFactory.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/DataContextFactory.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/DataContextFactoryRegistry.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/DataContextFactoryRegistryImpl.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/DataContextProperties.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/DataContextPropertiesImpl.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/FileResourceFactory.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/InMemoryResourceFactory.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/ResourceFactory.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/ResourceFactoryRegistry.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/ResourceFactoryRegistryImpl.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/ResourceProperties.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/ResourcePropertiesImpl.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/SimpleResourceProperties.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/UnsupportedDataContextPropertiesException.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/UnsupportedResourcePropertiesException.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/factory/UrlResourceFactory.java [new file with mode: 0644]
core/src/main/java/org/apache/metamodel/util/ResourceUtils.java
core/src/main/resources/META-INF/services/org.apache.metamodel.factory.ResourceFactory [new file with mode: 0644]
core/src/test/java/org/apache/metamodel/factory/ResourceFactoryRegistryImplTest.java [new file with mode: 0644]
core/src/test/java/org/apache/metamodel/util/UrlResourceTest.java
csv/src/main/java/org/apache/metamodel/csv/CsvDataContextFactory.java [new file with mode: 0644]
csv/src/main/resources/META-INF/services/org.apache.metamodel.factory.DataContextFactory [new file with mode: 0644]
csv/src/test/java/org/apache/metamodel/csv/CsvDataContextFactoryTest.java [new file with mode: 0644]
hadoop/src/main/java/org/apache/metamodel/hadoop/HdfsResourceFactory.java [new file with mode: 0644]
hadoop/src/main/resources/META-INF/services/org.apache.metamodel.factory.ResourceFactory [new file with mode: 0644]
jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContextFactory.java [new file with mode: 0644]
jdbc/src/main/resources/META-INF/services/org.apache.metamodel.factory.DataContextFactory [new file with mode: 0644]
jdbc/src/test/java/org/apache/metamodel/jdbc/H2databaseTest.java
jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextFactoryTest.java [new file with mode: 0644]
pom.xml

index 817893a..3ff4ca0 100644 (file)
@@ -1,5 +1,6 @@
 ### Apache MetaModel 4.5.4 (work in progress)
 
+ * [METAMODEL-1099] - Created a new DataContextFactory SPI and a extensible registry of implementations based on ServiceLoader.
  * [METAMODEL-1088] - Add support for aliases in MongoDB.
  * [METAMODEL-1086] - Fixed encoding issue when CsvDataContext is instantiated with InputStream.
  * [METAMODEL-1094] - Added support for Apache Cassandra version 3.x.
diff --git a/core/src/main/java/org/apache/metamodel/ConnectionException.java b/core/src/main/java/org/apache/metamodel/ConnectionException.java
new file mode 100644 (file)
index 0000000..5379f09
--- /dev/null
@@ -0,0 +1,44 @@
+/**
+ * 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.metamodel;
+
+/**
+ * Specialized {@link MetaModelException} thrown to indicate that establishing
+ * the connection to the underlying data store of an {@link DataContext} failed.
+ */
+public class ConnectionException extends MetaModelException {
+
+    private static final long serialVersionUID = 1L;
+
+    public ConnectionException() {
+        super();
+    }
+
+    public ConnectionException(Exception cause) {
+        super(cause);
+    }
+
+    public ConnectionException(String message, Exception cause) {
+        super(message, cause);
+    }
+
+    public ConnectionException(String message) {
+        super(message);
+    }
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/ClasspathResourceFactory.java b/core/src/main/java/org/apache/metamodel/factory/ClasspathResourceFactory.java
new file mode 100644 (file)
index 0000000..526e5f3
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * 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.metamodel.factory;
+
+import org.apache.metamodel.util.ClasspathResource;
+import org.apache.metamodel.util.Resource;
+
+public class ClasspathResourceFactory implements ResourceFactory {
+
+    @Override
+    public boolean accepts(ResourceProperties properties) {
+        return "classpath".equals(properties.getUri().getScheme());
+    }
+
+    @Override
+    public Resource create(ResourceProperties properties) throws UnsupportedResourcePropertiesException {
+        assert accepts(properties);
+        return new ClasspathResource(properties.getUri().getPath());
+    }
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/DataContextFactory.java b/core/src/main/java/org/apache/metamodel/factory/DataContextFactory.java
new file mode 100644 (file)
index 0000000..b9f8e3e
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * 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.metamodel.factory;
+
+import org.apache.metamodel.ConnectionException;
+import org.apache.metamodel.DataContext;
+
+public interface DataContextFactory {
+
+    public boolean accepts(DataContextProperties properties, ResourceFactoryRegistry resourceFactoryRegistry);
+
+    public DataContext create(DataContextProperties properties, ResourceFactoryRegistry resourceFactoryRegistry)
+            throws UnsupportedDataContextPropertiesException, ConnectionException;
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/DataContextFactoryRegistry.java b/core/src/main/java/org/apache/metamodel/factory/DataContextFactoryRegistry.java
new file mode 100644 (file)
index 0000000..79af1ab
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * 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.metamodel.factory;
+
+import java.util.Collection;
+
+import org.apache.metamodel.ConnectionException;
+import org.apache.metamodel.DataContext;
+
+/**
+ * Represents a registry of {@link DataContextFactory} objects. This registry
+ * can be used to create {@link DataContext}s of varying types using the
+ * underlying factories.
+ */
+public interface DataContextFactoryRegistry {
+
+    public void addFactory(DataContextFactory factory);
+
+    public void clearFactories();
+
+    public Collection<DataContextFactory> getFactories();
+
+    public DataContext createDataContext(DataContextProperties properties)
+            throws UnsupportedDataContextPropertiesException, ConnectionException;
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/DataContextFactoryRegistryImpl.java b/core/src/main/java/org/apache/metamodel/factory/DataContextFactoryRegistryImpl.java
new file mode 100644 (file)
index 0000000..0dfb897
--- /dev/null
@@ -0,0 +1,84 @@
+/**
+ * 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.metamodel.factory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.ServiceLoader;
+
+import org.apache.metamodel.DataContext;
+
+public class DataContextFactoryRegistryImpl implements DataContextFactoryRegistry {
+
+    private static final DataContextFactoryRegistry DEFAULT_INSTANCE;
+
+    static {
+        final ResourceFactoryRegistry resourceFactoryRegistry = ResourceFactoryRegistryImpl.getDefaultInstance();
+        final DataContextFactoryRegistryImpl registry = new DataContextFactoryRegistryImpl(resourceFactoryRegistry);
+        registry.discoverFromClasspath();
+        DEFAULT_INSTANCE = registry;
+    }
+
+    public static DataContextFactoryRegistry getDefaultInstance() {
+        return DEFAULT_INSTANCE;
+    }
+
+    private final List<DataContextFactory> factories;
+    private final ResourceFactoryRegistry resourceFactoryRegistry;
+
+    public DataContextFactoryRegistryImpl(ResourceFactoryRegistry resourceFactoryRegistry) {
+        this.factories = new ArrayList<>();
+        this.resourceFactoryRegistry = resourceFactoryRegistry;
+    }
+
+    @Override
+    public void addFactory(DataContextFactory factory) {
+        factories.add(factory);
+    }
+
+    @Override
+    public void clearFactories() {
+        factories.clear();
+    }
+
+    @Override
+    public Collection<DataContextFactory> getFactories() {
+        return Collections.unmodifiableList(factories);
+    }
+
+    @Override
+    public DataContext createDataContext(DataContextProperties properties)
+            throws UnsupportedDataContextPropertiesException {
+        for (DataContextFactory factory : factories) {
+            if (factory.accepts(properties, resourceFactoryRegistry)) {
+                return factory.create(properties, resourceFactoryRegistry);
+            }
+        }
+        throw new UnsupportedDataContextPropertiesException();
+    }
+
+    public void discoverFromClasspath() {
+        final ServiceLoader<DataContextFactory> serviceLoader = ServiceLoader.load(DataContextFactory.class);
+        for (DataContextFactory factory : serviceLoader) {
+            addFactory(factory);
+        }
+    }
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/DataContextProperties.java b/core/src/main/java/org/apache/metamodel/factory/DataContextProperties.java
new file mode 100644 (file)
index 0000000..5251726
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * 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.metamodel.factory;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import org.apache.metamodel.DataContext;
+import org.apache.metamodel.schema.TableType;
+import org.apache.metamodel.util.SimpleTableDef;
+
+/**
+ * Represents the {@link Serializable} properties used to fully describe and
+ * construct a {@link DataContext}.
+ */
+public interface DataContextProperties extends Serializable {
+
+    /**
+     * Gets the type of {@link DataContext}, such as "csv" or "jdbc".
+     * 
+     * @return
+     */
+    String getDataContextType();
+
+    /**
+     * Gets all the properties represented as a {@link Map}. Note that any
+     * unstandardized properties may also be exposed via this map.
+     * 
+     * @return
+     */
+    Map<String, Object> toMap();
+
+    ResourceProperties getResourceProperties();
+
+    Integer getColumnNameLineNumber();
+
+    Boolean isSkipEmptyLines();
+
+    Boolean isSkipEmptyColumns();
+
+    String getEncoding();
+
+    Character getSeparatorChar();
+
+    Character getQuoteChar();
+
+    Character getEscapeChar();
+
+    Boolean isFailOnInconsistentRowLength();
+
+    Boolean isMultilineValuesEnabled();
+
+    TableType[] getTableTypes();
+
+    String getCatalogName();
+
+    String getUrl();
+
+    DataSource getDataSource();
+
+    String getUsername();
+
+    String getPassword();
+
+    String getDriverClassName();
+
+    String getHostname();
+
+    Integer getPort();
+
+    String getDatabaseName();
+
+    SimpleTableDef[] getTableDefs();
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/DataContextPropertiesImpl.java b/core/src/main/java/org/apache/metamodel/factory/DataContextPropertiesImpl.java
new file mode 100644 (file)
index 0000000..e66a811
--- /dev/null
@@ -0,0 +1,304 @@
+/**
+ * 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.metamodel.factory;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.sql.DataSource;
+
+import org.apache.metamodel.schema.TableType;
+import org.apache.metamodel.util.BooleanComparator;
+import org.apache.metamodel.util.NumberComparator;
+import org.apache.metamodel.util.SimpleTableDef;
+import org.apache.metamodel.util.SimpleTableDefParser;
+
+public class DataContextPropertiesImpl implements DataContextProperties {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String PROPERTY_USERNAME = "username";
+    public static final String PROPERTY_PASSWORD = "password";
+    public static final String PROPERTY_DRIVER_CLASS = "driver-class";
+    public static final String PROPERTY_HOSTNAME = "hostname";
+    public static final String PROPERTY_PORT = "port";
+    public static final String PROPERTY_DATABASE = "database";
+    public static final String PROPERTY_URL = "url";
+    public static final String PROPERTY_CATALOG_NAME = "catalog";
+    public static final String PROPERTY_RESOURCE_PROPERTIES = "resource";
+    public static final String PROPERTY_IS_MULTILINE_VALUES_ENABLED = "multiline-values";
+    public static final String PROPERTY_IS_FAIL_ON_INCONSISTENT_ROW_LENGTH = "fail-on-inconsistent-row-length";
+    public static final String PROPERTY_ESCAPE_CHAR = "escape-char";
+    public static final String PROPERTY_QUOTE_CHAR = "quote-char";
+    public static final String PROPERTY_SEPARATOR_CHAR = "separator-char";
+    public static final String PROPERTY_ENCODING = "encoding";
+    public static final String PROPERTY_SKIP_EMPTY_COLUMNS = "skip-empty-columns";
+    public static final String PROPERTY_SKIP_EMPTY_LINES = "skip-empty-lines";
+    public static final String PROPERTY_COLUMN_NAME_LINE_NUMBER = "column-name-line-number";
+    public static final String PROPERTY_DATA_CONTEXT_TYPE = "type";
+    public static final String PROPERTY_TABLE_TYPES = "table-types";
+    public static final String PROPERTY_DATA_SOURCE = "data-source";
+    public static final String PROPERTY_TABLE_DEFS = "table-defs";
+
+    private final Map<String, Object> map;
+
+    public DataContextPropertiesImpl() {
+        this(new HashMap<String, Object>());
+    }
+
+    public DataContextPropertiesImpl(Properties properties) {
+        this();
+        final Set<String> propertyNames = properties.stringPropertyNames();
+        for (String key : propertyNames) {
+            put(key, properties.get(key));
+        }
+    }
+
+    public DataContextPropertiesImpl(Map<String, Object> map) {
+        this.map = map;
+    }
+
+    public Object get(String key) {
+        return map.get(key);
+    }
+
+    public Object put(String key, Object value) {
+        return map.put(key, value);
+    }
+
+    public String getString(String key) {
+        final Object value = map.get(key);
+        if (value == null) {
+            return null;
+        }
+        return value.toString();
+    }
+
+    public Character getChar(String key) {
+        final String str = getString(key);
+        if (str == null || str.isEmpty()) {
+            return null;
+        }
+        return str.charAt(0);
+    }
+
+    public Integer getInt(String key) {
+        final Object obj = get(key);
+        if (obj == null) {
+            return null;
+        }
+        return NumberComparator.toNumber(obj).intValue();
+    }
+
+    private Boolean getBoolean(String key) {
+        final Object obj = get(key);
+        if (obj == null) {
+            return null;
+        }
+        return BooleanComparator.toBoolean(obj);
+    }
+
+    @SuppressWarnings("unchecked")
+    public Map<String, Object> getMap(String key) {
+        final Object obj = get(key);
+        if (obj == null) {
+            return null;
+        }
+        if (obj instanceof Map) {
+            return (Map<String, Object>) obj;
+        }
+        if (obj instanceof String) {
+            // TODO: Try parse as JSON
+        }
+        throw new IllegalStateException("Expected Map value for property '" + key + "'. Found " + obj.getClass()
+                .getName());
+    }
+
+    @Override
+    public String getDataContextType() {
+        return getString(PROPERTY_DATA_CONTEXT_TYPE);
+    }
+
+    public void setDataContextType(String type) {
+        put(PROPERTY_DATA_CONTEXT_TYPE, type);
+    }
+
+    @Override
+    public Map<String, Object> toMap() {
+        return map;
+    }
+
+    @Override
+    public ResourceProperties getResourceProperties() {
+        final Object resourceValue = get(PROPERTY_RESOURCE_PROPERTIES);
+        if (resourceValue == null) {
+            return null;
+        }
+        if (resourceValue instanceof String) {
+            return new SimpleResourceProperties((String) resourceValue);
+        }
+        if (resourceValue instanceof URI) {
+            return new SimpleResourceProperties((URI) resourceValue);
+        }
+        if (resourceValue instanceof Map) {
+            @SuppressWarnings("unchecked")
+            final Map<String, Object> resourceMap = (Map<String, Object>) resourceValue;
+            return new ResourcePropertiesImpl(resourceMap);
+        }
+        throw new IllegalStateException("Expected String, URI or Map value for property 'resource'. Found: "
+                + resourceValue);
+    }
+
+    @Override
+    public Integer getColumnNameLineNumber() {
+        return getInt(PROPERTY_COLUMN_NAME_LINE_NUMBER);
+    }
+
+    @Override
+    public Boolean isSkipEmptyLines() {
+        return getBoolean(PROPERTY_SKIP_EMPTY_LINES);
+    }
+
+    @Override
+    public Boolean isSkipEmptyColumns() {
+        return getBoolean(PROPERTY_SKIP_EMPTY_COLUMNS);
+    }
+
+    @Override
+    public String getEncoding() {
+        return getString(PROPERTY_ENCODING);
+    }
+
+    @Override
+    public Character getSeparatorChar() {
+        return getChar(PROPERTY_SEPARATOR_CHAR);
+    }
+
+    @Override
+    public Character getQuoteChar() {
+        return getChar(PROPERTY_QUOTE_CHAR);
+    }
+
+    @Override
+    public Character getEscapeChar() {
+        return getChar(PROPERTY_ESCAPE_CHAR);
+    }
+
+    @Override
+    public Boolean isFailOnInconsistentRowLength() {
+        return getBoolean(PROPERTY_IS_FAIL_ON_INCONSISTENT_ROW_LENGTH);
+    }
+
+    @Override
+    public Boolean isMultilineValuesEnabled() {
+        return getBoolean(PROPERTY_IS_MULTILINE_VALUES_ENABLED);
+    }
+
+    @Override
+    public TableType[] getTableTypes() {
+        final Object obj = get(PROPERTY_TABLE_TYPES);
+        if (obj == null) {
+            return null;
+        }
+        if (obj instanceof TableType[]) {
+            return (TableType[]) obj;
+        }
+        if (obj instanceof TableType) {
+            return new TableType[] { (TableType) obj };
+        }
+        if (obj instanceof String) {
+            String str = (String) obj;
+            if (str.startsWith("[") && str.endsWith("]")) {
+                str = str.substring(1, str.length() - 2);
+            }
+            final String[] tokens = str.split(",");
+            final TableType[] tableTypes = new TableType[tokens.length];
+            for (int i = 0; i < tableTypes.length; i++) {
+                tableTypes[i] = TableType.getTableType(tokens[i]);
+            }
+        }
+        throw new IllegalStateException("Expected TableType[] value for property '" + PROPERTY_TABLE_TYPES + "'. Found "
+                + obj.getClass().getName());
+    }
+
+    @Override
+    public String getCatalogName() {
+        return getString(PROPERTY_CATALOG_NAME);
+    }
+
+    @Override
+    public String getUrl() {
+        return getString(PROPERTY_URL);
+    }
+
+    @Override
+    public DataSource getDataSource() {
+        return (DataSource) get(PROPERTY_DATA_SOURCE);
+    }
+
+    @Override
+    public String getUsername() {
+        return getString(PROPERTY_USERNAME);
+    }
+
+    @Override
+    public String getPassword() {
+        return getString(PROPERTY_PASSWORD);
+    }
+
+    @Override
+    public String getDriverClassName() {
+        return getString(PROPERTY_DRIVER_CLASS);
+    }
+
+    @Override
+    public String getHostname() {
+        return getString(PROPERTY_HOSTNAME);
+    }
+
+    @Override
+    public Integer getPort() {
+        return getInt(PROPERTY_PORT);
+    }
+
+    @Override
+    public String getDatabaseName() {
+        return getString(PROPERTY_DATABASE);
+    }
+
+    @Override
+    public SimpleTableDef[] getTableDefs() {
+        final Object obj = get(PROPERTY_TABLE_DEFS);
+        if (obj instanceof SimpleTableDef[]) {
+            return (SimpleTableDef[]) obj;
+        }
+        if (obj instanceof SimpleTableDef) {
+            return new SimpleTableDef[] { (SimpleTableDef) obj };
+        }
+        if (obj instanceof String) {
+            return SimpleTableDefParser.parseTableDefs((String) obj);
+        }
+        throw new IllegalStateException("Expected SimpleTableDef[] value for property '" + PROPERTY_TABLE_DEFS
+                + "'. Found " + obj.getClass().getName());
+    }
+
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/FileResourceFactory.java b/core/src/main/java/org/apache/metamodel/factory/FileResourceFactory.java
new file mode 100644 (file)
index 0000000..8da1815
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * 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.metamodel.factory;
+
+import org.apache.metamodel.util.FileResource;
+import org.apache.metamodel.util.Resource;
+
+public class FileResourceFactory implements ResourceFactory {
+
+    @Override
+    public boolean accepts(ResourceProperties properties) {
+        final String scheme = properties.getUri().getScheme();
+        return scheme == null || "file".equals(scheme);
+    }
+
+    @Override
+    public Resource create(ResourceProperties properties) throws UnsupportedResourcePropertiesException {
+        assert accepts(properties);
+        final String path = properties.getUri().getPath();
+        return new FileResource(path);
+    }
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/InMemoryResourceFactory.java b/core/src/main/java/org/apache/metamodel/factory/InMemoryResourceFactory.java
new file mode 100644 (file)
index 0000000..8726441
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * 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.metamodel.factory;
+
+import org.apache.metamodel.util.InMemoryResource;
+import org.apache.metamodel.util.Resource;
+
+public class InMemoryResourceFactory implements ResourceFactory {
+
+    @Override
+    public boolean accepts(ResourceProperties properties) {
+        return "mem".equals(properties.getUri().getScheme());
+    }
+
+    @Override
+    public Resource create(ResourceProperties properties) throws UnsupportedResourcePropertiesException {
+        assert accepts(properties);
+        return new InMemoryResource(properties.getUri().getPath());
+    }
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/ResourceFactory.java b/core/src/main/java/org/apache/metamodel/factory/ResourceFactory.java
new file mode 100644 (file)
index 0000000..f128596
--- /dev/null
@@ -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.
+ */
+package org.apache.metamodel.factory;
+
+import org.apache.metamodel.util.Resource;
+
+public interface ResourceFactory {
+
+    public boolean accepts(ResourceProperties properties);
+
+    public Resource create(ResourceProperties properties) throws UnsupportedResourcePropertiesException;
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/ResourceFactoryRegistry.java b/core/src/main/java/org/apache/metamodel/factory/ResourceFactoryRegistry.java
new file mode 100644 (file)
index 0000000..2815787
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * 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.metamodel.factory;
+
+import java.util.Collection;
+
+import org.apache.metamodel.util.Resource;
+
+public interface ResourceFactoryRegistry {
+
+    public void addFactory(ResourceFactory factory);
+
+    public void clearFactories();
+
+    public Collection<ResourceFactory> getFactories();
+
+    public Resource createResource(ResourceProperties properties) throws UnsupportedResourcePropertiesException;
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/ResourceFactoryRegistryImpl.java b/core/src/main/java/org/apache/metamodel/factory/ResourceFactoryRegistryImpl.java
new file mode 100644 (file)
index 0000000..b8fae41
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * 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.metamodel.factory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.ServiceLoader;
+
+import org.apache.metamodel.util.Resource;
+
+public class ResourceFactoryRegistryImpl implements ResourceFactoryRegistry {
+
+    private static final ResourceFactoryRegistry DEFAULT_INSTANCE;
+
+    static {
+        final ResourceFactoryRegistryImpl registry = new ResourceFactoryRegistryImpl();
+        registry.discoverFromClasspath();
+        DEFAULT_INSTANCE = registry;
+    }
+
+    public static ResourceFactoryRegistry getDefaultInstance() {
+        return DEFAULT_INSTANCE;
+    }
+
+    private final List<ResourceFactory> factories;
+
+    public ResourceFactoryRegistryImpl() {
+        factories = new ArrayList<>();
+    }
+
+    @Override
+    public void addFactory(ResourceFactory factory) {
+        factories.add(factory);
+    }
+
+    @Override
+    public void clearFactories() {
+        factories.clear();
+    }
+
+    @Override
+    public Collection<ResourceFactory> getFactories() {
+        return Collections.unmodifiableList(factories);
+    }
+
+    @Override
+    public Resource createResource(ResourceProperties properties) {
+        for (ResourceFactory factory : factories) {
+            if (factory.accepts(properties)) {
+                return factory.create(properties);
+            }
+        }
+        throw new UnsupportedResourcePropertiesException();
+    }
+
+    public void discoverFromClasspath() {
+        final ServiceLoader<ResourceFactory> serviceLoader = ServiceLoader.load(ResourceFactory.class);
+        for (ResourceFactory factory : serviceLoader) {
+            addFactory(factory);
+        }
+    }
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/ResourceProperties.java b/core/src/main/java/org/apache/metamodel/factory/ResourceProperties.java
new file mode 100644 (file)
index 0000000..ab56036
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * 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.metamodel.factory;
+
+import java.io.Serializable;
+import java.net.URI;
+import java.util.Map;
+
+import org.apache.metamodel.util.Resource;
+
+/**
+ * Represents the {@link Serializable} properties used to fully describe and
+ * construct a {@link Resource}.
+ */
+public interface ResourceProperties extends Serializable {
+
+    URI getUri();
+
+    /**
+     * Gets all the properties represented as a {@link Map}. Note that any
+     * unstandardized properties may also be exposed via this map.
+     * 
+     * @return
+     */
+    Map<String, Object> toMap();
+
+    String getUsername();
+
+    String getPassword();
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/ResourcePropertiesImpl.java b/core/src/main/java/org/apache/metamodel/factory/ResourcePropertiesImpl.java
new file mode 100644 (file)
index 0000000..c27bd4f
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * 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.metamodel.factory;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ResourcePropertiesImpl implements ResourceProperties {
+
+    private static final long serialVersionUID = 1L;
+
+    private final Map<String, Object> map;
+
+    public ResourcePropertiesImpl() {
+        this(new HashMap<String, Object>());
+    }
+
+    public ResourcePropertiesImpl(Map<String, Object> map) {
+        this.map = map;
+    }
+
+    private String getString(String key) {
+        final Object value = map.get(key);
+        if (value == null) {
+            return null;
+        }
+        return value.toString();
+    }
+
+    @Override
+    public URI getUri() {
+        final Object uri = map.get("uri");
+        if (uri == null) {
+            return null;
+        }
+        if (uri instanceof URI) {
+            return (URI) uri;
+        }
+        return URI.create(uri.toString());
+    }
+
+    @Override
+    public Map<String, Object> toMap() {
+        return map;
+    }
+
+    @Override
+    public String getUsername() {
+        return getString("username");
+    }
+
+    @Override
+    public String getPassword() {
+        return getString("password");
+    }
+
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/SimpleResourceProperties.java b/core/src/main/java/org/apache/metamodel/factory/SimpleResourceProperties.java
new file mode 100644 (file)
index 0000000..7298454
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * 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.metamodel.factory;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SimpleResourceProperties implements ResourceProperties {
+
+    private static final long serialVersionUID = 1L;
+    private final URI uri;
+
+    public SimpleResourceProperties(URI uri) {
+        this.uri = uri;
+    }
+
+    public SimpleResourceProperties(String uri) {
+        this.uri = URI.create(uri);
+    }
+
+    @Override
+    public URI getUri() {
+        return uri;
+    }
+
+    @Override
+    public Map<String, Object> toMap() {
+        final Map<String, Object> map = new HashMap<>();
+        map.put("uri", uri);
+        return map;
+    }
+
+    @Override
+    public String getUsername() {
+        return null;
+    }
+
+    @Override
+    public String getPassword() {
+        return null;
+    }
+
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/UnsupportedDataContextPropertiesException.java b/core/src/main/java/org/apache/metamodel/factory/UnsupportedDataContextPropertiesException.java
new file mode 100644 (file)
index 0000000..225fa72
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * 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.metamodel.factory;
+
+import org.apache.metamodel.MetaModelException;
+
+/**
+ * Exception thrown if a {@link DataContextFactory} or
+ * {@link DataContextFactoryRegistry} is being invoked with
+ * {@link DataContextProperties} that are not supported by the implementation.
+ */
+public class UnsupportedDataContextPropertiesException extends MetaModelException {
+
+    private static final long serialVersionUID = 1L;
+
+    public UnsupportedDataContextPropertiesException() {
+        super();
+    }
+
+    public UnsupportedDataContextPropertiesException(Exception cause) {
+        super(cause);
+    }
+
+    public UnsupportedDataContextPropertiesException(String message, Exception cause) {
+        super(message, cause);
+    }
+
+    public UnsupportedDataContextPropertiesException(String message) {
+        super(message);
+    }
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/UnsupportedResourcePropertiesException.java b/core/src/main/java/org/apache/metamodel/factory/UnsupportedResourcePropertiesException.java
new file mode 100644 (file)
index 0000000..abf51bb
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * 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.metamodel.factory;
+
+import org.apache.metamodel.MetaModelException;
+
+public class UnsupportedResourcePropertiesException extends MetaModelException {
+
+    private static final long serialVersionUID = 1L;
+
+    public UnsupportedResourcePropertiesException() {
+        super();
+    }
+
+    public UnsupportedResourcePropertiesException(Exception cause) {
+        super(cause);
+    }
+
+    public UnsupportedResourcePropertiesException(String message, Exception cause) {
+        super(message, cause);
+    }
+
+    public UnsupportedResourcePropertiesException(String message) {
+        super(message);
+    }
+
+}
diff --git a/core/src/main/java/org/apache/metamodel/factory/UrlResourceFactory.java b/core/src/main/java/org/apache/metamodel/factory/UrlResourceFactory.java
new file mode 100644 (file)
index 0000000..fbca8f1
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * 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.metamodel.factory;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+
+import org.apache.metamodel.util.Resource;
+import org.apache.metamodel.util.UrlResource;
+
+public class UrlResourceFactory implements ResourceFactory {
+
+    @Override
+    public boolean accepts(ResourceProperties properties) {
+        final URI uri = properties.getUri();
+        switch (uri.getScheme()) {
+        case "http":
+        case "https":
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Resource create(ResourceProperties properties) throws UnsupportedResourcePropertiesException {
+        try {
+            return new UrlResource(properties.getUri().toURL());
+        } catch (MalformedURLException e) {
+            throw new UnsupportedResourcePropertiesException(e);
+        }
+    }
+}
index ad3426a..9e2faea 100644 (file)
  */
 package org.apache.metamodel.util;
 
+import java.net.URI;
+
+import org.apache.metamodel.factory.ResourceFactoryRegistryImpl;
+import org.apache.metamodel.factory.ResourceProperties;
+import org.apache.metamodel.factory.SimpleResourceProperties;
+import org.apache.metamodel.factory.UnsupportedResourcePropertiesException;
+
 /**
  * Static utility methods for handling {@link Resource}s.
  */
 public class ResourceUtils {
 
     /**
+     * Creates a Resource based on a URI
+     * 
+     * @param uri
+     * @return
+     * @throws UnsupportedResourcePropertiesException
+     *             if the scheme or other part of the URI is unsupported.
+     */
+    public static Resource toResource(URI uri) throws UnsupportedResourcePropertiesException {
+        return toResource(new SimpleResourceProperties(uri));
+    }
+
+    /**
+     * Creates a Resource based on a path or URI (represented by a String)
+     * 
+     * @param uri
+     * @return
+     * @throws UnsupportedResourcePropertiesException
+     *             if the scheme or other part of the string is unsupported.
+     */
+    public static Resource toResource(String uri) throws UnsupportedResourcePropertiesException {
+        return toResource(new SimpleResourceProperties(uri));
+    }
+
+    /**
+     * Creates a Resource based on the {@link ResourceProperties} definition.
+     * 
+     * @param resourceProperties
+     * @return
+     * @throws UnsupportedResourcePropertiesException
+     *             if the provided properties cannot be handled in creation of a
+     *             resource.
+     */
+    public static Resource toResource(ResourceProperties resourceProperties)
+            throws UnsupportedResourcePropertiesException {
+        return ResourceFactoryRegistryImpl.getDefaultInstance().createResource(resourceProperties);
+    }
+
+    /**
      * Gets the parent name of a resource. For example, if the resource's
      * qualified path is /foo/bar/baz, this method will return "bar".
      * 
diff --git a/core/src/main/resources/META-INF/services/org.apache.metamodel.factory.ResourceFactory b/core/src/main/resources/META-INF/services/org.apache.metamodel.factory.ResourceFactory
new file mode 100644 (file)
index 0000000..5bb0b9e
--- /dev/null
@@ -0,0 +1,4 @@
+org.apache.metamodel.factory.FileResourceFactory
+org.apache.metamodel.factory.ClasspathResourceFactory
+org.apache.metamodel.factory.UrlResourceFactory
+org.apache.metamodel.factory.InMemoryResourceFactory
diff --git a/core/src/test/java/org/apache/metamodel/factory/ResourceFactoryRegistryImplTest.java b/core/src/test/java/org/apache/metamodel/factory/ResourceFactoryRegistryImplTest.java
new file mode 100644 (file)
index 0000000..0122253
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ * 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.metamodel.factory;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.apache.metamodel.util.ClasspathResource;
+import org.apache.metamodel.util.InMemoryResource;
+import org.apache.metamodel.util.Resource;
+import org.apache.metamodel.util.UrlResource;
+import org.junit.Test;
+
+public class ResourceFactoryRegistryImplTest {
+
+    private final ResourceFactoryRegistry registry = ResourceFactoryRegistryImpl.getDefaultInstance();
+
+    @Test
+    public void testGetQualifiedFileResource() throws Exception {
+        final File file = new File("src/test/resources/unicode-text-utf8.txt");
+        final Resource res = registry.createResource(new SimpleResourceProperties("file:///" + file.getAbsolutePath()));
+        assertTrue(res.isExists());
+        assertEquals("unicode-text-utf8.txt", res.getName());
+    }
+
+    @Test
+    public void testGetUnqualifiedRelativeFileResource() throws Exception {
+        final Resource res = registry.createResource(new SimpleResourceProperties(
+                "src/test/resources/unicode-text-utf8.txt"));
+        assertTrue(res.isExists());
+        assertEquals("unicode-text-utf8.txt", res.getName());
+    }
+
+    @Test
+    public void testGetInMemoryResource() throws Exception {
+        final Resource res = registry.createResource(new SimpleResourceProperties("mem:///foo.bar.txt"));
+        assertTrue(res instanceof InMemoryResource);
+    }
+
+    @Test
+    public void testGetClasspathResource() throws Exception {
+        final Resource res = registry.createResource(new SimpleResourceProperties("classpath:///folder/foo"));
+        assertTrue(res.isExists());
+        assertTrue(res instanceof ClasspathResource);
+    }
+
+    @Test
+    public void testGetUrlResource() throws Exception {
+        final Resource res = registry.createResource(new SimpleResourceProperties(
+                "http://metamodel.apache.org/robots.txt"));
+        assertTrue(res.isExists());
+        assertTrue(res instanceof UrlResource);
+    }
+}
index 8478253..8054d8d 100644 (file)
@@ -23,10 +23,10 @@ import junit.framework.TestCase;
 public class UrlResourceTest extends TestCase {
 
     public void testGetName() throws Exception {
-        UrlResource resource = new UrlResource("http://eobjects.org/foo.txt");
-        assertEquals("foo.txt", resource.getName());
+        UrlResource resource = new UrlResource("http://metamodel.apache.org/robots.txt");
+        assertEquals("robots.txt", resource.getName());
         
-        resource = new UrlResource("http://eobjects.org/");
-        assertEquals("http://eobjects.org/", resource.getName());
+        resource = new UrlResource("http://metamodel.apache.org/");
+        assertEquals("http://metamodel.apache.org/", resource.getName());
     }
 }
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvDataContextFactory.java b/csv/src/main/java/org/apache/metamodel/csv/CsvDataContextFactory.java
new file mode 100644 (file)
index 0000000..105fd5b
--- /dev/null
@@ -0,0 +1,73 @@
+/**
+ * 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.metamodel.csv;
+
+import org.apache.metamodel.DataContext;
+import org.apache.metamodel.factory.DataContextFactory;
+import org.apache.metamodel.factory.DataContextProperties;
+import org.apache.metamodel.factory.ResourceFactoryRegistry;
+import org.apache.metamodel.util.FileHelper;
+import org.apache.metamodel.util.Resource;
+
+public class CsvDataContextFactory implements DataContextFactory {
+
+    public static final String PROPERTY_TYPE = "csv";
+
+    @Override
+    public boolean accepts(DataContextProperties properties, ResourceFactoryRegistry resourceFactoryRegistry) {
+        return PROPERTY_TYPE.equals(properties.getDataContextType());
+    }
+
+    @Override
+    public DataContext create(DataContextProperties properties, ResourceFactoryRegistry resourceFactoryRegistry) {
+        assert accepts(properties, resourceFactoryRegistry);
+
+        final Resource resource = resourceFactoryRegistry.createResource(properties.getResourceProperties());
+
+        final int columnNameLineNumber = getInt(properties.getColumnNameLineNumber(),
+                CsvConfiguration.DEFAULT_COLUMN_NAME_LINE);
+        final String encoding = getString(properties.getEncoding(), FileHelper.DEFAULT_ENCODING);
+        final char separatorChar = getChar(properties.getSeparatorChar(), CsvConfiguration.DEFAULT_SEPARATOR_CHAR);
+        final char quoteChar = getChar(properties.getQuoteChar(), CsvConfiguration.DEFAULT_QUOTE_CHAR);
+        final char escapeChar = getChar(properties.getEscapeChar(), CsvConfiguration.DEFAULT_ESCAPE_CHAR);
+        final boolean failOnInconsistentRowLength = getBoolean(properties.isFailOnInconsistentRowLength(), false);
+        final boolean multilineValuesEnabled = getBoolean(properties.isMultilineValuesEnabled(), true);
+
+        final CsvConfiguration configuration = new CsvConfiguration(columnNameLineNumber, encoding, separatorChar,
+                quoteChar, escapeChar, failOnInconsistentRowLength, multilineValuesEnabled);
+        return new CsvDataContext(resource, configuration);
+    }
+
+    private String getString(String value, String ifNull) {
+        return value == null ? ifNull : value;
+    }
+
+    private int getInt(Integer value, int ifNull) {
+        return value == null ? ifNull : value;
+    }
+
+    private boolean getBoolean(Boolean value, boolean ifNull) {
+        return value == null ? ifNull : value;
+    }
+
+    private char getChar(Character value, char ifNull) {
+        return value == null ? ifNull : value;
+    }
+
+}
diff --git a/csv/src/main/resources/META-INF/services/org.apache.metamodel.factory.DataContextFactory b/csv/src/main/resources/META-INF/services/org.apache.metamodel.factory.DataContextFactory
new file mode 100644 (file)
index 0000000..28857ad
--- /dev/null
@@ -0,0 +1 @@
+org.apache.metamodel.csv.CsvDataContextFactory
\ No newline at end of file
diff --git a/csv/src/test/java/org/apache/metamodel/csv/CsvDataContextFactoryTest.java b/csv/src/test/java/org/apache/metamodel/csv/CsvDataContextFactoryTest.java
new file mode 100644 (file)
index 0000000..bd92a73
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * 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.metamodel.csv;
+
+import java.util.Collection;
+
+import org.apache.metamodel.factory.DataContextFactory;
+import org.apache.metamodel.factory.DataContextFactoryRegistryImpl;
+import org.apache.metamodel.factory.DataContextPropertiesImpl;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CsvDataContextFactoryTest {
+
+    @Test
+    public void testDiscovery() throws Exception {
+        final Collection<DataContextFactory> factories = DataContextFactoryRegistryImpl.getDefaultInstance()
+                .getFactories();
+
+        boolean found = false;
+        for (DataContextFactory factory : factories) {
+            if (factory instanceof CsvDataContextFactory) {
+                found = true;
+                break;
+            }
+        }
+
+        if (!found) {
+            Assert.fail("Expected to find CsvDataContextFactory. Found: " + factories);
+        }
+    }
+
+    @Test
+    public void testCreateDataContext() throws Exception {
+        final DataContextPropertiesImpl properties = new DataContextPropertiesImpl();
+        properties.put("type", "csv");
+        properties.put("resource", "src/test/resources/csv_people.csv");
+
+        DataContextFactoryRegistryImpl.getDefaultInstance().createDataContext(properties);
+    }
+}
diff --git a/hadoop/src/main/java/org/apache/metamodel/hadoop/HdfsResourceFactory.java b/hadoop/src/main/java/org/apache/metamodel/hadoop/HdfsResourceFactory.java
new file mode 100644 (file)
index 0000000..767937d
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * 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.metamodel.hadoop;
+
+import org.apache.metamodel.factory.ResourceFactory;
+import org.apache.metamodel.factory.ResourceProperties;
+import org.apache.metamodel.factory.UnsupportedResourcePropertiesException;
+import org.apache.metamodel.util.HdfsResource;
+import org.apache.metamodel.util.Resource;
+
+public class HdfsResourceFactory implements ResourceFactory {
+
+    /**
+     * Property that can be used
+     */
+    public static final String PROPERTY_HADOOP_CONF_DIR = "hadoop-conf-dir";
+
+    @Override
+    public boolean accepts(ResourceProperties properties) {
+        return "hdfs".equals(properties.getUri().getScheme());
+    }
+
+    @Override
+    public Resource create(ResourceProperties properties) throws UnsupportedResourcePropertiesException {
+        final Object hadoopConfDirProperty = properties.toMap().get(PROPERTY_HADOOP_CONF_DIR);
+        final String hadoopConfDir = hadoopConfDirProperty == null ? null : hadoopConfDirProperty.toString();
+        return new HdfsResource(properties.getUri().toString(), hadoopConfDir);
+    }
+
+}
diff --git a/hadoop/src/main/resources/META-INF/services/org.apache.metamodel.factory.ResourceFactory b/hadoop/src/main/resources/META-INF/services/org.apache.metamodel.factory.ResourceFactory
new file mode 100644 (file)
index 0000000..a66dba0
--- /dev/null
@@ -0,0 +1 @@
+org.apache.metamodel.hadoop.HdfsResourceFactory
diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContextFactory.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContextFactory.java
new file mode 100644 (file)
index 0000000..2dfe81d
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * 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.metamodel.jdbc;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+import javax.sql.DataSource;
+
+import org.apache.metamodel.ConnectionException;
+import org.apache.metamodel.DataContext;
+import org.apache.metamodel.factory.DataContextFactory;
+import org.apache.metamodel.factory.DataContextProperties;
+import org.apache.metamodel.factory.ResourceFactoryRegistry;
+import org.apache.metamodel.schema.TableType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JdbcDataContextFactory implements DataContextFactory {
+
+    private static final Logger logger = LoggerFactory.getLogger(JdbcDataContextFactory.class);
+
+    public static final String PROPERTY_TYPE = "jdbc";
+
+    @Override
+    public boolean accepts(DataContextProperties properties, ResourceFactoryRegistry resourceFactoryRegistry) {
+        return PROPERTY_TYPE.equals(properties.getDataContextType());
+    }
+
+    @Override
+    public DataContext create(DataContextProperties properties, ResourceFactoryRegistry resourceFactoryRegistry)
+            throws ConnectionException {
+        final String driverClassName = properties.getDriverClassName();
+        if (driverClassName != null) {
+            try {
+                Class.forName(driverClassName);
+            } catch (ClassNotFoundException e) {
+                logger.warn("Failed to initialize driver class: {}", driverClassName, e);
+            }
+        }
+
+        final TableType[] tableTypes = properties.getTableTypes() == null ? TableType.DEFAULT_TABLE_TYPES
+                : properties.getTableTypes();
+        final String catalogName = properties.getCatalogName();
+
+        final DataSource dataSource = properties.getDataSource();
+        if (dataSource != null) {
+            return new JdbcDataContext(dataSource, tableTypes, catalogName);
+        }
+
+        final String url = properties.getUrl();
+        final String username = properties.getUsername();
+        final String password = properties.getPassword();
+
+        final Connection connection;
+        try {
+            if (username != null) {
+                connection = DriverManager.getConnection(url, username, password);
+            } else {
+                connection = DriverManager.getConnection(url);
+            }
+        } catch (SQLException e) {
+            throw new ConnectionException("Failed to open JDBC connection from URL: " + url, e);
+        }
+
+        return new JdbcDataContext(connection, tableTypes, catalogName);
+    }
+
+}
diff --git a/jdbc/src/main/resources/META-INF/services/org.apache.metamodel.factory.DataContextFactory b/jdbc/src/main/resources/META-INF/services/org.apache.metamodel.factory.DataContextFactory
new file mode 100644 (file)
index 0000000..c7c4772
--- /dev/null
@@ -0,0 +1 @@
+org.apache.metamodel.jdbc.JdbcDataContextFactory
\ No newline at end of file
index 6560247..6e21ae5 100644 (file)
@@ -26,8 +26,6 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
-import junit.framework.TestCase;
-
 import org.apache.metamodel.UpdateCallback;
 import org.apache.metamodel.UpdateScript;
 import org.apache.metamodel.create.CreateTable;
@@ -48,10 +46,15 @@ import org.apache.metamodel.schema.Table;
 import org.apache.metamodel.update.Update;
 import org.apache.metamodel.util.MutableRef;
 
+import junit.framework.TestCase;
+
 /**
  * Test case that tests interaction with the H2 embedded database
  */
 public class H2databaseTest extends TestCase {
+    
+    public static final String DRIVER_CLASS = "org.h2.Driver";
+    public static final String URL_MEMORY_DATABASE = "jdbc:h2:mem:";
 
     private final String[] FIRST_NAMES = { "Suzy", "Barbara", "John", "Ken", "Billy", "Larry", "Joe", "Margareth", "Bobby",
             "Elizabeth" };
@@ -62,8 +65,8 @@ public class H2databaseTest extends TestCase {
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        Class.forName("org.h2.Driver");
-        conn = DriverManager.getConnection("jdbc:h2:mem:");
+        Class.forName(DRIVER_CLASS);
+        conn = DriverManager.getConnection(URL_MEMORY_DATABASE);
     }
 
     @Override
diff --git a/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextFactoryTest.java b/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextFactoryTest.java
new file mode 100644 (file)
index 0000000..9511164
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * 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.metamodel.jdbc;
+
+import static org.junit.Assert.assertTrue;
+
+import org.apache.metamodel.DataContext;
+import org.apache.metamodel.factory.DataContextFactoryRegistryImpl;
+import org.apache.metamodel.factory.DataContextPropertiesImpl;
+import org.apache.metamodel.jdbc.dialects.H2QueryRewriter;
+import org.junit.Test;
+
+public class JdbcDataContextFactoryTest {
+
+    @Test
+    public void testCreateDataContext() throws Exception {
+        final DataContextPropertiesImpl properties = new DataContextPropertiesImpl();
+        properties.setDataContextType("jdbc");
+        properties.put("driver-class", H2databaseTest.DRIVER_CLASS);
+        properties.put("url", H2databaseTest.URL_MEMORY_DATABASE);
+
+        final DataContext dataContext = DataContextFactoryRegistryImpl.getDefaultInstance().createDataContext(
+                properties);
+        assertTrue(dataContext instanceof JdbcDataContext);
+
+        final JdbcDataContext jdbcDataContext = (JdbcDataContext) dataContext;
+        assertTrue(jdbcDataContext.getQueryRewriter() instanceof H2QueryRewriter);
+    }
+}
diff --git a/pom.xml b/pom.xml
index dad89f2..95ed697 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -376,6 +376,7 @@ under the License.
                                                        <exclude>**/src/assembly/metamodel-packaged-assembly-descriptor.xml</exclude>
                                                        <exclude>**/.gitignore/**</exclude>
                                                        <exclude>.git/**</exclude>
+                                                       <exclude>**/src/main/resources/META-INF/services/**</exclude>
                                                        <exclude>**/src/test/resources/**</exclude>
                                                        <exclude>**/src/site/**</exclude>
                                                        <exclude>**/.project</exclude>