1+ /*
2+ * Licensed to the Apache Software Foundation (ASF) under one or more
3+ * contributor license agreements. See the NOTICE file distributed with
4+ * this work for additional information regarding copyright ownership.
5+ * The ASF licenses this file to You under the Apache License, Version 2.0
6+ * (the "License"); you may not use this file except in compliance with
7+ * the License. You may obtain a copy of the License at
8+ *
9+ * http://www.apache.org/licenses/LICENSE-2.0
10+ *
11+ * Unless required by applicable law or agreed to in writing, software
12+ * distributed under the License is distributed on an "AS IS" BASIS,
13+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+ * See the License for the specific language governing permissions and
15+ * limitations under the License.
16+ */
17+ package com .ctrip .framework .apollo .spring .boot ;
18+
19+ import com .ctrip .framework .apollo .Config ;
20+ import com .ctrip .framework .apollo .ConfigService ;
21+ import com .ctrip .framework .apollo .core .ConfigConsts ;
22+ import com .ctrip .framework .apollo .spring .config .ConfigPropertySourceFactory ;
23+ import com .ctrip .framework .apollo .spring .config .PropertySourcesConstants ;
24+ import com .ctrip .framework .apollo .spring .util .SpringInjector ;
25+ import com .ctrip .framework .apollo .util .factory .PropertiesFactory ;
26+ import com .google .common .base .Splitter ;
27+ import com .google .common .base .Strings ;
28+ import org .slf4j .Logger ;
29+ import org .slf4j .LoggerFactory ;
30+ import org .springframework .boot .SpringApplication ;
31+ import org .springframework .boot .env .EnvironmentPostProcessor ;
32+ import org .springframework .context .ApplicationContextInitializer ;
33+ import org .springframework .context .ConfigurableApplicationContext ;
34+ import org .springframework .core .Ordered ;
35+ import org .springframework .core .env .CompositePropertySource ;
36+ import org .springframework .core .env .ConfigurableEnvironment ;
37+
38+ import java .util .List ;
39+
40+ /**
41+ * Initialize apollo system properties and inject the Apollo config in Spring Boot bootstrap phase
42+ *
43+ * <p>Configuration example:</p>
44+ * <pre class="code">
45+ * # set app.id
46+ * app.id = 100004458
47+ * # enable apollo bootstrap config and inject 'application' namespace in bootstrap phase
48+ * apollo.bootstrap.enabled = true
49+ * </pre>
50+ *
51+ * or
52+ *
53+ * <pre class="code">
54+ * # set app.id
55+ * app.id = 100004458
56+ * # enable apollo bootstrap config
57+ * apollo.bootstrap.enabled = true
58+ * # will inject 'application' and 'FX.apollo' namespaces in bootstrap phase
59+ * apollo.bootstrap.namespaces = application,FX.apollo
60+ * </pre>
61+ *
62+ *
63+ * If you want to load Apollo configurations even before Logging System Initialization Phase,
64+ * add
65+ * <pre class="code">
66+ * # set apollo.bootstrap.eagerLoad.enabled
67+ * apollo.bootstrap.eagerLoad.enabled = true
68+ * </pre>
69+ *
70+ * This would be very helpful when your logging configurations is set by Apollo.
71+ *
72+ * for example, you have defined logback-spring.xml in your project, and you want to inject some attributes into logback-spring.xml.
73+ *
74+ */
75+ public class ApolloApplicationContextInitializer
76+ implements
77+ ApplicationContextInitializer <ConfigurableApplicationContext >,
78+ EnvironmentPostProcessor , Ordered {
79+ public static final int DEFAULT_ORDER = 0 ;
80+
81+ private static final Logger logger = LoggerFactory
82+ .getLogger (ApolloApplicationContextInitializer .class );
83+ private static final Splitter NAMESPACE_SPLITTER = Splitter .on ("," )
84+ .omitEmptyStrings ()
85+ .trimResults ();
86+ private static final String [] APOLLO_SYSTEM_PROPERTIES = { "app.id" ,
87+ ConfigConsts .APOLLO_CLUSTER_KEY , "apollo.cacheDir" , "apollo.accesskey.secret" ,
88+ ConfigConsts .APOLLO_META_KEY , PropertiesFactory .APOLLO_PROPERTY_ORDER_ENABLE };
89+
90+ private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector
91+ .getInstance (ConfigPropertySourceFactory .class );
92+
93+ private int order = DEFAULT_ORDER ;
94+
95+ @ Override
96+ public void initialize (ConfigurableApplicationContext context ) {
97+ ConfigurableEnvironment environment = context .getEnvironment ();
98+
99+ if (!environment .getProperty (PropertySourcesConstants .APOLLO_BOOTSTRAP_ENABLED ,
100+ Boolean .class , false )) {
101+ logger .debug (
102+ "Apollo bootstrap config is not enabled for context {}, see property: ${{}}" ,
103+ context , PropertySourcesConstants .APOLLO_BOOTSTRAP_ENABLED );
104+ return ;
105+ }
106+ logger .debug ("Apollo bootstrap config is enabled for context {}" , context );
107+
108+ initialize (environment );
109+ }
110+
111+ /**
112+ * Initialize Apollo Configurations Just after environment is ready.
113+ *
114+ * @param environment
115+ */
116+ protected void initialize (ConfigurableEnvironment environment ) {
117+
118+ if (environment .getPropertySources ().contains (
119+ PropertySourcesConstants .APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME )) {
120+ //already initialized
121+ return ;
122+ }
123+
124+ String namespaces = environment .getProperty (
125+ PropertySourcesConstants .APOLLO_BOOTSTRAP_NAMESPACES ,
126+ ConfigConsts .NAMESPACE_APPLICATION );
127+ logger .debug ("Apollo bootstrap namespaces: {}" , namespaces );
128+ List <String > namespaceList = NAMESPACE_SPLITTER .splitToList (namespaces );
129+
130+ CompositePropertySource composite = new CompositePropertySource (
131+ PropertySourcesConstants .APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME );
132+ for (String namespace : namespaceList ) {
133+ Config config = ConfigService .getConfig (namespace );
134+
135+ composite .addPropertySource (configPropertySourceFactory .getConfigPropertySource (
136+ namespace , config ));
137+ }
138+
139+ environment .getPropertySources ().addFirst (composite );
140+ }
141+
142+ /**
143+ * To fill system properties from environment config
144+ */
145+ void initializeSystemProperty (ConfigurableEnvironment environment ) {
146+ for (String propertyName : APOLLO_SYSTEM_PROPERTIES ) {
147+ fillSystemPropertyFromEnvironment (environment , propertyName );
148+ }
149+ }
150+
151+ private void fillSystemPropertyFromEnvironment (ConfigurableEnvironment environment ,
152+ String propertyName ) {
153+ if (System .getProperty (propertyName ) != null ) {
154+ return ;
155+ }
156+
157+ String propertyValue = environment .getProperty (propertyName );
158+
159+ if (Strings .isNullOrEmpty (propertyValue )) {
160+ return ;
161+ }
162+
163+ System .setProperty (propertyName , propertyValue );
164+ }
165+
166+ /**
167+ *
168+ * In order to load Apollo configurations as early as even before Spring loading logging system phase,
169+ * this EnvironmentPostProcessor can be called Just After ConfigFileApplicationListener has succeeded.
170+ *
171+ * <br />
172+ * The processing sequence would be like this: <br />
173+ * Load Bootstrap properties and application properties -----> load Apollo configuration properties ----> Initialize Logging systems
174+ *
175+ * @param configurableEnvironment
176+ * @param springApplication
177+ */
178+ @ Override
179+ public void postProcessEnvironment (ConfigurableEnvironment configurableEnvironment ,
180+ SpringApplication springApplication ) {
181+
182+ // should always initialize system properties like app.id in the first place
183+ // initializeSystemProperty(configurableEnvironment);
184+
185+ Boolean eagerLoadEnabled = configurableEnvironment .getProperty (
186+ PropertySourcesConstants .APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED , Boolean .class , false );
187+
188+ //EnvironmentPostProcessor should not be triggered if you don't want Apollo Loading before Logging System Initialization
189+ if (!eagerLoadEnabled ) {
190+ return ;
191+ }
192+
193+ Boolean bootstrapEnabled = configurableEnvironment .getProperty (
194+ PropertySourcesConstants .APOLLO_BOOTSTRAP_ENABLED , Boolean .class , false );
195+
196+ if (bootstrapEnabled ) {
197+ initialize (configurableEnvironment );
198+ }
199+
200+ }
201+
202+ /**
203+ * @since 1.3.0
204+ */
205+ @ Override
206+ public int getOrder () {
207+ return order ;
208+ }
209+
210+ /**
211+ * @since 1.3.0
212+ */
213+ public void setOrder (int order ) {
214+ this .order = order ;
215+ }
216+ }
0 commit comments