View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.sql.config.spring;
17  
18  import com.google.common.base.Optional;
19  import com.google.common.collect.ImmutableList;
20  import com.google.common.collect.Lists;
21  import com.google.common.collect.Maps;
22  import org.apache.commons.lang3.StringUtils;
23  import org.kuali.common.util.metainf.model.MetaInfContext;
24  import org.kuali.common.util.metainf.model.MetaInfResource;
25  import org.kuali.common.util.metainf.model.MetaInfResourceLocationComparator;
26  import org.kuali.common.util.metainf.service.MetaInfUtils;
27  import org.kuali.common.util.metainf.spring.MetaInfConfigUtils;
28  import org.kuali.common.util.metainf.spring.MetaInfContextsConfig;
29  import org.kuali.common.util.metainf.spring.MetaInfDataLocation;
30  import org.kuali.common.util.metainf.spring.MetaInfDataType;
31  import org.kuali.common.util.metainf.spring.MetaInfExecutableConfig;
32  import org.kuali.common.util.metainf.spring.MetaInfGroup;
33  import org.kuali.common.util.nullify.NullUtils;
34  import org.kuali.common.util.project.ProjectUtils;
35  import org.kuali.common.util.project.model.Build;
36  import org.kuali.common.util.project.model.Project;
37  import org.kuali.common.util.project.spring.AutowiredProjectConfig;
38  import org.kuali.common.util.spring.SpringUtils;
39  import org.kuali.common.util.spring.env.EnvironmentService;
40  import org.kuali.common.util.spring.service.SpringServiceConfig;
41  import org.springframework.beans.factory.annotation.Autowired;
42  import org.springframework.context.annotation.Bean;
43  import org.springframework.context.annotation.Configuration;
44  import org.springframework.context.annotation.Import;
45  
46  import java.io.File;
47  import java.util.Comparator;
48  import java.util.List;
49  import java.util.Map;
50  
51  /**
52   * Defines the configuration for creating the sql property files that define how the database is created.
53   *
54   * @author Kuali Rice Team (rice.collab@kuali.org)
55   */
56  @Configuration
57  @Import({AutowiredProjectConfig.class, MetaInfExecutableConfig.class, SpringServiceConfig.class})
58  public class RiceSqlConfig implements MetaInfContextsConfig {
59  
60      private static final Boolean DEFAULT_GENERATE_RELATIVE_PATHS = Boolean.TRUE;
61      private static final String RELATIVE_KEY = MetaInfUtils.PROPERTY_PREFIX + ".sql.relative";
62      private static final String PREFIX = "sql";
63      private static final String DEFAULT_VENDORS = "mysql,oracle";
64      private static final String VENDORS_KEY = MetaInfUtils.PROPERTY_PREFIX + ".db.vendors";
65  
66      // All paths must have the hardcoded separator to be consistent for deployment
67      private static final String PATH_SEPARATOR = "/";
68  
69      private static final String INITIAL_SQL_PATH = "initial-sql" + PATH_SEPARATOR + "2.3.0";
70      private static final String UPGRADE_SQL_PATH = "upgrades" + PATH_SEPARATOR + "*";
71      private static final String SCHEMA_SQL_PATH = "rice-" + MetaInfGroup.SCHEMA.name().toLowerCase() + ".sql";
72      private static final String CONSTRAINTS_SQL_PATH = "rice-" + MetaInfGroup.CONSTRAINTS.name().toLowerCase() + ".sql";
73      private static final String ALL_SQL_PATH = "*.sql";
74  
75      /**
76       * The Spring environment.
77       */
78      @Autowired
79      EnvironmentService env;
80  
81      /**
82       * The Rice Maven project.
83       */
84      @Autowired
85      Project project;
86  
87      /**
88       * The build information.
89       */
90      @Autowired
91      Build build;
92  
93      /**
94       * {@inheritDoc}
95       *
96       * <p>
97       * All of the initial data (the data included in the {@code MetaInfGroup.SCHEMA}, {@code MetaInfGroup.CONSTRAINTS},
98       * or {@code MetaInfGroup.DATA} groups) needs to be added before the update data (the data included in the
99       * {@code MetaInfGroup.OTHER} group).
100      * </p>
101      */
102     @Override
103     @Bean
104     public List<MetaInfContext> metaInfContexts() {
105         List<MetaInfContext> metaInfContexts = Lists.newArrayList();
106 
107         List<MetaInfDataType> types = Lists.newArrayList(MetaInfDataType.BOOTSTRAP, MetaInfDataType.DEMO, MetaInfDataType.TEST);
108         List<String> vendors = SpringUtils.getNoneSensitiveListFromCSV(env, VENDORS_KEY, DEFAULT_VENDORS);
109         List<MetaInfGroup> groups = Lists.newArrayList(MetaInfGroup.SCHEMA, MetaInfGroup.DATA, MetaInfGroup.CONSTRAINTS);
110 
111         for (MetaInfDataType type : types) {
112             for (MetaInfDataLocation location : MetaInfDataLocation.values()) {
113                 for (String vendor : vendors) {
114                     for (MetaInfGroup group : groups) {
115                         List<MetaInfContext> contexts = getMetaInfContexts(group, INITIAL_SQL_PATH, vendor,
116                                 location, type);
117                         metaInfContexts.addAll(contexts);
118                     }
119                 }
120             }
121         }
122 
123         for (MetaInfDataType type : types) {
124             for (MetaInfDataLocation location : MetaInfDataLocation.values()) {
125                 for (String vendor : vendors) {
126                     List<MetaInfContext> contexts = getMetaInfContexts(MetaInfGroup.OTHER, UPGRADE_SQL_PATH, vendor,
127                             location, type);
128                     metaInfContexts.addAll(contexts);
129                 }
130             }
131         }
132 
133         return ImmutableList.copyOf(metaInfContexts);
134     }
135 
136     /**
137      * Creates a list of META-INF contexts for the given {@code group}, {@code qualifier}, {@code vendor},
138      * {@code location}, and {@code type}.
139      *
140      * @param group the group of the data to create the context for
141      * @param qualifier the prefix to add to the initial resource path
142      * @param vendor the database vendor to create the context for
143      * @param location the location of the data to create the context for
144      * @param type the type of data to create the context for
145      *
146      * @return a list of META-INF contexts
147      */
148     protected List<MetaInfContext> getMetaInfContexts(MetaInfGroup group, String qualifier, String vendor, MetaInfDataLocation location, MetaInfDataType type) {
149         List<MetaInfContext> metaInfContexts = Lists.newArrayList();
150 
151         File scanDir = build.getOutputDir();
152         String encoding = build.getEncoding();
153 
154         Comparator<MetaInfResource> comparator = new MetaInfResourceLocationComparator();
155 
156         String includesKey = MetaInfConfigUtils.getIncludesKey(group, PREFIX) + "." + vendor;
157         String excludesKey = MetaInfConfigUtils.getExcludesKey(group, PREFIX) + "." + vendor;
158 
159         Boolean relativePaths = env.getBoolean(RELATIVE_KEY, DEFAULT_GENERATE_RELATIVE_PATHS);
160 
161         List<String> pathQualifiers = MetaInfUtils.getQualifiers(scanDir, project, Lists.newArrayList(qualifier), Lists.<String> newArrayList());
162 
163         for (String pathQualifier : pathQualifiers) {
164             File outputFile = MetaInfUtils.getOutputFile(project, build, Optional.of(pathQualifier + PATH_SEPARATOR + vendor),
165                     Optional.of(location), Optional.of(type), group.name().toLowerCase());
166 
167             Map<MetaInfGroup, String> defaultIncludes = getDefaultIncludes(pathQualifier, vendor, location, type);
168             Map<MetaInfGroup, String> defaultExcludes = getDefaultExcludes(defaultIncludes);
169             List<String> includes = SpringUtils.getNoneSensitiveListFromCSV(env, includesKey, defaultIncludes.get(group));
170             List<String> excludes = SpringUtils.getNoneSensitiveListFromCSV(env, excludesKey, defaultExcludes.get(group));
171 
172             MetaInfContext context = MetaInfContext.builder(outputFile, encoding, scanDir).comparator(comparator)
173                     .includes(includes).excludes(excludes).relativePaths(relativePaths.booleanValue()).build();
174             metaInfContexts.add(context);
175         }
176 
177         return metaInfContexts;
178     }
179 
180     /**
181      * Generates the default mapping of included paths from the given {@code qualifier}, {@code vendor}, and
182      * {@code type}.
183      *
184      * @param qualifier the prefix to add to the initial resource path
185      * @param vendor the database vendor to include
186      * @param location the location of the data to include
187      * @param type the type of data to include
188      *
189      * @return the map of included paths
190      */
191     protected Map<MetaInfGroup, String> getDefaultIncludes(String qualifier, String vendor, MetaInfDataLocation location, MetaInfDataType type) {
192         Map<MetaInfGroup, String> defaultIncludes = Maps.newEnumMap(MetaInfGroup.class);
193 
194         String resourcePath = ProjectUtils.getResourcePath(project.getGroupId(), project.getArtifactId());
195         List<String> paths = Lists.newArrayList(resourcePath, qualifier, vendor, location.name().toLowerCase(), type.name().toLowerCase());
196         String value = StringUtils.join(paths, PATH_SEPARATOR);
197 
198         defaultIncludes.put(MetaInfGroup.SCHEMA, value + PATH_SEPARATOR + SCHEMA_SQL_PATH);
199         defaultIncludes.put(MetaInfGroup.DATA, value + PATH_SEPARATOR + ALL_SQL_PATH);
200         defaultIncludes.put(MetaInfGroup.CONSTRAINTS, value + PATH_SEPARATOR + CONSTRAINTS_SQL_PATH);
201         defaultIncludes.put(MetaInfGroup.OTHER, value + PATH_SEPARATOR + ALL_SQL_PATH);
202 
203         return defaultIncludes;
204     }
205 
206     /**
207      * Generates the default mapping of excluded paths from the {@code defaultIncludes} map.
208      *
209      * <p>
210      * Generally, nothing is excluded, but there is a special case with {@code MetaInfGroup.DATA} where it does not
211      * include either the {@code MetaInfGroup.SCHEMA} or {@code MetaInfGroup.CONSTRAINTS}.
212      * </p>
213      *
214      * @param defaultIncludes the map of included paths
215      *
216      * @return the map of excluded paths
217      */
218     protected Map<MetaInfGroup, String> getDefaultExcludes(Map<MetaInfGroup, String> defaultIncludes) {
219         Map<MetaInfGroup, String> defaultExcludes = Maps.newEnumMap(MetaInfGroup.class);
220 
221         List<String> dataExcludes = Lists.newArrayList(defaultIncludes.get(MetaInfGroup.SCHEMA), defaultIncludes.get(MetaInfGroup.CONSTRAINTS));
222 
223         defaultExcludes.put(MetaInfGroup.SCHEMA, NullUtils.NONE);
224         defaultExcludes.put(MetaInfGroup.DATA, StringUtils.join(dataExcludes, ","));
225         defaultExcludes.put(MetaInfGroup.CONSTRAINTS, NullUtils.NONE);
226         defaultExcludes.put(MetaInfGroup.OTHER, NullUtils.NONE);
227 
228         return defaultExcludes;
229     }
230 
231 }