View Javadoc
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 org.apache.logging.log4j.core.config;
18  
19  import java.io.ByteArrayOutputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.net.HttpURLConnection;
23  import java.net.MalformedURLException;
24  import java.net.URL;
25  import java.util.List;
26  
27  import org.apache.logging.log4j.Logger;
28  import org.apache.logging.log4j.core.config.plugins.Plugin;
29  import org.apache.logging.log4j.core.config.plugins.PluginAliases;
30  import org.apache.logging.log4j.core.net.UrlConnectionFactory;
31  import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
32  import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory;
33  import org.apache.logging.log4j.core.util.AbstractWatcher;
34  import org.apache.logging.log4j.core.util.Source;
35  import org.apache.logging.log4j.core.util.Watcher;
36  import org.apache.logging.log4j.status.StatusLogger;
37  
38  /**
39   *
40   */
41  @Plugin(name = "http", category = Watcher.CATEGORY, elementType = Watcher.ELEMENT_TYPE, printObject = true)
42  @PluginAliases("https")
43  public class HttpWatcher extends AbstractWatcher {
44  
45      private Logger LOGGER = StatusLogger.getLogger();
46  
47      private SslConfiguration sslConfiguration;
48      private URL url;
49      private volatile long lastModifiedMillis;
50      private static final int NOT_MODIFIED = 304;
51      private static final int OK = 200;
52      private static final int BUF_SIZE = 1024;
53      private static final String HTTP = "http";
54      private static final String HTTPS = "https";
55  
56      public HttpWatcher(final Configuration configuration, final Reconfigurable reconfigurable,
57          final List<ConfigurationListener> configurationListeners, long lastModifiedMillis) {
58          super(configuration, reconfigurable, configurationListeners);
59          sslConfiguration = SslConfigurationFactory.getSslConfiguration();
60          this.lastModifiedMillis = lastModifiedMillis;
61      }
62  
63      @Override
64      public long getLastModified() {
65          return lastModifiedMillis;
66      }
67  
68      @Override
69      public boolean isModified() {
70          return refreshConfiguration();
71      }
72  
73      @Override
74      public void watching(Source source) {
75          if (!source.getURI().getScheme().equals(HTTP) && !source.getURI().getScheme().equals(HTTPS)) {
76              throw new IllegalArgumentException(
77                  "HttpWatcher requires a url using the HTTP or HTTPS protocol, not " + source.getURI().getScheme());
78          }
79          try {
80              url = source.getURI().toURL();
81          } catch (MalformedURLException ex) {
82              throw new IllegalArgumentException("Invalid URL for HttpWatcher " + source.getURI(), ex);
83          }
84          super.watching(source);
85      }
86  
87      @Override
88      public Watcher newWatcher(Reconfigurable reconfigurable, List<ConfigurationListener> listeners,
89          long lastModifiedMillis) {
90          HttpWatcher watcher = new HttpWatcher(getConfiguration(), reconfigurable, listeners, lastModifiedMillis);
91          if (getSource() != null) {
92              watcher.watching(getSource());
93          }
94          return watcher;
95      }
96  
97      private boolean refreshConfiguration() {
98          try {
99              final HttpURLConnection urlConnection = UrlConnectionFactory.createConnection(url, lastModifiedMillis,
100                 sslConfiguration);
101             urlConnection.connect();
102 
103             try {
104                 int code = urlConnection.getResponseCode();
105                 switch (code) {
106                     case NOT_MODIFIED: {
107                         LOGGER.debug("Configuration Not Modified");
108                         return false;
109                     }
110                     case OK: {
111                         try (InputStream is = urlConnection.getInputStream()) {
112                             ConfigurationSource configSource = getConfiguration().getConfigurationSource();
113                             configSource.setData(readStream(is));
114                             lastModifiedMillis = urlConnection.getLastModified();
115                             configSource.setModifiedMillis(lastModifiedMillis);
116                             LOGGER.debug("Content was modified for {}", url.toString());
117                             return true;
118                         } catch (final IOException e) {
119                             try (InputStream es = urlConnection.getErrorStream()) {
120                                 LOGGER.info("Error accessing configuration at {}: {}", url, readStream(es));
121                             } catch (final IOException ioe) {
122                                 LOGGER.error("Error accessing configuration at {}: {}", url, e.getMessage());
123                             }
124                             return false;
125                         }
126                     }
127                     default: {
128                         if (code < 0) {
129                             LOGGER.info("Invalid response code returned");
130                         } else {
131                             LOGGER.info("Unexpected response code returned {}", code);
132                         }
133                         return false;
134                     }
135                 }
136             } catch (final IOException ioe) {
137                 LOGGER.error("Error accessing configuration at {}: {}", url, ioe.getMessage());
138             }
139         } catch (final IOException ioe) {
140             LOGGER.error("Error connecting to configuration at {}: {}", url, ioe.getMessage());
141         }
142         return false;
143     }
144 
145     private byte[] readStream(InputStream is) throws IOException {
146         ByteArrayOutputStream result = new ByteArrayOutputStream();
147         byte[] buffer = new byte[BUF_SIZE];
148         int length;
149         while ((length = is.read(buffer)) != -1) {
150             result.write(buffer, 0, length);
151         }
152         return result.toByteArray();
153     }
154 }