001/* 002 * Copyright 2006-2018 the original author or authors. 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 * https://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 */ 016 017package org.springframework.batch.test; 018 019import java.io.IOException; 020import java.net.URI; 021import java.nio.charset.StandardCharsets; 022import java.nio.file.FileSystemNotFoundException; 023import java.nio.file.FileSystems; 024import java.nio.file.Files; 025import java.nio.file.Paths; 026import java.util.Collections; 027import java.util.List; 028 029import javax.sql.DataSource; 030 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.springframework.beans.factory.BeanInitializationException; 034import org.springframework.beans.factory.DisposableBean; 035import org.springframework.beans.factory.InitializingBean; 036import org.springframework.context.support.ClassPathXmlApplicationContext; 037import org.springframework.core.io.Resource; 038import org.springframework.dao.DataAccessException; 039import org.springframework.jdbc.core.JdbcTemplate; 040import org.springframework.jdbc.datasource.DataSourceTransactionManager; 041import org.springframework.transaction.support.TransactionCallback; 042import org.springframework.transaction.support.TransactionTemplate; 043import org.springframework.util.Assert; 044import org.springframework.util.ClassUtils; 045import org.springframework.util.StringUtils; 046 047/** 048 * Wrapper for a {@link DataSource} that can run scripts on start up and shut 049 * down. Use as a bean definition <br> 050 * 051 * Run this class to initialize a database in a running server process. 052 * Make sure the server is running first by launching the "hsql-server" from the 053 * <code>hsql.server</code> project. Then you can right click in Eclipse and 054 * Run As -> Java Application. Do the same any time you want to wipe the 055 * database and start again. 056 * 057 * @author Dave Syer 058 * @author Drummond Dawson 059 * @author Mahmoud Ben Hassine 060 * 061 */ 062public class DataSourceInitializer implements InitializingBean, DisposableBean { 063 064 private static final Log logger = LogFactory.getLog(DataSourceInitializer.class); 065 066 private Resource[] initScripts; 067 068 private Resource[] destroyScripts; 069 070 private DataSource dataSource; 071 072 private boolean ignoreFailedDrop = true; 073 074 private boolean initialized = false; 075 076 /** 077 * Main method as convenient entry point. 078 * 079 * @param args arguments to be passed to main. 080 */ 081 @SuppressWarnings("resource") 082 public static void main(String... args) { 083 new ClassPathXmlApplicationContext(ClassUtils.addResourcePathToPackagePath(DataSourceInitializer.class, 084 DataSourceInitializer.class.getSimpleName() + "-context.xml")); 085 } 086 087 @Override 088 public void destroy() { 089 if (this.destroyScripts == null) { 090 return; 091 } 092 for (Resource destroyScript : this.destroyScripts) { 093 try { 094 doExecuteScript(destroyScript); 095 } 096 catch (Exception e) { 097 if (logger.isDebugEnabled()) { 098 logger.warn("Could not execute destroy script [" + destroyScript + "]", e); 099 } 100 else { 101 logger.warn("Could not execute destroy script [" + destroyScript + "]"); 102 } 103 } 104 } 105 } 106 107 @Override 108 public void afterPropertiesSet() { 109 Assert.notNull(this.dataSource, "A DataSource is required"); 110 initialize(); 111 } 112 113 private void initialize() { 114 if (!this.initialized) { 115 destroy(); 116 if (this.initScripts != null) { 117 for (Resource initScript : this.initScripts) { 118 doExecuteScript(initScript); 119 } 120 } 121 this.initialized = true; 122 } 123 } 124 125 private void doExecuteScript(final Resource scriptResource) { 126 if (scriptResource == null || !scriptResource.exists()) { 127 return; 128 } 129 TransactionTemplate transactionTemplate = new TransactionTemplate(new DataSourceTransactionManager(this.dataSource)); 130 transactionTemplate.execute((TransactionCallback<Void>) status -> { 131 JdbcTemplate jdbcTemplate = new JdbcTemplate(this.dataSource); 132 String[] scripts; 133 try { 134 scripts = StringUtils 135 .delimitedListToStringArray(stripComments(getScriptLines(scriptResource)), ";"); 136 } 137 catch (IOException e) { 138 throw new BeanInitializationException("Cannot load script from [" + scriptResource + "]", e); 139 } 140 for (String script : scripts) { 141 String trimmedScript = script.trim(); 142 if (StringUtils.hasText(trimmedScript)) { 143 try { 144 jdbcTemplate.execute(trimmedScript); 145 } 146 catch (DataAccessException e) { 147 if (this.ignoreFailedDrop && trimmedScript.toLowerCase().startsWith("drop")) { 148 logger.debug("DROP script failed (ignoring): " + trimmedScript); 149 } 150 else { 151 throw e; 152 } 153 } 154 } 155 } 156 return null; 157 }); 158 159 } 160 161 private List<String> getScriptLines(Resource scriptResource) throws IOException { 162 URI uri = scriptResource.getURI(); 163 initFileSystem(uri); 164 return Files.readAllLines(Paths.get(uri), StandardCharsets.UTF_8); 165 } 166 167 private void initFileSystem(URI uri) throws IOException { 168 try { 169 FileSystems.getFileSystem(uri); 170 } 171 catch (FileSystemNotFoundException e) { 172 FileSystems.newFileSystem(uri, Collections.emptyMap()); 173 } 174 catch (IllegalArgumentException e) { 175 FileSystems.getDefault(); 176 } 177 } 178 179 private String stripComments(List<String> list) { 180 StringBuilder buffer = new StringBuilder(); 181 for (String line : list) { 182 if (!line.startsWith("//") && !line.startsWith("--")) { 183 buffer.append(line).append("\n"); 184 } 185 } 186 return buffer.toString(); 187 } 188 189 public void setInitScripts(Resource[] initScripts) { 190 this.initScripts = initScripts; 191 } 192 193 public void setDestroyScripts(Resource[] destroyScripts) { 194 this.destroyScripts = destroyScripts; 195 } 196 197 public void setDataSource(DataSource dataSource) { 198 this.dataSource = dataSource; 199 } 200 201 public void setIgnoreFailedDrop(boolean ignoreFailedDrop) { 202 this.ignoreFailedDrop = ignoreFailedDrop; 203 } 204 205}