Refactor base classes for EnvironmentRepository implementations

Allows us to re-use the Scm features independent of the repository
abstraction.
This commit is contained in:
Dave Syer
2015-09-29 16:52:37 +01:00
parent 7ca8d76e90
commit 02a9eb3cd7
5 changed files with 235 additions and 205 deletions

View File

@@ -34,7 +34,7 @@ import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.config.server.AbstractScmEnvironmentRepository;
import org.springframework.cloud.config.server.AbstractScmAccessor;
import org.springframework.cloud.config.server.NativeEnvironmentRepository;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.SmartLifecycle;
@@ -66,7 +66,7 @@ public class FileMonitorConfiguration implements SmartLifecycle, ResourceLoaderA
PropertyPathEndpoint endpoint;
@Autowired(required = false)
AbstractScmEnvironmentRepository scmRepository;
AbstractScmAccessor scmRepository;
@Autowired(required = false)
NativeEnvironmentRepository nativeEnvironmentRepository;

View File

@@ -1,171 +1,158 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed 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.springframework.cloud.config.server;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jgit.util.FileUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.UrlResource;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.StringUtils;
/**
* @author Dave Syer
* @author Michael Prankl
*/
public abstract class AbstractScmEnvironmentRepository implements EnvironmentRepository,
InitializingBean {
private static Log logger = LogFactory.getLog(AbstractScmEnvironmentRepository.class);
private File basedir;
private String uri;
private ConfigurableEnvironment environment;
private String username;
private String password;
private String[] searchPaths = new String[0];
public AbstractScmEnvironmentRepository(ConfigurableEnvironment environment) {
this.environment = environment;
this.basedir = createBaseDir();
}
private File createBaseDir() {
try {
final File basedir = Files.createTempDirectory("config-repo-").toFile();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
FileUtils.delete(basedir, FileUtils.RECURSIVE);
}
catch (IOException e) {
logger.warn("Failed to delete temporary directory on exit: " + e);
}
}
});
return basedir;
}
catch (IOException e) {
throw new IllegalStateException("Cannot create temp dir", e);
}
}
protected ConfigurableEnvironment getEnvironment() {
return environment;
}
protected void setEnvironment(ConfigurableEnvironment environment) {
this.environment = environment;
}
public void setUri(String uri) {
while (uri.endsWith("/")) {
uri = uri.substring(0, uri.length() - 1);
}
int index = uri.indexOf("://");
if (index>0 && !uri.substring(index+"://".length()).contains("/")) {
// If there's no context path add one
uri = uri + "/";
}
this.uri = uri;
}
public String getUri() {
return uri;
}
public void setBasedir(File basedir) {
this.basedir = basedir.getAbsoluteFile();
}
public File getBasedir() {
return basedir;
}
public void setSearchPaths(String... searchPaths) {
this.searchPaths = searchPaths;
}
public String[] getSearchPaths() {
return searchPaths;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
protected File getWorkingDirectory() {
if (uri.startsWith("file:")) {
try {
return new UrlResource(StringUtils.cleanPath(uri)).getFile();
}
catch (Exception e) {
throw new IllegalStateException("Cannot convert uri to file: " + uri);
}
}
return basedir;
}
protected String[] getSearchLocations(File dir) {
List<String> locations = new ArrayList<String>();
locations.add(dir.toURI().toString());
String[] list = dir.list();
if (list!=null) {
for (String path : list) {
File file = new File(dir, path);
if (file.isDirectory() && PatternMatchUtils.simpleMatch(searchPaths, path)) {
locations.add(file.toURI().toString());
}
}
}
return locations.toArray(new String[0]);
}
protected Environment clean(Environment value) {
Environment result = new Environment(value.getName(), value.getProfiles(), value.getLabel());
for (PropertySource source : value.getPropertySources()) {
String name = source.getName().replace(
getWorkingDirectory().toURI().toString(), "");
name = name.replace("applicationConfig: [", "");
name = uri + "/" + name.replace("]", "");
result.add(new PropertySource(name, source.getSource()));
}
return result;
}
/*
* Copyright 2015 the original author or authors.
*
* Licensed 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.springframework.cloud.config.server;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jgit.util.FileUtils;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.UrlResource;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.StringUtils;
/**
* Base class for components that want to access a source control management system.
*
* @author Dave Syer
*
*/
public class AbstractScmAccessor {
protected Log logger = LogFactory.getLog(getClass());
private File basedir;
private String uri;
private ConfigurableEnvironment environment;
private String username;
private String password;
private String[] searchPaths = new String[0];
public AbstractScmAccessor(ConfigurableEnvironment environment) {
this.environment = environment;
this.basedir = createBaseDir();
}
protected File createBaseDir() {
try {
final File basedir = Files.createTempDirectory("config-repo-").toFile();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
FileUtils.delete(basedir, FileUtils.RECURSIVE);
}
catch (IOException e) {
AbstractScmAccessor.this.logger.warn("Failed to delete temporary directory on exit: " + e);
}
}
});
return basedir;
}
catch (IOException e) {
throw new IllegalStateException("Cannot create temp dir", e);
}
}
protected ConfigurableEnvironment getEnvironment() {
return this.environment;
}
protected void setEnvironment(ConfigurableEnvironment environment) {
this.environment = environment;
}
public void setUri(String uri) {
while (uri.endsWith("/")) {
uri = uri.substring(0, uri.length() - 1);
}
int index = uri.indexOf("://");
if (index>0 && !uri.substring(index+"://".length()).contains("/")) {
// If there's no context path add one
uri = uri + "/";
}
this.uri = uri;
}
public String getUri() {
return this.uri;
}
public void setBasedir(File basedir) {
this.basedir = basedir.getAbsoluteFile();
}
public File getBasedir() {
return this.basedir;
}
public void setSearchPaths(String... searchPaths) {
this.searchPaths = searchPaths;
}
public String[] getSearchPaths() {
return this.searchPaths;
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
protected File getWorkingDirectory() {
if (this.uri.startsWith("file:")) {
try {
return new UrlResource(StringUtils.cleanPath(this.uri)).getFile();
}
catch (Exception e) {
throw new IllegalStateException("Cannot convert uri to file: " + this.uri);
}
}
return this.basedir;
}
protected String[] getSearchLocations(File dir) {
List<String> locations = new ArrayList<String>();
locations.add(dir.toURI().toString());
String[] list = dir.list();
if (list!=null) {
for (String path : list) {
File file = new File(dir, path);
if (file.isDirectory() && PatternMatchUtils.simpleMatch(this.searchPaths, path)) {
locations.add(file.toURI().toString());
}
}
}
return locations.toArray(new String[0]);
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed 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.springframework.cloud.config.server;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
/**
* @author Dave Syer
* @author Michael Prankl
*/
public class EnvironmentCleaner {
public Environment clean(Environment value, String workingDir, String uri) {
Environment result = new Environment(value.getName(), value.getProfiles(),
value.getLabel());
for (PropertySource source : value.getPropertySources()) {
String name = source.getName().replace(workingDir, "");
name = name.replace("applicationConfig: [", "");
name = uri + "/" + name.replace("]", "");
result.add(new PropertySource(name, source.getSource()));
}
return result;
}
}

View File

@@ -22,8 +22,6 @@ import java.io.File;
import java.io.IOException;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
@@ -40,6 +38,7 @@ import org.eclipse.jgit.transport.OpenSshConfig.Host;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.util.FileUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.UrlResource;
@@ -54,9 +53,7 @@ import com.jcraft.jsch.Session;
* @author Dave Syer
* @author Roy Clarkson
*/
public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository {
private static Log logger = LogFactory.getLog(JGitEnvironmentRepository.class);
public class JGitEnvironmentRepository extends AbstractScmAccessor implements EnvironmentRepository, InitializingBean {
private static final String DEFAULT_LABEL = "master";
private static final String FILE_URI_PREFIX = "file:";
@@ -74,6 +71,8 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
*/
private boolean cloneOnStart = false;
private EnvironmentCleaner cleaner = new EnvironmentCleaner();
private JGitEnvironmentRepository.JGitFactory gitFactory = new JGitEnvironmentRepository.JGitFactory();
public JGitEnvironmentRepository(ConfigurableEnvironment environment) {
@@ -133,7 +132,7 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
}
}
catch (Exception e) {
logger.warn("Could not close git repository", e);
this.logger.warn("Could not close git repository", e);
}
}
}
@@ -173,7 +172,7 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
environment.setSearchLocations(getSearchLocations(getWorkingDirectory()));
Environment result = environment.findOne(application, profile, "");
result.setLabel(label);
return clean(result);
return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(), getUri());
}
private Ref checkout(Git git, String label) throws GitAPIException {
@@ -210,7 +209,7 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
pull.call();
}
catch (Exception e) {
logger.warn("Could not pull remote for " + label + " (current ref=" + ref
this.logger.warn("Could not pull remote for " + label + " (current ref=" + ref
+ "), remote: " + git.getRepository().getConfig().getString("remote",
"origin", "url"));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,10 +15,18 @@
*/
package org.springframework.cloud.config.server;
import static org.springframework.util.StringUtils.hasText;
import java.io.File;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNAuthenticationManager;
@@ -26,32 +34,28 @@ import org.tmatesoft.svn.core.wc2.SvnCheckout;
import org.tmatesoft.svn.core.wc2.SvnOperationFactory;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import org.tmatesoft.svn.core.wc2.SvnUpdate;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import static org.springframework.util.StringUtils.hasText;
/**
* Subversion-backed {@link EnvironmentRepository}.
*
*
* @author Michael Prankl
* @author Roy Clarkson
* @author Roy Clarkson
*/
@ConfigurationProperties("spring.cloud.config.server.svn")
public class SvnKitEnvironmentRepository extends AbstractScmEnvironmentRepository {
public class SvnKitEnvironmentRepository extends AbstractScmAccessor
implements EnvironmentRepository, InitializingBean {
private static Log logger = LogFactory.getLog(SvnKitEnvironmentRepository.class);
private static final String DEFAULT_LABEL = "trunk";
@Override
public String getDefaultLabel() {
return DEFAULT_LABEL;
}
private static final String DEFAULT_LABEL = "trunk";
private EnvironmentCleaner cleaner = new EnvironmentCleaner();
@Override
public String getDefaultLabel() {
return DEFAULT_LABEL;
}
@Override
public Environment findOne(String application, String profile, String label) {
SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
@@ -67,7 +71,8 @@ public class SvnKitEnvironmentRepository extends AbstractScmEnvironmentRepositor
else {
checkout(svnOperationFactory);
}
return clean(loadEnvironment(application, profile, label));
return this.cleaner.clean(loadEnvironment(application, profile, label),
getWorkingDirectory().toURI().toString(), getUri());
}
catch (SVNException e) {
throw new IllegalStateException("Cannot checkout repository", e);
@@ -77,14 +82,15 @@ public class SvnKitEnvironmentRepository extends AbstractScmEnvironmentRepositor
}
}
private synchronized Environment loadEnvironment(String application, String profile, String label) {
private synchronized Environment loadEnvironment(String application, String profile,
String label) {
final NativeEnvironmentRepository environmentRepository = new NativeEnvironmentRepository(
getEnvironment());
String[] locations = getSearchLocations(getSvnPath(
getWorkingDirectory(), label));
String[] locations = getSearchLocations(getSvnPath(getWorkingDirectory(), label));
boolean exists = false;
for (String location : locations) {
location = location.startsWith("file:") ? location.substring("file:".length()) : location;
location = location.startsWith("file:") ? location.substring("file:".length())
: location;
location = StringUtils.cleanPath(location);
if (new File(location).exists()) {
exists = true;
@@ -116,8 +122,7 @@ public class SvnKitEnvironmentRepository extends AbstractScmEnvironmentRepositor
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(
getUri() != null,
Assert.state(getUri() != null,
"You need to configure a uri for the subversion repository (e.g. 'http://example.com/svn/')");
resolveRelativeFileUri();
}