You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gg...@apache.org on 2017/06/21 13:32:29 UTC
[6/6] karaf git commit: [KARAF-5008] maven:password command
[KARAF-5008] maven:password command
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/ecc21a3c
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/ecc21a3c
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/ecc21a3c
Branch: refs/heads/master-maven-commands
Commit: ecc21a3c45063ce1c88f7306f4e62865d24260d2
Parents: 750fd14
Author: Grzegorz Grzybek <gr...@gmail.com>
Authored: Tue Jun 20 12:17:46 2017 +0200
Committer: Grzegorz Grzybek <gr...@gmail.com>
Committed: Wed Jun 21 15:31:54 2017 +0200
----------------------------------------------------------------------
maven/core/pom.xml | 6 ++
.../karaf/maven/command/HttpProxyCommand.java | 2 +-
.../command/MavenConfigurationSupport.java | 60 ++++++++++-
.../maven/command/MavenSecuritySupport.java | 31 ++++++
.../karaf/maven/command/PasswordCommand.java | 103 +++++++++++++++++++
.../command/RepositoryEditCommandSupport.java | 2 +-
.../maven/command/RepositoryListCommand.java | 2 +-
.../karaf/maven/command/SummaryCommand.java | 2 +-
.../org/apache/karaf/maven/SettingsTest.java | 2 +
.../command/MavenConfigurationSupportTest.java | 60 +++++++++++
maven/core/src/test/resources/log4j.properties | 34 ++++++
11 files changed, 295 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/ecc21a3c/maven/core/pom.xml
----------------------------------------------------------------------
diff --git a/maven/core/pom.xml b/maven/core/pom.xml
index db2f2a7..a2a724f 100644
--- a/maven/core/pom.xml
+++ b/maven/core/pom.xml
@@ -66,6 +66,12 @@
<artifactId>maven-embedder</artifactId>
<version>3.0.3</version>
</dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/karaf/blob/ecc21a3c/maven/core/src/main/java/org/apache/karaf/maven/command/HttpProxyCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/HttpProxyCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/HttpProxyCommand.java
index 6f8b794..129aca5 100644
--- a/maven/core/src/main/java/org/apache/karaf/maven/command/HttpProxyCommand.java
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/HttpProxyCommand.java
@@ -27,7 +27,7 @@ import org.apache.maven.settings.Proxy;
@Command(scope = "maven", name = "http-proxy", description = "Manage HTTP proxy configuration for Maven remote repositories")
@Service
-public class HttpProxyCommand extends MavenConfigurationSupport {
+public class HttpProxyCommand extends MavenSecuritySupport {
@Option(name = "--add", description = "Adds HTTP proxy configuration to Maven settings", required = false, multiValued = false)
boolean add;
http://git-wip-us.apache.org/repos/asf/karaf/blob/ecc21a3c/maven/core/src/main/java/org/apache/karaf/maven/command/MavenConfigurationSupport.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/MavenConfigurationSupport.java b/maven/core/src/main/java/org/apache/karaf/maven/command/MavenConfigurationSupport.java
index 4c3416a..0588afd 100644
--- a/maven/core/src/main/java/org/apache/karaf/maven/command/MavenConfigurationSupport.java
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/MavenConfigurationSupport.java
@@ -17,10 +17,13 @@
package org.apache.karaf.maven.command;
import java.io.File;
+import java.io.IOException;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.LinkedHashSet;
@@ -28,10 +31,11 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
import org.apache.karaf.maven.core.MavenRepositoryURL;
import org.apache.karaf.shell.api.action.Action;
-import org.apache.karaf.shell.api.action.Option;
import org.apache.karaf.shell.api.action.lifecycle.Reference;
import org.apache.karaf.shell.api.console.Session;
import org.apache.karaf.shell.support.table.Row;
@@ -72,6 +76,12 @@ public abstract class MavenConfigurationSupport implements Action {
protected static final String PATTERN_PID_PROPERTY = "Explicit %s PID configuration (%s)";
+ protected static final String PATTERN_SECURITY_SETTINGS = "maven-security-settings-%d.xml";
+ protected static final Pattern RE_SECURITY_SETTINGS = Pattern.compile("maven-security-settings-(\\d+)\\.xml");
+ protected static final String PATTERN_SETTINGS = "maven-settings-%d.xml";
+ protected static final Pattern RE_SETTINGS = Pattern.compile("maven-settings-(\\d+)\\.xml");
+ private static final int MAX_SEQUENCE_SIZE = 10;
+
protected static final String PROPERTY_LOCAL_REPOSITORY = "localRepository";
protected static final String PROPERTY_DEFAULT_REPOSITORIES = "defaultRepositories";
protected static final String PROPERTY_REPOSITORIES = "repositories";
@@ -122,9 +132,6 @@ public abstract class MavenConfigurationSupport implements Action {
@Reference
protected Session session;
- @Option(name = "-x", aliases = { "--show-passwords" }, description = "Do not hide passwords related to Maven encryption", required = false, multiValued = false)
- boolean showPasswords;
-
@Override
final public Object execute() throws Exception {
Configuration c = cm.getConfiguration(PID);
@@ -144,7 +151,7 @@ public abstract class MavenConfigurationSupport implements Action {
localRepository = localRepository((String) c.getProperties().get(PID + "." + PROPERTY_LOCAL_REPOSITORY));
- if (showPasswords) {
+ if (showPasswords()) {
decryptSettings();
}
@@ -558,6 +565,49 @@ public abstract class MavenConfigurationSupport implements Action {
}
}
+ /**
+ * Asks for confirmation (user has to press <code>y</code>) after presenting a prompt
+ * @param prompt
+ * @return
+ */
+ protected boolean confirm(String prompt) throws IOException {
+ String response = session.readLine(prompt, null);
+ return "y".equals(response);
+ }
+
+ /**
+ * Returns new {@link File} that's part of fixed-size sequence. Keeps the sequence bounded.
+ * @param dataDir
+ * @param pattern
+ * @param fileNameFormat
+ * @return
+ */
+ protected File nextSequenceFile(File dataDir, Pattern pattern, String fileNameFormat) {
+ File[] files = dataDir.listFiles((dir, name) -> pattern.matcher(name).matches());
+ File result = null;
+ if (files != null && files.length > 0) {
+ List<String> names = new ArrayList<>(Arrays.stream(files).map(File::getName)
+ .collect(TreeSet<String>::new, TreeSet::add, TreeSet::addAll));
+
+ names.add(String.format(fileNameFormat, new Date().getTime()));
+
+ while (names.size() > MAX_SEQUENCE_SIZE) {
+ String name = names.remove(0);
+ new File(dataDir, name).delete();
+ }
+ result = new File(dataDir, names.get(names.size() - 1));
+ }
+ if (result == null) {
+ result = new File(dataDir, String.format(fileNameFormat, new Date().getTime()));
+ }
+
+ return result;
+ }
+
+ protected boolean showPasswords() {
+ return false;
+ }
+
protected static class SourceAnd<T> {
String source;
T value;
http://git-wip-us.apache.org/repos/asf/karaf/blob/ecc21a3c/maven/core/src/main/java/org/apache/karaf/maven/command/MavenSecuritySupport.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/MavenSecuritySupport.java b/maven/core/src/main/java/org/apache/karaf/maven/command/MavenSecuritySupport.java
new file mode 100644
index 0000000..6c46c9d
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/MavenSecuritySupport.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.karaf.maven.command;
+
+import org.apache.karaf.shell.api.action.Option;
+
+public abstract class MavenSecuritySupport extends MavenConfigurationSupport {
+
+ @Option(name = "-x", aliases = { "--show-passwords" }, description = "Do not hide passwords related to Maven encryption", required = false, multiValued = false)
+ boolean showPasswords;
+
+ @Override
+ protected boolean showPasswords() {
+ return showPasswords;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/ecc21a3c/maven/core/src/main/java/org/apache/karaf/maven/command/PasswordCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/PasswordCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/PasswordCommand.java
new file mode 100644
index 0000000..ed44b35
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/PasswordCommand.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.karaf.maven.command;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.util.Dictionary;
+
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.osgi.service.cm.Configuration;
+import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
+import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
+import org.sonatype.plexus.components.sec.dispatcher.model.io.xpp3.SecurityConfigurationXpp3Writer;
+
+@Command(scope = "maven", name = "password", description = "Manage passwords for remote repositories and proxies")
+@Service
+public class PasswordCommand extends MavenConfigurationSupport {
+
+ @Option(name = "-ep", aliases = { "--encrypt-password" }, description = "Encrypts passwords to use for remote repositories and proxies, see \"mvn -ep\"", required = false, multiValued = false)
+ boolean ep;
+
+ @Option(name = "-emp", aliases = { "--encrypt-master-password" }, description = "Encrypts master password used to encrypt/decrypt other passwords, see \"mvn -emp\"", required = false, multiValued = false)
+ boolean emp;
+
+ @Option(name = "-p", aliases = { "--persist" }, description = "", required = false, multiValued = false)
+ boolean persist;
+
+ @Override
+ public void doAction(String prefix, Dictionary<String, Object> config) throws Exception {
+ if (ep && emp) {
+ System.err.println("Please specify only one of --encrypt-password and --encrypt-master-password");
+ return;
+ }
+
+ if (ep && persist) {
+ System.err.println("Ordinary passwords are not persisted - use the encrypted password in either <proxy> or <server>");
+ return;
+ }
+
+ if (ep) {
+ // encrypt password using master password
+ if (masterPassword == null) {
+ System.err.println("Master password is not available");
+ return;
+ }
+ String password = session.readLine("Password to encrypt: ", '*');
+ System.out.println("Encrypted password: " + cipher.encryptAndDecorate(password, masterPassword));
+ return;
+ }
+
+ if (emp) {
+ if (persist && !confirm("Maven security settings will be stored in new file. This file will be used in org.ops4j.pax.url.mvn.security property. Continue? (y/N) ")) {
+ return;
+ }
+
+ // encrypt master password using DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION
+ String password = session.readLine("Master password to encrypt: ", '*');
+ String encryptedPassword = cipher.encryptAndDecorate(password, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION);
+ System.out.println("Encrypted master password: " + encryptedPassword);
+ if (persist) {
+ SettingsSecurity settingsSecurity = new SettingsSecurity();
+ settingsSecurity.setMaster(encryptedPassword);
+ File dataDir = context.getDataFile(".");
+ if (!dataDir.isDirectory()) {
+ System.err.println("Can't access data directory for " + context.getBundle().getSymbolicName() + " bundle");
+ }
+ File newSecuritySettingsFile = nextSequenceFile(dataDir, RE_SECURITY_SETTINGS, PATTERN_SECURITY_SETTINGS);
+ try (FileWriter fw = new FileWriter(newSecuritySettingsFile)) {
+ new SecurityConfigurationXpp3Writer().write(fw, settingsSecurity);
+ }
+
+ System.out.println("New security settings stored in \"" + newSecuritySettingsFile.getCanonicalPath() + "\"");
+
+ Configuration cmConfig = cm.getConfiguration(PID);
+ config.put(prefix + PROPERTY_SECURITY_FILE, newSecuritySettingsFile.getCanonicalPath());
+ cmConfig.update(config);
+ }
+ return;
+ }
+ }
+
+ @Override
+ protected boolean showPasswords() {
+ return true;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/ecc21a3c/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryEditCommandSupport.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryEditCommandSupport.java b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryEditCommandSupport.java
index 0179b02..12700d0 100644
--- a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryEditCommandSupport.java
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryEditCommandSupport.java
@@ -21,7 +21,7 @@ import java.util.Dictionary;
import org.apache.karaf.maven.core.MavenRepositoryURL;
import org.apache.karaf.shell.api.action.Option;
-public abstract class RepositoryEditCommandSupport extends MavenConfigurationSupport {
+public abstract class RepositoryEditCommandSupport extends MavenSecuritySupport {
@Override
public void doAction(String prefix, Dictionary<String, Object> config) throws Exception {
http://git-wip-us.apache.org/repos/asf/karaf/blob/ecc21a3c/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryListCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryListCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryListCommand.java
index 90c7c26..ddc2ea5 100644
--- a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryListCommand.java
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryListCommand.java
@@ -28,7 +28,7 @@ import org.apache.maven.settings.Server;
@Command(scope = "maven", name = "repository-list", description = "Maven repository summary.")
@Service
-public class RepositoryListCommand extends MavenConfigurationSupport {
+public class RepositoryListCommand extends MavenSecuritySupport {
@Option(name = "-v", aliases = { "--verbose" }, description = "Show additional information (policies, source)", required = false, multiValued = false)
boolean verbose;
http://git-wip-us.apache.org/repos/asf/karaf/blob/ecc21a3c/maven/core/src/main/java/org/apache/karaf/maven/command/SummaryCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/SummaryCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/SummaryCommand.java
index 1678239..3f7e91a 100644
--- a/maven/core/src/main/java/org/apache/karaf/maven/command/SummaryCommand.java
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/SummaryCommand.java
@@ -28,7 +28,7 @@ import org.apache.maven.settings.Proxy;
@Command(scope = "maven", name = "summary", description = "Maven configuration summary.")
@Service
-public class SummaryCommand extends MavenConfigurationSupport {
+public class SummaryCommand extends MavenSecuritySupport {
@Option(name = "-p", aliases = { "--property-ids" }, description = "Use PID property identifiers instead of their names", required = false, multiValued = false)
boolean propertyIds;
http://git-wip-us.apache.org/repos/asf/karaf/blob/ecc21a3c/maven/core/src/test/java/org/apache/karaf/maven/SettingsTest.java
----------------------------------------------------------------------
diff --git a/maven/core/src/test/java/org/apache/karaf/maven/SettingsTest.java b/maven/core/src/test/java/org/apache/karaf/maven/SettingsTest.java
index b0d9110..3ecaf30 100644
--- a/maven/core/src/test/java/org/apache/karaf/maven/SettingsTest.java
+++ b/maven/core/src/test/java/org/apache/karaf/maven/SettingsTest.java
@@ -53,9 +53,11 @@ public class SettingsTest {
// non-master password ('mvn -ep admin')
DefaultPlexusCipher plexusCipher = new DefaultPlexusCipher();
System.out.println(plexusCipher.decrypt("{EhjazkVpkMoHjAgaUKX+UxeXn9lsJGHst2uFKmhNZ8U=}", "admin"));
+ System.out.println(plexusCipher.decrypt("{oWE12FbirwYHNit93TAMA+OC/GJge2r9FuzI8kOuHlA=}", "settings.security"));
// master password (`mvn -emp admin`)
PBECipher cipher = new PBECipher();
+ System.out.println(cipher.decrypt64("EhjazkVpkMoHjAgaUKX+UxeXn9lsJGHst2uFKmhNZ8U=","admin"));
System.out.println(cipher.decrypt64("oWE12FbirwYHNit93TAMA+OC/GJge2r9FuzI8kOuHlA=","settings.security"));
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/ecc21a3c/maven/core/src/test/java/org/apache/karaf/maven/command/MavenConfigurationSupportTest.java
----------------------------------------------------------------------
diff --git a/maven/core/src/test/java/org/apache/karaf/maven/command/MavenConfigurationSupportTest.java b/maven/core/src/test/java/org/apache/karaf/maven/command/MavenConfigurationSupportTest.java
new file mode 100644
index 0000000..16ffc3f
--- /dev/null
+++ b/maven/core/src/test/java/org/apache/karaf/maven/command/MavenConfigurationSupportTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.karaf.maven.command;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.regex.Pattern;
+
+import org.junit.Test;
+import shaded.org.apache.commons.io.FileUtils;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class MavenConfigurationSupportTest {
+
+ @Test
+ public void sequenceFiles() throws IOException {
+ File dataDir = new File("target/data");
+ FileUtils.deleteDirectory(dataDir);
+ dataDir.mkdirs();
+
+ MavenConfigurationSupport support = new MavenConfigurationSupport() {
+ @Override
+ protected void doAction(String prefix, Dictionary<String, Object> config) throws Exception { }
+ };
+
+ File newFile = support.nextSequenceFile(dataDir, Pattern.compile("file-(\\d+).txt"), "file-%04d.txt");
+ assertThat(newFile.getName(), equalTo("file-0001.txt"));
+
+ try (FileWriter fw = new FileWriter(new File(dataDir, "file-abcd.txt"))) {
+ fw.write("~");
+ }
+ newFile = support.nextSequenceFile(dataDir, Pattern.compile("file-(\\d+).txt"), "file-%04d.txt");
+ assertThat(newFile.getName(), equalTo("file-0001.txt"));
+
+ try (FileWriter fw = new FileWriter(new File(dataDir, "file-0004.txt"))) {
+ fw.write("~");
+ }
+ newFile = support.nextSequenceFile(dataDir, Pattern.compile("file-(\\d+).txt"), "file-%04d.txt");
+ assertThat(newFile.getName(), equalTo("file-0005.txt"));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/ecc21a3c/maven/core/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/maven/core/src/test/resources/log4j.properties b/maven/core/src/test/resources/log4j.properties
new file mode 100644
index 0000000..e1cbdd1
--- /dev/null
+++ b/maven/core/src/test/resources/log4j.properties
@@ -0,0 +1,34 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You 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.
+## ---------------------------------------------------------------------------
+
+#
+# The logging properties used during tests..
+#
+log4j.rootLogger=DEBUG, console, file
+
+# Console will only display warnnings
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+#log4j.appender.console.threshold=WARN
+
+# File appender will contain all info messages
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d | %-5p | %m | %c | %t%n
+log4j.appender.file.file=target/test.log
+log4j.appender.file.append=true