001package ball.spring.mysqld; 002/*- 003 * ########################################################################## 004 * Reusable Spring Components 005 * $Id: MysqldConfiguration.java 7215 2021-01-03 18:39:51Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-spring/trunk/jar/src/main/java/ball/spring/mysqld/MysqldConfiguration.java $ 007 * %% 008 * Copyright (C) 2018 - 2021 Allen D. Ball 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * ########################################################################## 022 */ 023import java.io.File; 024import java.io.IOException; 025import java.lang.ProcessBuilder.Redirect; 026import java.nio.file.Files; 027import javax.annotation.PostConstruct; 028import javax.annotation.PreDestroy; 029import lombok.NoArgsConstructor; 030import lombok.ToString; 031import lombok.extern.log4j.Log4j2; 032import org.springframework.beans.factory.annotation.Value; 033import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 034import org.springframework.context.annotation.Bean; 035import org.springframework.context.annotation.Configuration; 036import org.springframework.scheduling.annotation.EnableScheduling; 037 038import static java.util.concurrent.TimeUnit.SECONDS; 039import static org.apache.commons.lang3.StringUtils.EMPTY; 040 041/** 042 * {@code mysqld} {@link Configuration}. A {@code mysqld} process is 043 * started if the {@code mysqld.home} application property is set. In 044 * addition, a port must be specified with the {@code mysqld.port} property. 045 * 046 * {@injected.fields} 047 * 048 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 049 * @version $Revision: 7215 $ 050 */ 051@Configuration 052@EnableScheduling 053@ConditionalOnProperty(name = "mysqld.home", havingValue = EMPTY) 054@NoArgsConstructor @ToString @Log4j2 055public class MysqldConfiguration { 056 @Value("${mysqld.home}") 057 private File home; 058 059 @Value("${mysqld.defaults.file:${mysqld.home}/my.cnf}") 060 private File defaults; 061 062 @Value("${mysqld.datadir:${mysqld.home}/data}") 063 private File datadir; 064 065 @Value("${mysqld.port}") 066 private Integer port; 067 068 @Value("${mysqld.socket:${mysqld.home}/socket}") 069 private File socket; 070 071 @Value("${logging.file.path}/mysqld.log") 072 private File console; 073 074 private volatile Process mysqld = null; 075 076 @PostConstruct 077 public void init() { } 078 079 @Bean 080 public Process mysqld() throws IOException { 081 if (mysqld == null) { 082 synchronized (this) { 083 if (mysqld == null) { 084 Files.createDirectories(home.toPath()); 085 Files.createDirectories(datadir.toPath().getParent()); 086 Files.createDirectories(console.toPath().getParent()); 087 088 String defaultsArg = "--no-defaults"; 089 090 if (defaults.exists()) { 091 defaultsArg = 092 "--defaults-file=" + defaults.getAbsolutePath(); 093 } 094 095 String datadirArg = "--datadir=" + datadir.getAbsolutePath(); 096 String socketArg = "--socket=" + socket.getAbsolutePath(); 097 String portArg = "--port=" + port; 098 099 if (! datadir.exists()) { 100 try { 101 new ProcessBuilder("mysqld", 102 defaultsArg, datadirArg, 103 "--initialize-insecure") 104 .directory(home) 105 .inheritIO() 106 .redirectOutput(Redirect.to(console)) 107 .redirectErrorStream(true) 108 .start() 109 .waitFor(); 110 } catch (InterruptedException exception) { 111 } 112 } 113 114 if (datadir.exists()) { 115 socket.delete(); 116 117 mysqld = 118 new ProcessBuilder("mysqld", 119 defaultsArg, datadirArg, 120 socketArg, portArg) 121 .directory(home) 122 .inheritIO() 123 .redirectOutput(Redirect.appendTo(console)) 124 .redirectErrorStream(true) 125 .start(); 126 127 while (! socket.exists()) { 128 try { 129 mysqld.waitFor(15, SECONDS); 130 } catch (InterruptedException exception) { 131 } 132 133 if (mysqld.isAlive()) { 134 continue; 135 } else { 136 throw new IllegalStateException("mysqld not started"); 137 } 138 } 139 } else { 140 throw new IllegalStateException("mysqld datadir does not exist"); 141 } 142 } 143 } 144 } 145 146 return mysqld; 147 } 148 149 @PreDestroy 150 public void destroy() { 151 if (mysqld != null) { 152 try { 153 for (int i = 0; i < 8; i+= 1) { 154 if (mysqld.isAlive()) { 155 mysqld.destroy(); 156 mysqld.waitFor(15, SECONDS); 157 } else { 158 break; 159 } 160 } 161 } catch (InterruptedException exception) { 162 } 163 164 try { 165 if (mysqld.isAlive()) { 166 mysqld.destroyForcibly().waitFor(60, SECONDS); 167 } 168 } catch (InterruptedException exception) { 169 } 170 } 171 } 172}