Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Creates spring-cloud-gcp-autoconfigure module #276

Merged
merged 30 commits into from
Jan 11, 2018

Conversation

joaoandremartins
Copy link
Contributor

Only contains the config module for now, remaining ones to come
separately so that the PR doesn't grow too big.
Also creates a spring-cloud-gcp-config module.

Helps fixing #262

Only contains the config module for now, remaining ones to come
separately so that the PR doesn't grow too big.
Also creates a spring-cloud-gcp-config module.

Helps fixing #262
@joaoandremartins
Copy link
Contributor Author

Will need to repeat for all the other starters.
One thing that's happening is that, by importing only spring-cloud-gcp-autoconfigure (and not spring-cloud-gcp-starter-config), GcpConfigProperties is somehow being populated, with only GcpConfigAutoConfiguration in the classpath (i.e., no GoogleConfigPropertySourceLocator).

@snicoll FYI to make sure I'm on the right track.

@artembilan
Copy link
Contributor

Travis doesn't like some your changes:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project spring-cloud-gcp-config-example: Compilation failure: Compilation failure:

[ERROR] /home/travis/build/spring-cloud/spring-cloud-gcp/spring-cloud-gcp-examples/spring-cloud-gcp-config-example/src/main/java/com/example/MyAppProperties.java:[20,59] package org.springframework.cloud.context.config.annotation does not exist

[ERROR] /home/travis/build/spring-cloud/spring-cloud-gcp/spring-cloud-gcp-examples/spring-cloud-gcp-config-example/src/main/java/com/example/MyAppProperties.java:[28,2] cannot find symbol

[ERROR] symbol: class RefreshScope

@joaoandremartins
Copy link
Contributor Author

Nevermind the point about config always working. Turns out I didn't maven reinstall everything, working as intended now.

Copy link
Contributor

@artembilan artembilan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might miss something or just don't understand the whole picture, but here is what I feel on the matter.

I hope @snicoll will share some his wisdom as well when he has a spare cycle.

Thanks

</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-core</artifactId>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this must go. The new spring-cloud-gcp-autoconfigure should absorb its code fully.

*/
@Configuration
@AutoConfigureAfter(GcpContextAutoConfiguration.class)
@EnableConfigurationProperties(GcpConfigProperties.class)
@ConditionalOnClass(GoogleConfigPropertySourceLocator.class)
@ConditionalOnMissingBean(GcpConfigProperties.class)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need this condition. The @EnableConfigurationProperties does the trick anyway.

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.gcp.config.autoconfig.GcpConfigAutoConfiguration,\
org.springframework.cloud.gcp.autoconfigure.config.GcpConfigAutoConfiguration,\
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not BootstrapConfiguration part. This must be for the org.springframework.boot.autoconfigure.EnableAutoConfiguration property

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is correct, because it's for BootstrapConfiguration, as required by Spring Cloud Config.

</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-core</artifactId>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-Boot module can't depend on Boot. I see there GcpContextAutoConfiguration which should be as part of the spring-boot-autoconfigure as well and this one (spring-cloud-gcp-config) must be revisited respectivelly do not depend on Boot.
If we need this separate module at all though...

@joaoandremartins
Copy link
Contributor Author

For some reason that I don't understand, the trace starter tests are stalling.

@artembilan
Copy link
Contributor

Any ideas regarding current Travis failure?

@joaoandremartins
Copy link
Contributor Author

It seems something is adding BootstrapApplicationListener to the test application's listeners.
That's causing the application to try to load GoogleConfigPropertySourceLocator, I suspect because it's in spring-cloud-gcp-autoconfigure's spring.factories.

There's a class load error and the Spring app gets into an endless loop through handleRunFailure().
Tomorrow, I'll need to understand why this test is trying to load config components, or why the similar SQL test doesn't try to load them at all, with a similar set of dependencies.

</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-core</artifactId>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this dependency needed currently?

Copy link
Contributor Author

@joaoandremartins joaoandremartins Dec 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we need it to import GcpProperties, GcpProjectIdProvider, etc.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider the case someone decides to add the auto-configuration of GCP to their stack but there are scenarios where they might not use it yet.

IMO, this should be <optional>true</optional> and you should have an extra condition to guard the fact this dependency is not available.

@meltsufin
Copy link
Contributor

For me, a simple mvn clean install at the root fails.

testResolveScopesDefaultScopes(org.springframework.cloud.gcp.autoconfigure.core.GcpContextAutoConfigurationTests)  Time elapsed: 0.616 sec  <<< ERROR!
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gcpProjectIdProvider' defined in org.springframework.cloud.gcp.autoconfigure.core.GcpContextAutoConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cloud.gcp.core.GcpProjectIdProvider]: Factory method 'gcpProjectIdProvider' threw exception; nested exception is java.lang.NoSuchMethodError: org.json.JSONTokener.<init>(Ljava/io/InputStream;)V
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1173)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1067)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
        at org.springframework.cloud.gcp.autoconfigure.core.GcpContextAutoConfigurationTests.loadEnvironment(GcpContextAutoConfigurationTests.java:103)
        at org.springframework.cloud.gcp.autoconfigure.core.GcpContextAutoConfigurationTests.testResolveScopesDefaultScopes(GcpContextAutoConfigurationTests.java:71)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
        at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
        at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cloud.gcp.core.GcpProjectIdProvider]: Factory method 'gcpProjectIdProvider' threw exception; nested exception is java.lang.NoSuchMethodError: org.json.JSONTokener.<init>(Ljava/io/InputStream;)V
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
        ... 43 more
Caused by: java.lang.NoSuchMethodError: org.json.JSONTokener.<init>(Ljava/io/InputStream;)V
        at com.google.cloud.ServiceOptions.getServiceAccountProjectId(ServiceOptions.java:442)
        at com.google.cloud.ServiceOptions.getDefaultProjectId(ServiceOptions.java:315)
        at org.springframework.cloud.gcp.core.DefaultGcpProjectIdProvider.getProjectId(DefaultGcpProjectIdProvider.java:36)
        at org.springframework.cloud.gcp.autoconfigure.core.GcpContextAutoConfiguration.gcpProjectIdProvider(GcpContextAutoConfiguration.java:157)
        at org.springframework.cloud.gcp.autoconfigure.core.GcpContextAutoConfiguration$$EnhancerBySpringCGLIB$$9e94071a.CGLIB$gcpProjectIdProvider$2(<generated>)
        at org.springframework.cloud.gcp.autoconfigure.core.GcpContextAutoConfiguration$$EnhancerBySpringCGLIB$$9e94071a$$FastClassBySpringCGLIB$$76e931a9.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
        at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:358)
        at org.springframework.cloud.gcp.autoconfigure.core.GcpContextAutoConfiguration$$EnhancerBySpringCGLIB$$9e94071a.gcpProjectIdProvider(<generated>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
        ... 44 more

@joaoandremartins
Copy link
Contributor Author

@meltsufin not sure why that's happening... Sounds like you have the wrong version of org.json.

@meltsufin
Copy link
Contributor

I found the conflict. spring-boot-configuration-processor brings in android-json that conflicts with the regular json jar. Adding the exclusion fixes the problem for me.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
            <exclusions>
                <exclusion>
                    <groupId>com.vaadin.external.google</groupId>
                    <artifactId>android-json</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

However, now the trace tests are stalling on:

10:30:39.815 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding PropertySource 'Inlined Test Properties' with highest search precedence

@joaoandremartins
Copy link
Contributor Author

Sorry, I'll merge the fix shortly.

Configuration now isn't auto-completing in STS, need to find out why.
@joaoandremartins
Copy link
Contributor Author

@meltsufin it should be working now

@joaoandremartins
Copy link
Contributor Author

This change disables auto-completion for both core and config properties.
I haven't been able to understand why, yet.

@meltsufin
Copy link
Contributor

The build still fails for me unless I exclude android-json, but then it succeeds.
I think we need to resolve the json dependency conflict.
google-cloud-core brings in org.json:json and spring-boot-configuration-processor brings in com.vaadin.external.google:android-json. We need to exclude one or the other.

@joaoandremartins
Copy link
Contributor Author

I wonder if this something with your local environment, since neither I nor Travis are seeing the same thing.
In any case, it would be very helpful not to block the review on this.

@@ -22,5 +22,9 @@
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cyclic dependency?
Why should we worry about autoconfig here if it is just core?
Not sure what I am missing, but would like to take your attention on this.

Thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spring-boot-autoconfigure, not spring-cloud-gcp-autoconfigure.

We need spring-boot-autoconfigure to get GcpProperties's @ConfigurationProperties annotation. LMK if I should be using another dependency.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh! My bad. I see what is that.
Well, I think the problem that all the @ConfigurationProperties must be as a part of our autoconfigure not more.

Not sure how have you arranged it with Stephane, but I feel like something in architecture is still missing. Maybe there is still something in the target modules what is Boot-based? So, that should be moved to the autoconfigure module as well...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other spring-boot projects seem to be keeping their @ConfigurationProperties in the autoconfigure module as well.

I reviewed our project and all @ConfigurationProperties are used only within autoconfigure, except for the config and SQL ones.

For the config one, we might be able to make GoogleConfigPropertySourceLocator take in individual arguments instead of GcpConfigProperties (e.g., config name, enabled, etc.).
The SQL properties are used by the JdbcUrlProviders, but I think we can add those to the autoconfigure module as well..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the config one, we might be able to make

Are you going to do those changes to avoid this spring-boot-autoconfigure dependency in the core?
Or do you mean to do that in the separate issue?

Thanks

org.springframework.cloud.gcp.config.autoconfig.GcpConfigAutoConfiguration,\
org.springframework.cloud.gcp.core.autoconfig.GcpContextAutoConfiguration,\
org.springframework.cloud.gcp.config.GoogleConfigPropertySourceLocator
org.springframework.cloud.gcp.config.GoogleConfigPropertySourceLocator
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New line in the end of file.
The IDEA can be configured to do that automatically.

@joaoandremartins
Copy link
Contributor Author

@snicoll PTAL

I've added back the spring-cloud-gcp-config module. It doesn't have a use besides activating the @EnableConfigurationProperties annotation in GcpConfigAutoConfiguration. This is so that Spring Cloud Config isn't always enabled for any project that uses core. It's the best compromise we found here, but let us know what you think.

I'd prefer to keep spring-cloud-gcp-core. spring-cloud-gcp is not descriptive enough, but I can change this if you feel very strongly about it.

@dsyer
Copy link
Contributor

dsyer commented Jan 10, 2018

I'm not sure I follow all of the discussion above (possibly refers to previous drafts), so at the risk of misunderstanding something, I have a couple of comments.

We should look at it from the point of view of the user. As a user I want to use GCP as a config properties source (whatever that means). How do I achieve that? I expect it is: I add something to the classpath (one thing, or two?), and configure an env var (or equivalent). I believe @snicoll is offering to add a feature to start.spring.io so that even "two things on the classpath" can be specified as "one checkbox". So that would be nice. IMO we should make that happen, and reduce the number of tiny modules here as much as possible.

This is so that Spring Cloud Config isn't always enabled for any project that uses core

I don't understand that comment, and I don't understand why spring-cloud-gcp-config is needed as a separate module (it's basically trivial to merge with the autoconfig one and is only ever going to be used with that).

@meltsufin
Copy link
Contributor

@dsyer Thank you for looking at this PR and your comments.
In the interest of moving this PR forward, I would suggest limiting its scope just to the introduction of the autoconfigure module and making the starters have no code -- basically, just aligning the module structure to what is done in spring-boot.

We've gone back and forth on whether to have the config module or merge into the autoconfigure module. I don't think it makes much of a difference to the user because enabling it is the same -- by adding the starter-config module. In any case, let's continue this discussion in #299 so as not to block this PR.

On the topic of merging starters or exposing combination of them on start.spring.io, let's have that discussion in #300.

</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<scope>test</scope>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. Can't have commons-logging as a transitive for user apps. OTOH, if we just merge this tiny module and make it depend on Spring Boot (like it feels it should), then it's probably moot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merger of the config module is being discussed in #299 (comment)

As per my comment, I don't think it's doable, but your input is welcome!

meltsufin
meltsufin previously approved these changes Jan 10, 2018
@meltsufin
Copy link
Contributor

@artembilan Do you still have concerns on this PR? Can you do another quick pass please?

@artembilan
Copy link
Contributor

Sorry, I'm not so good in everything we have in this PR, but if @snicoll or @dsyer says it's OK, I'll approve.

Will take a look one more time otherwise somewhere later in the afternoon.

Investigating NPE in some other place - blocker though 😄

@joaoandremartins
Copy link
Contributor Author

joaoandremartins commented Jan 10, 2018

@artembilan an NPE in our project? If so, I'd like to know about it. Maybe I can help.

Also, provided @snicoll is OK with this PR, hope you don't mind if I dismiss your review. Comments were resolved, anyway.

@artembilan
Copy link
Contributor

No, that's not related to this project. I have many others in my plate.

OK. Feel free to dismiss and merge. I can review afterwards.

@snicoll
Copy link
Contributor

snicoll commented Jan 10, 2018

Also, provided @snicoll is OK with this PR

I really would like to move forward with this and get a better idea on the bigger picture but unfortunately I can't. I feel we've been back and forth on this so I don't know what to do to unblock the situation.

Here are some feedback on the current state, I hope this will help

spring-cloud-gcp-config shouldn't exist IMO:

  • The GcpConfigPropertiesProvider doesn't look like an API you'd have designed without the constraints that have been expressed here. There is only one implementation in the auto-configuration module and I doubt anyone would bother implementing this interface to use this feature without Spring Boot
  • The only reason it exists as far as I understand is to make sure the feature only kicks in when it is added to the classpath. But it doesn't have to be that way. If we want to be good citizen, we shouldn't enable this feature when running the code in our IDE. Perhaps we need a key, or we should check we are running on GCP.
  • This module that, supposingly, isn't related to Spring Boot/Spring Cloud as a spring.factories in it. That doesn't sound right.

GcpConfigAutoConfiguration is empty. I can't help but think that something is missing there. What does this auto-configuration do exactly besides processing GcpConfigProperties? Why is it an auto-configuration if the thing that it auto-configures should run in the bootstrap context? I don't know how BootstrapConfiguration is supposed to work but I assume these are components that run in the parent context that processes the Environment. If I am right, that auto-configuration seems useless.

Perhaps it makes sense to have a spring-cloud-gcp-config module but I fail to see why considering that this feature should only be enabled (?) when the app run on GCP.

Having said that, I don't think I am giving any argument that I'ven't given already so feel free to move forward if you believe those objections aren't warranted.

Thank you!

@meltsufin
Copy link
Contributor

@snicoll Since the last comment primarily relates to #299. I moved it there. If you don't mind, let's continue this discussion there, and merge this PR for the time being.

@meltsufin meltsufin dismissed artembilan’s stale review January 10, 2018 20:51

Review is now stale. Comments can be raised in the next PR that continues this work.

@snicoll
Copy link
Contributor

snicoll commented Jan 11, 2018

@meltsufin I don't understand why you would want to merge something that adds something that is discussed for removal in another issue (I find that super confusing). But if merging this PR helps moving things forward, sure thing.

@joaoandremartins joaoandremartins merged commit 4dbe159 into master Jan 11, 2018
@joaoandremartins joaoandremartins deleted the gh-262-autoconfigure-module branch January 11, 2018 15:54
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

5 participants