1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.mongodb;
18
19 import java.lang.reflect.Field;
20 import java.lang.reflect.Method;
21 import java.util.ArrayList;
22 import java.util.List;
23
24 import org.apache.logging.log4j.Logger;
25 import org.apache.logging.log4j.core.Core;
26 import org.apache.logging.log4j.core.config.plugins.Plugin;
27 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
28 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
29 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
30 import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters;
31 import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
32 import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidHost;
33 import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidPort;
34 import org.apache.logging.log4j.core.filter.AbstractFilterable;
35 import org.apache.logging.log4j.core.util.NameUtil;
36 import org.apache.logging.log4j.core.appender.nosql.NoSqlProvider;
37 import org.apache.logging.log4j.status.StatusLogger;
38 import org.apache.logging.log4j.util.LoaderUtil;
39 import org.apache.logging.log4j.util.Strings;
40
41 import com.mongodb.DB;
42 import com.mongodb.MongoClient;
43 import com.mongodb.MongoCredential;
44 import com.mongodb.ServerAddress;
45 import com.mongodb.WriteConcern;
46
47
48
49
50 @Plugin(name = "MongoDb", category = Core.CATEGORY_NAME, printObject = true)
51 public final class MongoDbProvider implements NoSqlProvider<MongoDbConnection> {
52
53 private static final WriteConcern DEFAULT_WRITE_CONCERN = WriteConcern.ACKNOWLEDGED;
54 private static final Logger LOGGER = StatusLogger.getLogger();
55 private static final int DEFAULT_PORT = 27017;
56 private static final int DEFAULT_COLLECTION_SIZE = 536870912;
57
58 private final String collectionName;
59 private final DB database;
60 private final String description;
61 private final WriteConcern writeConcern;
62 private final boolean isCapped;
63 private final Integer collectionSize;
64
65 private MongoDbProvider(final DB database, final WriteConcern writeConcern, final String collectionName,
66 final boolean isCapped, final Integer collectionSize, final String description) {
67 this.database = database;
68 this.writeConcern = writeConcern;
69 this.collectionName = collectionName;
70 this.isCapped = isCapped;
71 this.collectionSize = collectionSize;
72 this.description = "mongoDb{ " + description + " }";
73 }
74
75 @Override
76 public MongoDbConnection getConnection() {
77 return new MongoDbConnection(this.database, this.writeConcern, this.collectionName, this.isCapped, this.collectionSize);
78 }
79
80 @Override
81 public String toString() {
82 return this.description;
83 }
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108 @PluginFactory
109 public static MongoDbProvider createNoSqlProvider(
110 final String collectionName,
111 final String writeConcernConstant,
112 final String writeConcernConstantClassName,
113 final String databaseName,
114 final String server,
115 final String port,
116 final String userName,
117 final String password,
118 final String factoryClassName,
119 final String factoryMethodName) {
120 LOGGER.info("createNoSqlProvider");
121 return newBuilder().setCollectionName(collectionName).setWriteConcernConstant(writeConcernConstantClassName)
122 .setWriteConcernConstant(writeConcernConstant).setDatabaseName(databaseName).setServer(server)
123 .setPort(port).setUserName(userName).setPassword(password).setFactoryClassName(factoryClassName)
124 .setFactoryMethodName(factoryMethodName).build();
125 }
126
127 @PluginBuilderFactory
128 public static <B extends Builder<B>> B newBuilder() {
129 return new Builder<B>().asBuilder();
130 }
131
132 public static class Builder<B extends Builder<B>> extends AbstractFilterable.Builder<B>
133 implements org.apache.logging.log4j.core.util.Builder<MongoDbProvider> {
134
135 @PluginBuilderAttribute
136 @ValidHost
137 private String server = "localhost";
138
139 @PluginBuilderAttribute
140 @ValidPort
141 private String port = "" + DEFAULT_PORT;
142
143 @PluginBuilderAttribute
144 @Required(message = "No database name provided")
145 private String databaseName;
146
147 @PluginBuilderAttribute
148 @Required(message = "No collection name provided")
149 private String collectionName;
150
151 @PluginBuilderAttribute
152 private String userName;
153
154 @PluginBuilderAttribute(sensitive = true)
155 private String password;
156
157 @PluginBuilderAttribute("capped")
158 private boolean isCapped = false;
159
160 @PluginBuilderAttribute
161 private int collectionSize = DEFAULT_COLLECTION_SIZE;
162
163 @PluginBuilderAttribute
164 private String factoryClassName;
165
166 @PluginBuilderAttribute
167 private String factoryMethodName;
168
169 @PluginBuilderAttribute
170 private String writeConcernConstantClassName;
171
172 @PluginBuilderAttribute
173 private String writeConcernConstant;
174
175 public B setServer(final String server) {
176 this.server = server;
177 return asBuilder();
178 }
179
180 public B setPort(final String port) {
181 this.port = port;
182 return asBuilder();
183 }
184
185 public B setDatabaseName(final String databaseName) {
186 this.databaseName = databaseName;
187 return asBuilder();
188 }
189
190 public B setCollectionName(final String collectionName) {
191 this.collectionName = collectionName;
192 return asBuilder();
193 }
194
195 public B setUserName(final String userName) {
196 this.userName = userName;
197 return asBuilder();
198 }
199
200 public B setPassword(final String password) {
201 this.password = password;
202 return asBuilder();
203 }
204
205 public B setCapped(final boolean isCapped) {
206 this.isCapped = isCapped;
207 return asBuilder();
208 }
209
210 public B setCollectionSize(final int collectionSize) {
211 this.collectionSize = collectionSize;
212 return asBuilder();
213 }
214
215 public B setFactoryClassName(final String factoryClassName) {
216 this.factoryClassName = factoryClassName;
217 return asBuilder();
218 }
219
220 public B setFactoryMethodName(final String factoryMethodName) {
221 this.factoryMethodName = factoryMethodName;
222 return asBuilder();
223 }
224
225 public B setWriteConcernConstantClassName(final String writeConcernConstantClassName) {
226 this.writeConcernConstantClassName = writeConcernConstantClassName;
227 return asBuilder();
228 }
229
230 public B setWriteConcernConstant(final String writeConcernConstant) {
231 this.writeConcernConstant = writeConcernConstant;
232 return asBuilder();
233 }
234
235 @Override
236 public MongoDbProvider build() {
237 DB database;
238 String description;
239 if (Strings.isNotEmpty(factoryClassName) && Strings.isNotEmpty(factoryMethodName)) {
240 try {
241 final Class<?> factoryClass = LoaderUtil.loadClass(factoryClassName);
242 final Method method = factoryClass.getMethod(factoryMethodName);
243 final Object object = method.invoke(null);
244
245 if (object instanceof DB) {
246 database = (DB) object;
247 } else if (object instanceof MongoClient) {
248 if (Strings.isNotEmpty(databaseName)) {
249 database = ((MongoClient) object).getDB(databaseName);
250 } else {
251 LOGGER.error("The factory method [{}.{}()] returned a MongoClient so the database name is "
252 + "required.", factoryClassName, factoryMethodName);
253 return null;
254 }
255 } else if (object == null) {
256 LOGGER.error("The factory method [{}.{}()] returned null.", factoryClassName, factoryMethodName);
257 return null;
258 } else {
259 LOGGER.error("The factory method [{}.{}()] returned an unsupported type [{}].", factoryClassName,
260 factoryMethodName, object.getClass().getName());
261 return null;
262 }
263
264 description = "database=" + database.getName();
265 final List<ServerAddress> addresses = database.getMongo().getAllAddress();
266 if (addresses.size() == 1) {
267 description += ", server=" + addresses.get(0).getHost() + ", port=" + addresses.get(0).getPort();
268 } else {
269 description += ", servers=[";
270 for (final ServerAddress address : addresses) {
271 description += " { " + address.getHost() + ", " + address.getPort() + " } ";
272 }
273 description += "]";
274 }
275 } catch (final ClassNotFoundException e) {
276 LOGGER.error("The factory class [{}] could not be loaded.", factoryClassName, e);
277 return null;
278 } catch (final NoSuchMethodException e) {
279 LOGGER.error("The factory class [{}] does not have a no-arg method named [{}].", factoryClassName,
280 factoryMethodName, e);
281 return null;
282 } catch (final Exception e) {
283 LOGGER.error("The factory method [{}.{}()] could not be invoked.", factoryClassName, factoryMethodName,
284 e);
285 return null;
286 }
287 } else if (Strings.isNotEmpty(databaseName)) {
288 final List<MongoCredential> credentials = new ArrayList<>();
289 description = "database=" + databaseName;
290 if (Strings.isNotEmpty(userName) && Strings.isNotEmpty(password)) {
291 description += ", username=" + userName + ", passwordHash="
292 + NameUtil.md5(password + MongoDbProvider.class.getName());
293 credentials.add(MongoCredential.createCredential(userName, databaseName, password.toCharArray()));
294 }
295 try {
296 final int portInt = TypeConverters.convert(port, int.class, DEFAULT_PORT);
297 description += ", server=" + server + ", port=" + portInt;
298 database = new MongoClient(new ServerAddress(server, portInt), credentials).getDB(databaseName);
299 } catch (final Exception e) {
300 LOGGER.error(
301 "Failed to obtain a database instance from the MongoClient at server [{}] and " + "port [{}].",
302 server, port);
303 return null;
304 }
305 } else {
306 LOGGER.error("No factory method was provided so the database name is required.");
307 return null;
308 }
309
310 try {
311 database.getCollectionNames();
312 } catch (final Exception e) {
313 LOGGER.error(
314 "The database is not up, or you are not authenticated, try supplying a username and password to the MongoDB provider.",
315 e);
316 return null;
317 }
318
319 final WriteConcern writeConcern = toWriteConcern(writeConcernConstant, writeConcernConstantClassName);
320
321 return new MongoDbProvider(database, writeConcern, collectionName, isCapped, collectionSize, description);
322 }
323
324 private static WriteConcern toWriteConcern(final String writeConcernConstant,
325 final String writeConcernConstantClassName) {
326 WriteConcern writeConcern;
327 if (Strings.isNotEmpty(writeConcernConstant)) {
328 if (Strings.isNotEmpty(writeConcernConstantClassName)) {
329 try {
330 final Class<?> writeConcernConstantClass = LoaderUtil.loadClass(writeConcernConstantClassName);
331 final Field field = writeConcernConstantClass.getField(writeConcernConstant);
332 writeConcern = (WriteConcern) field.get(null);
333 } catch (final Exception e) {
334 LOGGER.error("Write concern constant [{}.{}] not found, using default.",
335 writeConcernConstantClassName, writeConcernConstant);
336 writeConcern = DEFAULT_WRITE_CONCERN;
337 }
338 } else {
339 writeConcern = WriteConcern.valueOf(writeConcernConstant);
340 if (writeConcern == null) {
341 LOGGER.warn("Write concern constant [{}] not found, using default.", writeConcernConstant);
342 writeConcern = DEFAULT_WRITE_CONCERN;
343 }
344 }
345 } else {
346 writeConcern = DEFAULT_WRITE_CONCERN;
347 }
348 return writeConcern;
349 }
350 }
351 }