001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.schematic; 017 018import java.io.InputStream; 019import java.util.ArrayList; 020import java.util.List; 021import java.util.Objects; 022import java.util.ServiceLoader; 023import java.util.stream.StreamSupport; 024import org.modeshape.schematic.document.Document; 025import org.modeshape.schematic.document.Json; 026import org.modeshape.schematic.document.ParsingException; 027import org.modeshape.schematic.internal.InMemorySchemaLibrary; 028 029public class Schematic extends DocumentFactory { 030 031 public static final String TYPE_FIELD = "type"; 032 033 /** 034 * Returns a DB with reads the given input stream as a configuration document {@code Document}. This document is 035 * expected to contain a {@link Schematic#TYPE_FIELD type field} to indicate the type 036 * of DB. 037 * 038 * @see #getDb(Document, ClassLoader) 039 * @throws ParsingException if the given input stream is not a valid JSON document 040 */ 041 public static <T extends SchematicDb> T getDb(InputStream configInputStream) throws ParsingException, RuntimeException { 042 Document document = Json.read(configInputStream).withVariablesReplacedWithSystemProperties(); 043 return getDb(document, Schematic.class.getClassLoader()); 044 } 045 046 /** 047 * Returns a DB with the given configuration document {@code Document}. This document is expected to contain 048 * a {@link Schematic#TYPE_FIELD type field} to indicate the type of DB. 049 * 050 * @see #getDb(Document, ClassLoader) 051 */ 052 public static <T extends SchematicDb> T getDb(Document document) throws RuntimeException { 053 return getDb(document, Schematic.class.getClassLoader()); 054 } 055 056 /** 057 * Returns a DB with the given type and configuration document, by delegating to all the available {@link SchematicDbProvider} 058 * services. This document is expected to contain a {@link Schematic#TYPE_FIELD type field} to indicate the type 059 * 060 * @param document a {@link Document} containing the configuration of a particular DB type; may not be null 061 * @param cl a {@link ClassLoader} instance to be used when searching for available DB provider. 062 * @return a {@link SchematicDb} instance with the given alias, never {@code null} 063 * @throws RuntimeException if a DB with the given alias cannot be found or it fails during initialization 064 */ 065 @SuppressWarnings( { "unchecked", "rawtypes" } ) 066 public static <T extends SchematicDb> T getDb(Document document, ClassLoader cl) throws RuntimeException { 067 String type = document.getString(TYPE_FIELD); 068 if (type == null) { 069 throw new IllegalArgumentException("The configuration document '" + document + "' does not contain a '" + TYPE_FIELD + "' field"); 070 } 071 ServiceLoader<SchematicDbProvider> providers = ServiceLoader.load(SchematicDbProvider.class, cl); 072 List<RuntimeException> raisedExceptions = new ArrayList<>(); 073 return (T) StreamSupport.stream(providers.spliterator(), false) 074 .map(provider -> getDbFromProvider(type, document, raisedExceptions, provider)) 075 .filter(Objects::nonNull) 076 .findFirst() 077 .orElseThrow(() -> { 078 if (!raisedExceptions.isEmpty()) { 079 return raisedExceptions.get(0); 080 } else { 081 return new RuntimeException( 082 "None of the existing persistence providers could return a Schematic DB with type '" + type + "'"); 083 } 084 }); 085 } 086 087 private static SchematicDb getDbFromProvider(String alias, Document document, 088 List<RuntimeException> raisedExceptions, 089 SchematicDbProvider<?> provider) { 090 try { 091 return provider.getDB(alias, document); 092 } catch (RuntimeException re) { 093 raisedExceptions.add(re); 094 return null; 095 } catch (Exception e) { 096 raisedExceptions.add(new RuntimeException(e)); 097 return null; 098 } 099 } 100 101 /** 102 * Create an in-memory schema library. 103 * 104 * @return the empty, in-memory schema library 105 */ 106 public static SchemaLibrary createSchemaLibrary() { 107 return new InMemorySchemaLibrary("In-memory schema library"); 108 } 109 110 /** 111 * Create an in-memory schema library. 112 * 113 * @param name the name of the library; may be null if a default name is to be used 114 * @return the empty, in-memory schema library 115 */ 116 public static SchemaLibrary createSchemaLibrary( String name ) { 117 return new InMemorySchemaLibrary(name != null ? name : "In-memory schema library"); 118 } 119}