Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot config server

I have been trying to get a grip on the spring boot config server that is located Here: https://github.com/spring-cloud/spring-cloud-config and after reading the documentation more thoroughly I was able to work through most of my issues. I did however have to write an additional class for a file based PropertySourceLocator

/*
 * 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.client;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;

/**
 * @author Al Dispennette
 *
 */
@ConfigurationProperties("spring.cloud.config")
public class ConfigServiceFilePropertySourceLocator implements PropertySourceLocator {
    private Logger logger = LoggerFactory.getLogger(ConfigServiceFilePropertySourceLocator.class);

    private String env = "default";

    @Value("${spring.application.name:'application'}")
    private String name;

    private String label = name;

    private String basedir = System.getProperty("user.home");

    @Override
    public PropertySource<?> locate() {
        try {
            return getPropertySource();
        } catch (IOException e) {
            logger.error("An error ocurred while loading the properties.",e);
        }

        return null;
    }

    /**
     * @throws IOException
     */
    private PropertySource getPropertySource() throws IOException {
        Properties source = new Properties();
        Path path = Paths.get(getUri());
        if(Files.isDirectory(path)){
            Iterator<Path> itr = Files.newDirectoryStream(path).iterator();
            String fileName = null!=label||StringUtils.hasText(label)?label:name+".properties";
            logger.info("Searching for {}",fileName);
            while(itr.hasNext()){
                Path tmpPath = itr.next();
                if(tmpPath.getFileName().getName(0).toString().equals(fileName)){
                    logger.info("Found file: {}",fileName);
                    source.load(Files.newInputStream(tmpPath));
                }
            }
        }
        return new PropertiesPropertySource("configService",source);
    }

    public String getUri() {
        StringBuilder bldr = new StringBuilder(basedir)
        .append(File.separator)
        .append(env)
        .append(File.separator)
        .append(name);

        logger.info("loading properties directory: {}",bldr.toString());
        return bldr.toString();
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEnv() {
        return env;
    }

    public void setEnv(String env) {
        this.env = env;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getBasedir() {
        return basedir;
    }

    public void setBasedir(String basedir) {
        this.basedir = basedir;
    }

}

Then I added this to the ConfigServiceBootstrapConfiguration.java

@Bean
public PropertySourceLocator configServiceFilePropertySource(
        ConfigurableEnvironment environment) {
    ConfigServiceFilePropertySourceLocator locator = new ConfigServiceFilePropertySourceLocator();
    String[] profiles = environment.getActiveProfiles();
    if (profiles.length==0) {
        profiles = environment.getDefaultProfiles();
    }
    locator.setEnv(StringUtils.arrayToCommaDelimitedString(profiles));
    return locator;
}

In the end this did what I wanted. Now I'm curious to know if this is what I should have done or if I am still missing something and this was already handled and I just missed it.

*****Edit for info asked for by Dave******

If I take out the file property source loader and update the bootstrap.yml with

uri: file://${user.home}/resources

the sample application throws the following error on start up:

ConfigServiceBootstrapConfiguration : Could not locate PropertySource: Object of class [sun.net.www.protocol.file.FileURLConnection] must be an instance of class java.net.HttpURLConnection

This is why I thought the additional class would be needed. As far as the test case goes I believe you are talking about the SpringApplicationEnvironmentRepositoryTests.java and I agree creating the environment works but as a whole the application does not seem to be opertaing as expected when the uri protocol is 'file'.

******Additional Edits*******

This is how I understanding this is working: The sample project has a dependency on the spring-cloud-config-client artifact so therefore has a transitive dependency on the spring-cloud-config-server artifact. The ConfigServiceBootstrapConfiguration.java in the client artifact creates a property source locator bean of type ConfigServicePropertySourceLocator. The ConfigServicePropertySourceLocator.java in the config client artifact has the annotation @ConfigurationProperties("spring.cloud.config") And the property uri exists in said class, hence the setting of spring.cloud.config.uri in the bootstrap.yml file.

I believe this is reenforced up by the following statement in the quickstart.adoc:

When it runs it will pick up the external configuration from the default local config server on port 8888 if it is running. To modify the startup behaviour you can change the location of the config server using bootstrap.properties (like application.properties but for the bootstrap phase of an application context), e.g.

---- spring.cloud.config.uri: http://myconfigserver.com

At this point, some how the JGitEnvironmentRepository bean is getting used and looking for a connection to github. I assumed that since uri was the property being set in the ConfigServicePropertySourceLocator then any valid uri protocol would work for pointing to a location. That is why I used the 'file://' protocol thinking that the server would pick up the NativeEnvironmentRepository.

So at this point I'm sure I'm either missing some step or the file system property source locator needs to be added.

I hope that is a little clearer.

the Full Stack:

java.lang.IllegalArgumentException: Object of class [sun.net.www.protocol.file.FileURLConnection] must be an instance of class java.net.HttpURLConnection
    at org.springframework.util.Assert.isInstanceOf(Assert.java:339)
    at org.springframework.util.Assert.isInstanceOf(Assert.java:319)
    at org.springframework.http.client.SimpleClientHttpRequestFactory.openConnection(SimpleClientHttpRequestFactory.java:182)
    at org.springframework.http.client.SimpleClientHttpRequestFactory.createRequest(SimpleClientHttpRequestFactory.java:140)
    at org.springframework.http.client.support.HttpAccessor.createRequest(HttpAccessor.java:76)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:541)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:506)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:448)
    at org.springframework.cloud.config.client.ConfigServicePropertySourceLocator.locate(ConfigServicePropertySourceLocator.java:68)
    at org.springframework.cloud.bootstrap.config.ConfigServiceBootstrapConfiguration.initialize(ConfigServiceBootstrapConfiguration.java:70)
    at org.springframework.boot.SpringApplication.applyInitializers(SpringApplication.java:572)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:952)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:941)
    at sample.Application.main(Application.java:20)
like image 979
peekay Avatar asked Oct 24 '14 14:10

peekay


People also ask

What is a config server in Spring Boot?

Spring Cloud Config provides server and client-side support for externalized configuration in a distributed system. With the Config Server you have a central place to manage external properties for applications across all environments.

Why do we need config server in Spring Boot?

Spring Cloud Config provides server-side and client-side support for externalized configuration in a distributed system. With the Config Server, you have a central place to manage external properties for applications across all environments.

Where is Spring Boot config?

By default, the configured locations are classpath:/,classpath:/config/,file:./,file:./config/ .


2 Answers

I read this thread yesterday and it was missing a vital piece of information

If you don't want to use git as a repository, then you need to configure the spring cloud server to have spring.profiles.active=native

Checkout the spring-config-server code to understand it org.springframework.cloud.config.server.NativeEnvironmentRepository

 spring:
   application:
    name: configserver
  jmx:
    default_domain: cloud.config.server
  profiles:
    active: native
  cloud:
    config:
      server:
        file :
          url : <path to config files>  
like image 168
Tony Murphy Avatar answered Oct 17 '22 00:10

Tony Murphy


I just came cross same issue. I want the configuration server load properties from local file system instead of git repository. The following configuration works for me on windows.

spring:  
  profiles:
    active: native
  cloud:
    config:
      server:
        native: 
          searchLocations: file:C:/springbootapp/properties/

Suppose the property file is under C:/springbootapp/properties/

For more information please refer to Spring Cloud Documentation and Configuring It All Out

like image 30
Licheng Nie Avatar answered Oct 16 '22 23:10

Licheng Nie