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/22 11:15:32 UTC

[1/9] karaf git commit: [KARAF-5008] Clean up org.apache.maven.* dependencies (align to 3.0.3 for now)

Repository: karaf
Updated Branches:
  refs/heads/master 83d41351e -> e4585a99c


[KARAF-5008] Clean up org.apache.maven.* dependencies (align to 3.0.3 for now)

[KARAF-5008] Clean up org.apache.maven.wagon.* dependencies (align to 2.10 for now)


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/60e25fa1
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/60e25fa1
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/60e25fa1

Branch: refs/heads/master
Commit: 60e25fa18392343f8697b46b35530d5da9e4199d
Parents: 83d4135
Author: Grzegorz Grzybek <gr...@gmail.com>
Authored: Fri Mar 10 12:03:27 2017 +0100
Committer: Grzegorz Grzybek <gr...@gmail.com>
Committed: Wed Jun 21 15:31:41 2017 +0200

----------------------------------------------------------------------
 pom.xml                                     | 40 +++++++++++++++++++-----
 tooling/karaf-maven-plugin/pom.xml          |  7 +----
 tooling/karaf-services-maven-plugin/pom.xml |  7 +----
 3 files changed, 35 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/60e25fa1/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 7cf0e15..a7367d2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -252,7 +252,8 @@
         <jline.version>3.3.0</jline.version>
         <jsw.version>3.2.3</jsw.version>
         <log4j.version>1.2.17</log4j.version>
-        <maven.version>2.0.9</maven.version>
+        <maven.version>3.0.3</maven.version>
+        <maven.wagon.version>2.10</maven.wagon.version>
         <org.osgi.service.jdbc.version>1.0.0</org.osgi.service.jdbc.version>
         <org.osgi.service.jpa.version>1.0.0</org.osgi.service.jpa.version>
         <osgi.version>6.0.0</osgi.version>
@@ -1062,10 +1063,40 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.maven</groupId>
-                <artifactId>maven-project</artifactId>
+                <artifactId>maven-compat</artifactId>
                 <version>${maven.version}</version>
             </dependency>
             <dependency>
+                <groupId>org.apache.maven</groupId>
+                <artifactId>maven-core</artifactId>
+                <version>${maven.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.maven</groupId>
+                <artifactId>maven-artifact</artifactId>
+                <version>${maven.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.maven.shared</groupId>
+                <artifactId>maven-dependency-tree</artifactId>
+                <version>2.2</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-provider-api</artifactId>
+                <version>${maven.wagon.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-http</artifactId>
+                <version>${maven.wagon.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-http-lightweight</artifactId>
+                <version>${maven.wagon.version}</version>
+            </dependency>
+            <dependency>
                 <groupId>xerces</groupId>
                 <artifactId>xercesImpl</artifactId>
                 <version>${xerces.version}</version>
@@ -1177,11 +1208,6 @@
                 <version>${jline.version}</version>
             </dependency>
             <dependency>
-                <groupId>org.apache.maven.artifact</groupId>
-                <artifactId>maven-artifact</artifactId>
-                <version>3.0-alpha-1</version>
-            </dependency>
-            <dependency>
                 <groupId>org.slf4j</groupId>
                 <artifactId>slf4j-api</artifactId>
                 <version>${slf4j.version}</version>

http://git-wip-us.apache.org/repos/asf/karaf/blob/60e25fa1/tooling/karaf-maven-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/pom.xml b/tooling/karaf-maven-plugin/pom.xml
index d88899b..ffcc052 100644
--- a/tooling/karaf-maven-plugin/pom.xml
+++ b/tooling/karaf-maven-plugin/pom.xml
@@ -42,7 +42,6 @@
         <dependency>
             <groupId>org.apache.maven</groupId>
             <artifactId>maven-plugin-api</artifactId>
-            <version>3.0.3</version>
         </dependency>
         <dependency>
             <groupId>org.sonatype.aether</groupId>
@@ -67,17 +66,14 @@
         <dependency>
             <groupId>org.apache.maven</groupId>
             <artifactId>maven-artifact</artifactId>
-            <version>3.0.3</version>
         </dependency>
         <dependency>
             <groupId>org.apache.maven</groupId>
             <artifactId>maven-core</artifactId>
-            <version>3.0.3</version>
         </dependency>
         <dependency>
             <groupId>org.apache.maven</groupId>
             <artifactId>maven-compat</artifactId>
-            <version>3.0.3</version>
         </dependency>
         <dependency>
             <groupId>org.apache.maven.plugin-tools</groupId>
@@ -87,7 +83,6 @@
         <dependency>
             <groupId>org.apache.maven.wagon</groupId>
             <artifactId>wagon-http</artifactId>
-            <version>2.10</version>
         </dependency>
         <dependency>
             <groupId>org.apache.karaf</groupId>
@@ -105,7 +100,7 @@
         <dependency>
             <groupId>org.apache.maven.shared</groupId>
             <artifactId>maven-filtering</artifactId>
-            <version>1.0-beta-4</version>
+            <version>3.1.1</version>
         </dependency>
         <dependency>
             <groupId>org.codehaus.plexus</groupId>

http://git-wip-us.apache.org/repos/asf/karaf/blob/60e25fa1/tooling/karaf-services-maven-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-services-maven-plugin/pom.xml b/tooling/karaf-services-maven-plugin/pom.xml
index 728999f..4dbaa56 100644
--- a/tooling/karaf-services-maven-plugin/pom.xml
+++ b/tooling/karaf-services-maven-plugin/pom.xml
@@ -41,7 +41,6 @@
         <dependency>
             <groupId>org.apache.maven</groupId>
             <artifactId>maven-plugin-api</artifactId>
-            <version>3.0.3</version>
         </dependency>
         <dependency>
             <groupId>org.apache.maven.plugin-tools</groupId>
@@ -71,22 +70,18 @@
         <dependency>
             <groupId>org.apache.maven</groupId>
             <artifactId>maven-artifact</artifactId>
-            <version>3.0.3</version>
         </dependency>
         <dependency>
             <groupId>org.apache.maven</groupId>
             <artifactId>maven-core</artifactId>
-            <version>3.0.3</version>
         </dependency>
         <dependency>
             <groupId>org.apache.maven</groupId>
             <artifactId>maven-compat</artifactId>
-            <version>3.0.3</version>
         </dependency>
         <dependency>
             <groupId>org.apache.maven.wagon</groupId>
             <artifactId>wagon-http-lightweight</artifactId>
-            <version>2.6</version>
         </dependency>
         <dependency>
             <groupId>org.sonatype.plexus</groupId>
@@ -120,7 +115,7 @@
         <dependency>
             <groupId>org.apache.maven.shared</groupId>
             <artifactId>maven-filtering</artifactId>
-            <version>1.0-beta-4</version>
+            <version>3.1.1</version>
         </dependency>
         <dependency>
             <groupId>org.codehaus.plexus</groupId>


[2/9] karaf git commit: [KARAF-5008] Initial "maven/core" module and first maven:summary command

Posted by gg...@apache.org.
[KARAF-5008] Initial "maven/core" module and first maven:summary command

[KARAF-5008] maven:summary - update and checksum policy information

[KARAF-5008] Additional maven:summary details and settings-security.xml handling

[KARAF-5008] maven:summary for repositories, defaultRepositories and proxies

[KARAF-5008] maven:http-proxy command to list proxies + optional password decryption

[KARAF-5008] org.apache.karaf.maven.SettingsTest#encryptMavenPassword


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/750fd144
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/750fd144
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/750fd144

Branch: refs/heads/master
Commit: 750fd144a1e4c30a743e736af516114b13acd224
Parents: 60e25fa
Author: Grzegorz Grzybek <gr...@gmail.com>
Authored: Mon Mar 13 15:42:53 2017 +0100
Committer: Grzegorz Grzybek <gr...@gmail.com>
Committed: Wed Jun 21 15:31:52 2017 +0200

----------------------------------------------------------------------
 maven/core/pom.xml                              | 129 +++++
 .../karaf/maven/command/HttpProxyCommand.java   |  84 +++
 .../command/MavenConfigurationSupport.java      | 578 +++++++++++++++++++
 .../maven/command/RepositoryAddCommand.java     |  35 ++
 .../command/RepositoryEditCommandSupport.java   |  35 ++
 .../maven/command/RepositoryListCommand.java    | 127 ++++
 .../maven/command/RepositoryRemoveCommand.java  |  31 +
 .../karaf/maven/command/SummaryCommand.java     | 239 ++++++++
 .../karaf/maven/core/MavenRepositoryURL.java    | 386 +++++++++++++
 .../src/main/resources/OSGI-INF/bundle.info     |  36 ++
 .../org/apache/karaf/maven/SettingsTest.java    |  62 ++
 .../src/test/resources/reference-settings.xml   |  82 +++
 maven/pom.xml                                   |  40 ++
 pom.xml                                         |  11 +
 14 files changed, 1875 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/maven/core/pom.xml
----------------------------------------------------------------------
diff --git a/maven/core/pom.xml b/maven/core/pom.xml
new file mode 100644
index 0000000..db2f2a7
--- /dev/null
+++ b/maven/core/pom.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.karaf</groupId>
+        <artifactId>karaf</artifactId>
+        <version>4.2.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.apache.karaf.maven</groupId>
+    <artifactId>org.apache.karaf.maven.core</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache Karaf :: Maven :: Core</name>
+    <description>This bundle provides services and commands for Maven diagnostics.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.url</groupId>
+            <artifactId>pax-url-aether</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-settings</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-settings-builder</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-embedder</artifactId>
+            <version>3.0.3</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*.info</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.karaf.tooling</groupId>
+                <artifactId>karaf-services-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.apache.karaf.maven
+                        </Export-Package>
+                        <Import-Package>
+                            !org.apache.maven*,
+                            !org.codehaus.plexus*,
+                            !org.sonatype.aether*,
+                            !org.sonatype.guice*,
+                            *,
+                        </Import-Package>
+                        <Private-Package>
+                            org.apache.karaf.maven.command,
+                            org.apache.karaf.maven.core,
+                            org.apache.maven.settings.*;-split-package:=merge-first,
+                            org.apache.maven.execution;-split-package:=merge-first,
+                            org.apache.maven.model;-split-package:=merge-first,
+                            org.codehaus.plexus.component.annotations,
+                            org.sonatype.plexus.components.sec.dispatcher*,
+                            org.sonatype.plexus.components.cipher,
+                            org.codehaus.plexus.interpolation,
+                            org.codehaus.plexus.logging,
+                            org.codehaus.plexus.util,
+                            org.codehaus.plexus.util.xml,
+                            org.codehaus.plexus.util.xml.pull,
+                            org.codehaus.plexus.interpolation.os,
+                        </Private-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/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
new file mode 100644
index 0000000..6f8b794
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/HttpProxyCommand.java
@@ -0,0 +1,84 @@
+/*
+ * 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.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.apache.karaf.shell.support.table.Row;
+import org.apache.karaf.shell.support.table.ShellTable;
+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 {
+
+    @Option(name = "--add", description = "Adds HTTP proxy configuration to Maven settings", required = false, multiValued = false)
+    boolean add;
+
+    @Option(name = "--remove", description = "Removes HTTP proxy configuration from Maven settings", required = false, multiValued = false)
+    boolean remove;
+
+    @Override
+    public void doAction(String prefix, Dictionary<String, Object> config) throws Exception {
+        if (add && remove) {
+            System.err.println("Please specify only one of --add and --remove");
+            return;
+        }
+
+        if (add) {
+            // add
+        }
+
+        if (remove) {
+            // remove
+        }
+
+        // list (also after --add or --remove)
+        System.out.println();
+        if (mavenSettings != null && mavenSettings.getProxies() != null && mavenSettings.getProxies().size() > 0) {
+            ShellTable table = new ShellTable();
+            table.column("ID");
+            table.column("Host");
+            table.column("Port");
+            table.column("Username");
+            if (showPasswords) {
+                table.column("Password");
+            }
+            for (Proxy proxy : mavenSettings.getProxies()) {
+                Row row = table.addRow();
+                row.addContent(proxy.getId(), proxy.getHost(), proxy.getPort());
+                row.addContent(proxy.getUsername() != null ? proxy.getUsername() : "");
+                if (showPasswords) {
+                    addPasswordInfo(row, proxyPasswords, proxy.getId(), proxy.getPassword());
+                }
+            }
+            table.print(System.out);
+        } else {
+            System.out.print("No HTTP proxies configured");
+            if (settings != null && settings.value != null) {
+                System.out.print(" in " + settings.value);
+            }
+            System.out.println();
+        }
+
+        System.out.println();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/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
new file mode 100644
index 0000000..4c3416a
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/MavenConfigurationSupport.java
@@ -0,0 +1,578 @@
+/*
+ * 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.lang.reflect.Field;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+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;
+import org.apache.maven.settings.Profile;
+import org.apache.maven.settings.Proxy;
+import org.apache.maven.settings.Repository;
+import org.apache.maven.settings.Server;
+import org.apache.maven.settings.Settings;
+import org.apache.maven.settings.building.DefaultSettingsBuilder;
+import org.apache.maven.settings.building.DefaultSettingsBuilderFactory;
+import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
+import org.apache.maven.settings.building.SettingsBuildingException;
+import org.apache.maven.settings.building.SettingsBuildingRequest;
+import org.apache.maven.settings.building.SettingsBuildingResult;
+import org.apache.maven.settings.building.SettingsProblem;
+import org.apache.maven.settings.crypto.DefaultSettingsDecrypter;
+import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
+import org.apache.maven.settings.crypto.SettingsDecryptionResult;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
+import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
+import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
+import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
+
+/**
+ * <p>Base class for <code>maven:</code> commands.</p>
+ * <p>Important: even if it duplicates some code from pax-url-aether, this should be treated as verification code of
+ * how pax-url-aether should interact with <code>org.ops4j.pax.url.mvn</code> PID configuration.</p>
+ */
+public abstract class MavenConfigurationSupport implements Action {
+
+    public static Logger LOG = LoggerFactory.getLogger(MavenConfigurationSupport.class);
+    protected static final String PID = "org.ops4j.pax.url.mvn";
+
+    protected static final String PATTERN_PID_PROPERTY = "Explicit %s PID configuration (%s)";
+
+    protected static final String PROPERTY_LOCAL_REPOSITORY = "localRepository";
+    protected static final String PROPERTY_DEFAULT_REPOSITORIES = "defaultRepositories";
+    protected static final String PROPERTY_REPOSITORIES = "repositories";
+    protected static final String PROPERTY_SETTINGS_FILE = "settings";
+    protected static final String PROPERTY_SECURITY_FILE = "security";
+    protected static final String PROPERTY_GLOBAL_UPDATE_POLICY = "globalUpdatePolicy";
+    protected static final String PROPERTY_GLOBAL_CHECKSUM_POLICY = "globalChecksumPolicy";
+    protected static final String PROPERTY_UPDATE_RELEASES = "updateReleases";
+    protected static final String REQUIRE_CONFIG_ADMIN_CONFIG = "requireConfigAdminConfig";
+    protected static final String PROPERTY_USE_FALLBACK_REPOSITORIES = "useFallbackRepositories";
+    protected static final String PROPERTY_OFFLINE = "offline";
+    protected static final String PROPERTY_CERTIFICATE_CHECK = "certificateCheck";
+
+    // TODO timeout options {
+    protected static final String PROPERTY_TIMEOUT = "timeout";
+    protected static final String PROPERTY_SOCKET_SO_TIMEOUT = "socket.readTimeout";
+    protected static final String PROPERTY_SOCKET_SO_KEEPALIVE = "socket.keepAlive";
+    protected static final String PROPERTY_SOCKET_SO_LINGER = "socket.linger";
+    protected static final String PROPERTY_SOCKET_SO_REUSEADDRESS = "socket.reuseAddress";
+    protected static final String PROPERTY_SOCKET_TCP_NODELAY = "socket.tcpNoDelay";
+    protected static final String PROPERTY_SOCKET_CONNECTION_TIMEOUT = "socket.connectionTimeout";
+    protected static final String PROPERTY_CONNECTION_BUFFER_SIZE = "connection.bufferSize";
+    protected static final String PROPERTY_CONNECTION_RETRY_COUNT = "connection.retryCount";
+    // }
+
+    protected SourceAnd<File> localRepository;
+    protected SourceAnd<File> settings;
+    protected Settings mavenSettings;
+    protected SourceAnd<File> securitySettings;
+    protected SettingsSecurity mavenSecuritySettings;
+
+    protected Map<String, Server> servers = new HashMap<>();
+    protected Map<String, String> serverPasswords = new HashMap<>();
+    protected Map<String, String> proxyPasswords = new HashMap<>();
+
+    protected List<String> warnings = new LinkedList<>();
+
+    private static final String masterMasterPassword = DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION;
+    protected String masterPassword;
+    protected DefaultPlexusCipher cipher;
+
+    @Reference
+    protected ConfigurationAdmin cm;
+
+    @Reference
+    protected BundleContext context;
+
+    @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);
+
+        if (c != null && c.getProperties() != null) {
+            try {
+                cipher = new DefaultPlexusCipher();
+                securitySettings = securitySettings((String) c.getProperties().get(PID + "." + PROPERTY_SECURITY_FILE));
+                if (securitySettings != null && securitySettings.value != null) {
+                    mavenSecuritySettings = readSecuritySettings(securitySettings.value);
+                }
+
+                settings = settings((String) c.getProperties().get(PID + "." + PROPERTY_SETTINGS_FILE));
+                if (settings != null && settings.value != null) {
+                    mavenSettings = readSettings(settings.value);
+                }
+
+                localRepository = localRepository((String) c.getProperties().get(PID + "." + PROPERTY_LOCAL_REPOSITORY));
+
+                if (showPasswords) {
+                    decryptSettings();
+                }
+
+                doAction(PID + ".", c.getProperties());
+            } catch (Exception e) {
+                System.err.println(e.getMessage());
+                LOG.error(e.getMessage(), e);
+            }
+        } else {
+            System.err.printf("Can't access \"%s\" configuration\n", PID);
+        }
+
+        return null;
+    }
+
+    /**
+     * Performs command action on <strong>existing</strong> <code>org.ops4j.pax.url.mvn</code>
+     * PID configuration
+     * @param prefix
+     * @param config
+     */
+    abstract protected void doAction(String prefix, Dictionary<String, Object> config) throws Exception;
+
+    /**
+     * Gets effective location of <code>settings.xml</code> file - according to pax-url-aether rules
+     * @param cmProperty property obtained from Config Admin
+     * @return
+     */
+    protected SourceAnd<File> settings(String cmProperty) {
+        SourceAnd<File> result = new SourceAnd<>();
+        URL locationUrl = null;
+        String probableErrorMessage = null;
+
+        // 1. PID + ".settings"
+        if (cmProperty != null && !"".equals(cmProperty.trim())) {
+            result.source = String.format(PATTERN_PID_PROPERTY, PID, PID + "." + PROPERTY_SETTINGS_FILE);
+            try {
+                locationUrl = new URL(cmProperty);
+                probableErrorMessage = String.format("%s configured in %s.%s is not accessible",
+                        locationUrl, PID, PROPERTY_SETTINGS_FILE);
+            } catch (MalformedURLException e) {
+                File file = new File(cmProperty);
+                if (file.isFile()) {
+                    result.value = file;
+                    return result;
+                }
+            }
+        }
+
+        if (locationUrl == null) {
+            // 2. System.getProperty("user.home") + "/.m2/settings.xml"
+            File file = new File(System.getProperty("user.home") + "/.m2/settings.xml");
+            if (file.isFile()) {
+                result.value = file;
+                result.source = "Implicit ${user.home}/.m2/settings.xml";
+                return result;
+            }
+
+            // 3. System.getProperty("maven.home") + "/conf/settings.xml"
+            file = new File(System.getProperty("maven.home") + "/conf/settings.xml");
+            if (file.isFile()) {
+                result.value = file;
+                result.source = "Implicit ${maven.home}/conf/settings.xml";
+                return result;
+            }
+
+            // 4. System.getenv("M2_HOME") + "/conf/settings.xml"
+            file = new File(System.getenv("M2_HOME") + "/conf/settings.xml");
+            if (file.isFile()) {
+                result.value = file;
+                result.source = "Implicit $M2_HOME/conf/settings.xml";
+                return result;
+            }
+        } else {
+            File file = new File(locationUrl.getPath());
+            result.value = file;
+            if (!file.isFile()) {
+                result.source = probableErrorMessage;
+            }
+            return result;
+        }
+
+        // 5. new org.apache.maven.settings.Settings()
+        result.value = null;
+        result.source = "No implicit settings.xml location is available";
+        return result;
+    }
+
+    /**
+     * Gets effective location of <code>settings-security.xml</code> file - according to pax-url-aether rules
+     * @param cmProperty property obtained from Config Admin
+     * @return
+     */
+    protected SourceAnd<File> securitySettings(String cmProperty) {
+        SourceAnd<File> result = new SourceAnd<>();
+        URL locationUrl = null;
+        String probableErrorMessage = null;
+
+        // 1. PID + ".security"
+        if (cmProperty != null && !"".equals(cmProperty.trim())) {
+            result.source = String.format(PATTERN_PID_PROPERTY, PID, PID + "." + PROPERTY_SECURITY_FILE);
+            try {
+                locationUrl = new URL(cmProperty);
+                probableErrorMessage = String.format("%s configured in %s.%s is not accessible",
+                        locationUrl, PID, PROPERTY_SECURITY_FILE);
+            } catch (MalformedURLException e) {
+                File file = new File(cmProperty);
+                if (file.isFile()) {
+                    result.value = file;
+                    return result;
+                }
+            }
+        }
+
+        // 2. System.getProperty("user.home") + "/.m2/settings-security.xml"
+        if (locationUrl == null) {
+            File file = new File(System.getProperty("user.home") + "/.m2/settings-security.xml");
+            if (file.isFile()) {
+                result.value = file;
+                result.source = "Implicit ${user.home}/.m2/settings-security.xml";
+                return result;
+            }
+        } else {
+            File file = new File(locationUrl.getPath());
+            result.value = file;
+            if (!file.isFile()) {
+                result.source = probableErrorMessage;
+            }
+        }
+
+        result.value = null;
+        result.source = "No implicit settings-security.xml location is available";
+        return result;
+    }
+
+    /**
+     * Gets effective location of <em>local repository</em> - according to pax-url-aether rules
+     * @param cmProperty property obtained from Config Admin
+     * @return
+     */
+    protected SourceAnd<File> localRepository(String cmProperty) {
+        SourceAnd<File> result = new SourceAnd<>();
+        URL locationUrl = null;
+        String probableErrorMessage = null;
+
+        // 1. PID + ".localRepository"
+        if (cmProperty != null && !"".equals(cmProperty.trim())) {
+            result.source = String.format(PATTERN_PID_PROPERTY, PID, PID + "." + PROPERTY_LOCAL_REPOSITORY);
+            try {
+                locationUrl = new URL(cmProperty);
+                probableErrorMessage = String.format("%s configured in %s.%s is not accessible",
+                        locationUrl, PID, PROPERTY_LOCAL_REPOSITORY);
+            } catch (MalformedURLException e) {
+                File file = new File(cmProperty);
+                if (file.isDirectory()) {
+                    result.value = file;
+                    return result;
+                }
+            }
+        }
+
+        // 2. from settings.xml
+        if (locationUrl == null && mavenSettings != null && mavenSettings.getLocalRepository() != null) {
+            result.source = String.format("Explicit <localRepository> in %s", settings.value);
+            try {
+                locationUrl = new URL(mavenSettings.getLocalRepository());
+                probableErrorMessage = String.format("%s configured in %s is not accessible",
+                        mavenSettings.getLocalRepository(), settings.value);
+            } catch (MalformedURLException e) {
+                File file = new File(mavenSettings.getLocalRepository());
+                if (file.isDirectory()) {
+                    result.value = file;
+                    return result;
+                }
+            }
+        }
+
+        // 3. System.getProperty("user.home") + "/.m2/repository";
+        if (locationUrl == null) {
+            File file = new File(System.getProperty("user.home") + "/.m2/repository");
+            result.value = file; // whether it exists or not
+            if (file.isDirectory()) {
+                result.source = "Implicit ${user.home}/.m2/repository";
+            } else {
+                result.source = "Implicit ${user.home}/.m2/repository (not accessible)";
+            }
+            return result;
+        }
+
+        File file = new File(locationUrl.getPath());
+        result.value = file;
+        if (!file.isDirectory()) {
+            result.source = probableErrorMessage;
+        }
+        return result;
+    }
+
+    /**
+     * Reads on demand <code>settings.xml</code> file - without password decryption. Also
+     * collects declared servers by ID.
+     * @param settingsFile
+     */
+    protected synchronized Settings readSettings(File settingsFile) throws SettingsBuildingException {
+        if (!settingsFile.isFile() || !settingsFile.canRead()) {
+            return null;
+        }
+
+        try {
+            DefaultSettingsBuilderFactory factory = new DefaultSettingsBuilderFactory();
+            DefaultSettingsBuilder builder = factory.newInstance();
+            SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
+            request.setUserSettingsFile(settingsFile);
+
+            SettingsBuildingResult result = builder.build(request);
+            if (result.getProblems().size() > 0) {
+                for (SettingsProblem problem : result.getProblems()) {
+                    System.err.println(problem);
+                }
+                return null;
+            } else {
+                Settings settings = result.getEffectiveSettings();
+                if (settings.getServers() != null) {
+                    for (Server server : settings.getServers()) {
+                        servers.put(server.getId(), server);
+                    }
+                }
+                return settings;
+            }
+        } catch (Throwable e) {
+            System.err.println(e.getMessage());
+            LOG.error(e.getMessage(), e);
+            throw e;
+        }
+    }
+
+    /**
+     * Re-reads on demand <code>settings-security.xml</code> file
+     * @param securitySettingsFile
+     */
+    protected synchronized SettingsSecurity readSecuritySettings(File securitySettingsFile) throws Exception {
+        if (!securitySettingsFile.isFile() || !securitySettingsFile.canRead()) {
+            return null;
+        }
+
+        try {
+            return SecUtil.read(securitySettingsFile.getAbsolutePath(), true);
+        } catch (Throwable e) {
+            System.err.println(e.getMessage());
+            LOG.error(e.getMessage(), e);
+            throw e;
+        }
+    }
+
+    /**
+     * <p>Decrypts passwords inside correctly read <code>settings.xml</code>. Also tries to decrypt master password.</p>
+     * <p>Not called implicitly for each action invocation.</p>
+     */
+    private void decryptSettings() throws Exception {
+        if (mavenSecuritySettings != null && mavenSettings != null) {
+            masterPassword = cipher.decryptDecorated(mavenSecuritySettings.getMaster(), masterMasterPassword);
+            DefaultSecDispatcher dispatcher = new DefaultSecDispatcher();
+            DefaultSettingsDecrypter decrypter = new DefaultSettingsDecrypter();
+            try {
+                dispatcher.setConfigurationFile(securitySettings.value.getAbsolutePath());
+                Field f = dispatcher.getClass().getDeclaredField("_cipher");
+                f.setAccessible(true);
+                f.set(dispatcher, cipher);
+
+                f = decrypter.getClass().getDeclaredField("securityDispatcher");
+                f.setAccessible(true);
+                f.set(decrypter, dispatcher);
+
+                DefaultSettingsDecryptionRequest req = new DefaultSettingsDecryptionRequest(mavenSettings);
+                SettingsDecryptionResult res = decrypter.decrypt(req);
+                if (res.getProblems() != null && res.getProblems().size() > 0) {
+                    for (SettingsProblem sp : res.getProblems()) {
+                        System.err.println(sp);
+                    }
+                }
+
+                for (Proxy proxy : res.getProxies()) {
+                    if (!cipher.isEncryptedString(proxy.getPassword())) {
+                        proxyPasswords.put(proxy.getId(), proxy.getPassword());
+                    }
+                }
+                for (Server server : res.getServers()) {
+                    if (!cipher.isEncryptedString(server.getPassword())) {
+                        serverPasswords.put(server.getId(), server.getPassword());
+                    }
+                }
+            } catch (Throwable t) {
+                LOG.warn("Can't decrypt " + securitySettings.value, t);
+            }
+        }
+    }
+
+    /**
+     * Returns list of configured remote (<code>remote=true</code>) or default (<code>remote=false</code>)
+     * repositories.
+     * @param remote
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    protected MavenRepositoryURL[] repositories(Dictionary<String, Object> config, boolean remote) throws Exception {
+        String property = remote ? PID + "." + PROPERTY_REPOSITORIES : PID + "." + PROPERTY_DEFAULT_REPOSITORIES;
+        String[] repositories = listOfValues((String) config.get(property));
+
+        if (remote) {
+            if (repositories.length == 0 || repositories.length > 0 && repositories[0].charAt(0) == '+') {
+                if (repositories.length > 0) {
+                    repositories[0] = repositories[0].substring(1);
+                }
+
+                List<String> newRepositories = new LinkedList<>();
+                newRepositories.addAll(Arrays.asList(repositories));
+
+                // append all repositories from all active profiles from available settings.xml
+                if (mavenSettings != null) {
+                    // see org.ops4j.pax.url.mvn.internal.config.MavenConfigurationImpl.getRepositories()
+                    Set<String> activeProfiles = new LinkedHashSet<>(mavenSettings.getActiveProfiles());
+                    Map<String, Profile> profiles = (Map<String, Profile>)mavenSettings.getProfilesAsMap();
+                    profiles.values().stream()
+                            .filter((profile) -> profile.getActivation() != null && profile.getActivation().isActiveByDefault())
+                            .map(Profile::getId)
+                            .forEach(activeProfiles::add);
+
+                    for (String activeProfile : activeProfiles) {
+                        Profile profile = profiles.get(activeProfile);
+                        if (profile == null) {
+                            continue;
+                        }
+                        for (Repository repo : profile.getRepositories()) {
+                            StringBuilder builder = new StringBuilder();
+                            builder.append(repo.getUrl());
+                            builder.append("@id=").append(repo.getId());
+                            builder.append("@_from=").append(MavenRepositoryURL.FROM.SETTINGS);
+
+                            if (repo.getReleases() != null) {
+                                if (!repo.getReleases().isEnabled()) {
+                                    builder.append("@noreleases");
+                                }
+                                addPolicy(builder, repo.getReleases().getUpdatePolicy(), "releasesUpdate");
+                                // not used in pax-url-aether
+                                //addPolicy(builder, repo.getReleases().getChecksumPolicy(), "releasesChecksum");
+                            }
+                            if (repo.getSnapshots() != null) {
+                                if (repo.getSnapshots().isEnabled()) {
+                                    builder.append("@snapshots");
+                                }
+                                addPolicy(builder, repo.getSnapshots().getUpdatePolicy(), "snapshotsUpdate");
+                                // not used in pax-url-aether
+                                //addPolicy(builder, repo.getSnapshots().getChecksumPolicy(), "snapshotsChecksum");
+                            }
+                            newRepositories.add(builder.toString());
+                        }
+                    }
+                }
+
+                repositories = newRepositories.toArray(new String[newRepositories.size()]);
+            }
+        }
+
+        List<MavenRepositoryURL> result = new LinkedList<>();
+        for (String repo : repositories) {
+            result.add(new MavenRepositoryURL(repo));
+        }
+        return result.toArray(new MavenRepositoryURL[result.size()]);
+    }
+
+    private void addPolicy(StringBuilder builder, String policy, String option) {
+        if (policy != null && !policy.isEmpty()) {
+            builder.append("@");
+            builder.append(option);
+            builder.append("=");
+            builder.append(policy);
+        }
+    }
+
+    /**
+     * Splits comma separated list of values into String array
+     * @param list
+     * @return
+     */
+    protected String[] listOfValues(String list) {
+        if (list == null) {
+            return new String[0];
+        }
+
+        String[] values = list.split("\\s*,\\s*");
+        return Arrays.stream(values)
+                .filter((value) -> (value != null && !"".equals(value.trim())))
+                .toArray(String[]::new);
+    }
+
+    /**
+     * Adds information used by proxy/server
+     * @param row {@link org.apache.karaf.shell.support.table.ShellTable}'s row to add information to
+     * @param id2Password mapping of ids (servers/proxies to decrypted passwords)
+     * @param id ID of proxy or server from <code>settings.xml</code>
+     * @param password password to use if decryption failed
+     */
+    protected void addPasswordInfo(Row row, Map<String, String> id2Password, String id, String password) {
+        if (id2Password.containsKey(id)) {
+            row.addContent(id2Password.get(id));
+        } else {
+            if (cipher.isEncryptedString(password)) {
+                row.addContent(password + " (can't decrypt)");
+            } else {
+                row.addContent(password == null ? "" : password);
+            }
+        }
+    }
+
+    protected static class SourceAnd<T> {
+        String source;
+        T value;
+
+        public SourceAnd() {
+        }
+
+        public SourceAnd(String source, T value) {
+            this.source = source;
+            this.value = value;
+        }
+
+        public String val() {
+            return value == null ? "" : value.toString();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java
new file mode 100644
index 0000000..13bcbdb
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "maven", name = "repository-add", description = "Adds Maven repository")
+@Service
+public class RepositoryAddCommand extends RepositoryEditCommandSupport {
+
+    @Option(name = "-x", aliases = { "--show-passwords" }, description = "Do not hide passwords related to Maven encryption", required = false, multiValued = false)
+    boolean showPasswords;
+
+    @Override
+    protected void edit() {
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/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
new file mode 100644
index 0000000..0179b02
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryEditCommandSupport.java
@@ -0,0 +1,35 @@
+/*
+ * 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.util.Dictionary;
+
+import org.apache.karaf.maven.core.MavenRepositoryURL;
+import org.apache.karaf.shell.api.action.Option;
+
+public abstract class RepositoryEditCommandSupport extends MavenConfigurationSupport {
+
+    @Override
+    public void doAction(String prefix, Dictionary<String, Object> config) throws Exception {
+        edit();
+
+        session.execute("maven:repository-list");
+    }
+
+    protected abstract void edit();
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/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
new file mode 100644
index 0000000..90c7c26
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryListCommand.java
@@ -0,0 +1,127 @@
+/*
+ * 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.util.Dictionary;
+
+import org.apache.karaf.maven.core.MavenRepositoryURL;
+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.apache.karaf.shell.support.table.Row;
+import org.apache.karaf.shell.support.table.ShellTable;
+import org.apache.maven.settings.Server;
+
+@Command(scope = "maven", name = "repository-list", description = "Maven repository summary.")
+@Service
+public class RepositoryListCommand extends MavenConfigurationSupport {
+
+    @Option(name = "-v", aliases = { "--verbose" }, description = "Show additional information (policies, source)", required = false, multiValued = false)
+    boolean verbose;
+
+    @Override
+    public void doAction(String prefix, Dictionary<String, Object> config) throws Exception {
+        ShellTable table = new ShellTable();
+        table.column("ID");
+        table.column("URL");
+        if (verbose) {
+            table.column("Releases");
+            table.column("Snapshots");
+            table.column("Defined in");
+        }
+        if (showPasswords) {
+            table.column("Username");
+            table.column("Password");
+        }
+
+        System.out.println();
+        System.out.println("== Remote repositories");
+        MavenRepositoryURL[] repositories = repositories(config, true);
+        for (MavenRepositoryURL repoURL : repositories) {
+            Row row = table.addRow();
+            row.addContent(repoURL.getId(), repoURL.getURL());
+            if (verbose) {
+                row.addContent(repositoryKindInfo(repoURL, false),
+                        repositoryKindInfo(repoURL, true),
+                        repoURL.getFrom());
+            }
+            if (showPasswords) {
+                if (servers.containsKey(repoURL.getId())) {
+                    Server server = servers.get(repoURL.getId());
+                    row.addContent(server.getUsername() == null ? "" : server.getUsername());
+                    addPasswordInfo(row, serverPasswords, repoURL.getId(), server.getPassword());
+                } else {
+                    row.addContent("", "");
+                }
+            }
+        }
+
+        table.print(System.out);
+
+        table = new ShellTable();
+        table.column("ID");
+        table.column("URL");
+        if (verbose) {
+            table.column("Releases");
+            table.column("Snapshots");
+        }
+
+        System.out.println();
+        System.out.println("== Default repositories");
+        repositories = repositories(config, false);
+        for (MavenRepositoryURL repoURL : repositories) {
+            Row row = table.addRow();
+            row.addContent(repoURL.getId(),
+                    repoURL.getURL());
+            if (verbose) {
+                row.addContent(repositoryKindInfo(repoURL, false),
+                        repositoryKindInfo(repoURL, true),
+                        repoURL.getFrom());
+            }
+        }
+
+        table.print(System.out);
+        System.out.println();
+    }
+
+    /**
+     * Information about release/snapshot handing for give repository URL
+     * @param repoURL
+     * @param snapshots
+     * @return
+     */
+    private Object repositoryKindInfo(MavenRepositoryURL repoURL, boolean snapshots) {
+        if (snapshots) {
+            if (repoURL.isSnapshotsEnabled()) {
+                String snapshotsUpdatePolicy = repoURL.getSnapshotsUpdatePolicy();
+                return String.format("yes (%s)",
+                        snapshotsUpdatePolicy == null || "".equals(snapshotsUpdatePolicy.trim()) ? "daily" : snapshotsUpdatePolicy
+                );
+            }
+        } else {
+            if (repoURL.isReleasesEnabled()) {
+                String releasesUpdatePolicy = repoURL.getReleasesUpdatePolicy();
+                return String.format("yes (%s)",
+                        releasesUpdatePolicy == null || "".equals(releasesUpdatePolicy.trim()) ? "daily" : releasesUpdatePolicy
+                );
+            }
+        }
+
+        return "no";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java
new file mode 100644
index 0000000..46f82c8
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.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.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "maven", name = "repository-remove", description = "Removes Maven repository")
+@Service
+public class RepositoryRemoveCommand extends RepositoryEditCommandSupport {
+
+    @Override
+    protected void edit() {
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/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
new file mode 100644
index 0000000..1678239
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/SummaryCommand.java
@@ -0,0 +1,239 @@
+/*
+ * 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.util.Dictionary;
+
+import org.apache.karaf.maven.core.MavenRepositoryURL;
+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.apache.karaf.shell.support.table.Row;
+import org.apache.karaf.shell.support.table.ShellTable;
+import org.apache.maven.settings.Proxy;
+
+@Command(scope = "maven", name = "summary", description = "Maven configuration summary.")
+@Service
+public class SummaryCommand extends MavenConfigurationSupport {
+
+    @Option(name = "-p", aliases = { "--property-ids" }, description = "Use PID property identifiers instead of their names", required = false, multiValued = false)
+    boolean propertyIds;
+
+    @Option(name = "-s", aliases = { "--source" }, description = "Adds information about where the value is configured", required = false, multiValued = false)
+    boolean source;
+
+    @Option(name = "-d", aliases = { "--description" }, description = "Adds description of Maven configuration options", required = false, multiValued = false)
+    boolean description;
+
+    @Override
+    protected void doAction(String prefix, Dictionary<String, Object> config) throws Exception {
+        ShellTable table = new ShellTable();
+        table.column("Option");
+        table.column("Value");
+        if (source) {
+            table.column("Source");
+        }
+        if (description) {
+            table.column("Description");
+        }
+
+        addRow(table, propertyIds ? PROPERTY_LOCAL_REPOSITORY : "Local repository", localRepository,
+                "Maven repository to store artifacts resolved in *remote repositories*");
+
+        addRow(table, propertyIds ? PROPERTY_SETTINGS_FILE : "Settings file", settings,
+                "Settings file that may contain configuration of additional repositories, http proxies and mirrors");
+
+        addRow(table, propertyIds ? PROPERTY_SECURITY_FILE : "Security settings file", securitySettings,
+                "Settings file that contain (or relocates to) master Maven password");
+
+        if (showPasswords) {
+            addRow(table, propertyIds ? "<master>" : "Master password", new SourceAnd<String>(securitySettings.source, masterPassword),
+                    "Master password used to decrypt proxy and server passwords");
+        }
+
+        // for default update/checksum policies specified at repository URI level, see
+        // org.ops4j.pax.url.mvn.internal.AetherBasedResolver.addRepo()
+
+        // see org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer#isUpdatedRequired()
+        SourceAnd<String> updatePolicy = updatePolicy((String) config.get(prefix + PROPERTY_GLOBAL_UPDATE_POLICY));
+        addRow(table, propertyIds ? PROPERTY_GLOBAL_UPDATE_POLICY : "Global update policy", updatePolicy,
+                "Overrides update policy specified at repository level (if specified)");
+
+        // see org.eclipse.aether.internal.impl.DefaultChecksumPolicyProvider#newChecksumPolicy()
+        SourceAnd<String> checksumPolicy = checksumPolicy((String) config.get(prefix + PROPERTY_GLOBAL_CHECKSUM_POLICY));
+        addRow(table, propertyIds ? PROPERTY_GLOBAL_CHECKSUM_POLICY : "Global checksum policy", checksumPolicy,
+                "Checksum policy for all repositories");
+
+        String updateReleasesProperty = (String) config.get(prefix + PROPERTY_UPDATE_RELEASES);
+        boolean updateReleases = false;
+        String sourceInfo = String.format(PATTERN_PID_PROPERTY, PID, prefix + PROPERTY_UPDATE_RELEASES);
+        if (updateReleasesProperty == null) {
+            sourceInfo = "Default \"false\"";
+        } else {
+            updateReleases = "true".equals(updateReleasesProperty);
+        }
+        addRow(table, propertyIds ? PROPERTY_UPDATE_RELEASES : "Update releases", new SourceAnd<Boolean>(sourceInfo, updateReleases),
+                "Whether to download non-SNAPSHOT artifacts according to update policy");
+
+        // see org.ops4j.pax.url.mvn.internal.config.MavenConfigurationImpl.isValid()
+        // ANY non null value (even "false"!) means that we require configadmin
+        String requireConfigAdminProperty = context.getProperty(prefix + REQUIRE_CONFIG_ADMIN_CONFIG);
+        boolean requireConfigAdmin = requireConfigAdminProperty != null;
+        sourceInfo = "Default \"false\"";
+        if (requireConfigAdmin) {
+            sourceInfo = "BundleContext property (" + prefix + REQUIRE_CONFIG_ADMIN_CONFIG + ")";
+        }
+        addRow(table, propertyIds ? REQUIRE_CONFIG_ADMIN_CONFIG : "Require Config Admin", new SourceAnd<Boolean>(sourceInfo, requireConfigAdmin),
+                "Whether MavenResolver service is registered ONLY with proper " + PID + " PID configuration");
+
+        // see org.ops4j.pax.url.mvn.internal.config.MavenConfigurationImpl.buildSettings()
+        String useFallbackRepositoriesProperty = (String) config.get(prefix + PROPERTY_USE_FALLBACK_REPOSITORIES);
+        boolean useFallbackRepositories = Boolean.parseBoolean(useFallbackRepositoriesProperty);
+        sourceInfo = "Default \"false\"";
+        if (useFallbackRepositoriesProperty != null) {
+            sourceInfo = String.format(PATTERN_PID_PROPERTY, PID, prefix + PROPERTY_USE_FALLBACK_REPOSITORIES);
+        }
+        addRow(table, propertyIds ? PROPERTY_USE_FALLBACK_REPOSITORIES : "Use fallback repository", new SourceAnd<Boolean>(sourceInfo, useFallbackRepositories),
+                "Whether Maven Central is used as implicit, additional remote repository");
+
+        // see org.ops4j.pax.url.mvn.internal.config.MavenConfigurationImpl.enableProxy()
+        // "proxySupport" and "proxies" are not used in "new" MavenResolver
+
+        // see org.eclipse.aether.internal.impl.DefaultOfflineController#checkOffline()
+        String offlineProperty = (String) config.get(prefix + PROPERTY_OFFLINE);
+        boolean offline = Boolean.parseBoolean(offlineProperty);
+        sourceInfo = "Default \"false\"";
+        if (offlineProperty != null) {
+            sourceInfo = String.format(PATTERN_PID_PROPERTY, PID, prefix + PROPERTY_OFFLINE);
+        }
+        addRow(table, propertyIds ? PROPERTY_OFFLINE : "Offline mode", new SourceAnd<Boolean>(sourceInfo, offline),
+                "Disables access to external remote repositories (file:// based ones are still used)");
+
+        // see org.ops4j.pax.url.mvn.internal.HttpClients.createConnManager()
+        String certificateCheckProperty = (String) config.get(prefix + PROPERTY_CERTIFICATE_CHECK);
+        boolean certificateCheck = Boolean.parseBoolean(certificateCheckProperty);
+        sourceInfo = "Default \"false\"";
+        if (certificateCheckProperty != null) {
+            sourceInfo = String.format(PATTERN_PID_PROPERTY, PID, prefix + PROPERTY_CERTIFICATE_CHECK);
+        }
+        addRow(table, propertyIds ? PROPERTY_CERTIFICATE_CHECK : "SSL/TLS certificate check", new SourceAnd<Boolean>(sourceInfo, certificateCheck),
+                "Turns on server certificate validation for HTTPS remote repositories");
+
+        // repositories (short list)
+        MavenRepositoryURL[] remoteRepositories = repositories(config, true);
+        boolean first = true;
+        for (MavenRepositoryURL url : remoteRepositories) {
+            addRow(table, first ? (propertyIds ? PROPERTY_REPOSITORIES : "Remote repositories") : "", new SourceAnd<String>(url.getFrom().getSource(), url.getURL().toString()),
+                first ? "Remote repositories where artifacts are being resolved if not found locally" : "");
+            first = false;
+        }
+
+        // default repositories (short list)
+        MavenRepositoryURL[] defaultRepositories = repositories(config, false);
+        first = true;
+        for (MavenRepositoryURL url : defaultRepositories) {
+            addRow(table, first ? (propertyIds ? PROPERTY_DEFAULT_REPOSITORIES : "Default repositories") : "", new SourceAnd<String>(url.getFrom().getSource(), url.getURL().toString()),
+                    first ? "Repositories where artifacts are looked up before trying remote resolution" : "");
+            first = false;
+        }
+
+        // proxies (short list)
+        if (mavenSettings != null && mavenSettings.getProxies() != null) {
+            first = true;
+            for (Proxy proxy : mavenSettings.getProxies()) {
+                String value = String.format("%s:%s", proxy.getHost(), proxy.getPort());
+                addRow(table, first ? (propertyIds ? "<proxies>" : "HTTP proxies") : "", new SourceAnd<String>(MavenRepositoryURL.FROM.SETTINGS.getSource(), value),
+                        first ? "Maven HTTP proxies" : "");
+                first = false;
+            }
+        }
+
+        System.out.println();
+        table.print(System.out);
+        System.out.println();
+    }
+
+    /**
+     * Helper to add row to {@link ShellTable}
+     * @param table
+     * @param label
+     * @param value
+     * @param descriptionText
+     */
+    private <T> void addRow(ShellTable table, String label, SourceAnd<T> value, String descriptionText) {
+        Row row = table.addRow();
+        row.addContent(label, value.val());
+        if (source) {
+            row.addContent(value.source);
+        }
+        if (description) {
+            row.addContent(descriptionText);
+        }
+    }
+
+    private SourceAnd<String> updatePolicy(String cmProperty) {
+        SourceAnd<String> result = new SourceAnd<>();
+        result.value = cmProperty;
+
+        if (cmProperty == null || "".equals(cmProperty.trim())) {
+            result.value = "";
+            result.source = "Implicit \"never\", but doesn't override repository-specific value";
+            return result;
+        }
+
+        result.source = String.format(PATTERN_PID_PROPERTY, PID, PID + "." + PROPERTY_GLOBAL_UPDATE_POLICY);
+        if ("always".equals(cmProperty) || "never".equals(cmProperty) || "daily".equals(cmProperty)) {
+            // ok
+        } else if (cmProperty.startsWith("interval")) {
+            int minutes = 1440;
+            try {
+                String n = cmProperty.substring("interval".length() + 1);
+                minutes = Integer.parseInt(n);
+            } catch (Exception e) {
+                result.value = "interval:1440";
+                result.source = "Implicit \"interval:1440\" (error parsing \"" + cmProperty + "\")";
+            }
+        } else {
+            result.value = "never";
+            result.source = "Implicit \"never\" (unknown value \"" + cmProperty + "\")";
+        }
+
+        return result;
+    }
+
+    private SourceAnd<String> checksumPolicy(String cmProperty) {
+        SourceAnd<String> result = new SourceAnd<>();
+        result.value = cmProperty;
+
+        if (cmProperty == null || "".equals(cmProperty.trim())) {
+            result.value = "warn";
+            result.source = "Default \"warn\"";
+            return result;
+        }
+
+        result.source = String.format(PATTERN_PID_PROPERTY, PID, PID + "." + PROPERTY_GLOBAL_CHECKSUM_POLICY);
+        if ("ignore".equals(cmProperty) || "warn".equals(cmProperty) || "fail".equals(cmProperty)) {
+            // ok
+        } else {
+            result.value = "warn";
+            result.source = "Implicit \"warn\" (unknown value \"" + cmProperty + "\")";
+        }
+
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java b/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java
new file mode 100644
index 0000000..630976b
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2008 Alin Dreghiciu.
+ *
+ * Licensed  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.core;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.ops4j.pax.url.mvn.ServiceConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An URL of Maven repository that knows if it contains SNAPSHOT versions and/or releases.
+ *
+ * @author Alin Dreghiciu
+ * @author Guillaume Nodet
+ * @since 0.2.1, February 07, 2008
+ *
+ * @see org.ops4j.pax.url.mvn.internal.config.MavenRepositoryURL
+ */
+public class MavenRepositoryURL
+{
+    /*
+     * String OPTION_ALLOW_SNAPSHOTS = "snapshots";
+     * String OPTION_DISALLOW_RELEASES = "noreleases";
+     * String OPTION_MULTI = "multi";
+     * String OPTION_ID = "id";
+     * String OPTION_UPDATE = "update";
+     * String OPTION_RELEASES_UPDATE = "releasesUpdate";
+     * String OPTION_SNAPSHOTS_UPDATE = "snapshotsUpdate";
+     * String OPTION_CHECKSUM = "checksum";
+     * String OPTION_RELEASES_CHECKSUM = "releasesChecksum";
+     * String OPTION_SNAPSHOTS_CHECKSUM = "snapshotsChecksum";
+     */
+
+    private static final Logger LOG = LoggerFactory.getLogger( MavenRepositoryURL.class );
+
+    /**
+     * Repository Id.
+     */
+    private final String m_id;
+    /**
+     * Repository URL.
+     */
+    private final URL m_repositoryURL;
+    /**
+     * Repository file (only if URL is a file URL).
+     */
+    private final File m_file;
+    /**
+     * True if the repository contains snapshots.
+     */
+    private final boolean m_snapshotsEnabled;
+    /**
+     * True if the repository contains releases.
+     */
+    private final boolean m_releasesEnabled;
+    /**
+     * Repository update policy
+     */
+    private final String m_releasesUpdatePolicy;
+    /**
+     * Repository update policy
+     */
+    private final String m_snapshotsUpdatePolicy;
+    /**
+     * Repository checksum policy
+     */
+    private final String m_releasesChecksumPolicy;
+    /**
+     * Repository checksum policy
+     */
+    private final String m_snapshotsChecksumPolicy;
+
+    private final boolean m_multi;
+    /**
+     * Where the repository was defined (PID or settings.xml)
+     */
+    private final FROM m_from;
+
+    /**
+     * Creates a maven repository URL bases on a string spec. The path can be marked with @snapshots and/or @noreleases
+     * (not case sensitive).
+     *
+     * @param repositorySpec url spec of repository
+     *
+     * @throws MalformedURLException if spec contains a malformed maven repository url
+     */
+    public MavenRepositoryURL( final String repositorySpec )
+        throws MalformedURLException
+    {
+        final String[] segments = repositorySpec.split( ServiceConstants.SEPARATOR_OPTIONS );
+        final StringBuilder urlBuilder = new StringBuilder();
+        boolean snapshotEnabled = false;
+        boolean releasesEnabled = true;
+        boolean multi = false;
+
+        String name = null;
+        String update = null;
+        String updateReleases = null;
+        String updateSnapshots = null;
+        String checksum = null;
+        String checksumReleases = null;
+        String checksumSnapshots = null;
+        FROM from = null;
+
+        for( int i = 0; i < segments.length; i++ )
+        {
+            String segment = segments[i].trim();
+            if( segment.equalsIgnoreCase( ServiceConstants.OPTION_ALLOW_SNAPSHOTS ) )
+            {
+                snapshotEnabled = true;
+            }
+            else if( segment.equalsIgnoreCase( ServiceConstants.OPTION_DISALLOW_RELEASES ) )
+            {
+                releasesEnabled = false;
+            }
+            else if( segment.equalsIgnoreCase( ServiceConstants.OPTION_MULTI ) )
+            {
+                multi = true;
+            }
+            else if( segment.startsWith( ServiceConstants.OPTION_ID + "=" ) )
+            {
+                try {
+                    name = segments[ i ].split( "=" )[1].trim();
+                } catch (Exception e) {
+                    LOG.warn( "Problem with segment " + segments[i] + " in " + repositorySpec );
+                }
+            }
+            else if( segment.startsWith( ServiceConstants.OPTION_RELEASES_UPDATE + "=" ) )
+            {
+                try {
+                    updateReleases = segments[ i ].split( "=" )[1].trim();
+                } catch (Exception e) {
+                    LOG.warn( "Problem with segment " + segments[i] + " in " + repositorySpec );
+                }
+            }
+            else if( segment.startsWith( ServiceConstants.OPTION_SNAPSHOTS_UPDATE + "=" ) )
+            {
+                try {
+                    updateSnapshots = segments[ i ].split( "=" )[1].trim();
+                } catch (Exception e) {
+                    LOG.warn( "Problem with segment " + segments[i] + " in " + repositorySpec );
+                }
+            }
+            else if( segment.startsWith( ServiceConstants.OPTION_UPDATE + "=" ) )
+            {
+                try {
+                    update = segments[ i ].split( "=" )[1].trim();
+                } catch (Exception e) {
+                    LOG.warn( "Problem with segment " + segments[i] + " in " + repositorySpec );
+                }
+            }
+            else if( segment.startsWith( ServiceConstants.OPTION_RELEASES_CHECKSUM + "=" ) )
+            {
+                try {
+                    checksumReleases = segments[ i ].split( "=" )[1].trim();
+                } catch (Exception e) {
+                    LOG.warn( "Problem with segment " + segments[i] + " in " + repositorySpec );
+                }
+            }
+            else if( segment.startsWith( ServiceConstants.OPTION_SNAPSHOTS_CHECKSUM + "=" ) )
+            {
+                try {
+                    checksumSnapshots = segments[ i ].split( "=" )[1].trim();
+                } catch (Exception e) {
+                    LOG.warn( "Problem with segment " + segments[i] + " in " + repositorySpec );
+                }
+            }
+            else if( segment.startsWith( ServiceConstants.OPTION_CHECKSUM + "=" ) )
+            {
+                try {
+                    checksum = segments[ i ].split( "=" )[1].trim();
+                } catch (Exception e) {
+                    LOG.warn( "Problem with segment " + segments[i] + " in " + repositorySpec );
+                }
+            }
+            else if( segment.startsWith( "_from=" ) )
+            {
+                try {
+                    from = FROM.valueOf( segments[ i ].split( "=" )[1].trim() );
+                } catch (Exception ignored) {
+                }
+            }
+            else
+            {
+                if( i > 0 )
+                {
+                    urlBuilder.append( ServiceConstants.SEPARATOR_OPTIONS );
+                }
+                urlBuilder.append( segments[ i ] );
+            }
+        }
+        String spec = buildSpec( urlBuilder );
+        m_repositoryURL = new URL( spec );
+        m_snapshotsEnabled = snapshotEnabled;
+        m_releasesEnabled = releasesEnabled;
+        m_multi = multi;
+        if (name == null) {
+            String warn = "Repository spec " + spec + " does not contain an identifier. This is deprecated & discouraged & just evil.";
+            LOG.warn( warn );
+            name = "repo_" + spec.hashCode();
+        }
+        m_id = name;
+        m_releasesUpdatePolicy = updateReleases != null ? updateReleases : update;
+        m_snapshotsUpdatePolicy = updateSnapshots != null ? updateSnapshots : update;
+        m_releasesChecksumPolicy = checksumReleases != null ? checksumReleases : checksum;
+        m_snapshotsChecksumPolicy = checksumSnapshots != null ? checksumSnapshots : checksum;
+
+        m_from = from != null ? from : FROM.PID;
+
+        if( m_repositoryURL.getProtocol().equals( "file" ) )
+        {
+            try
+            {
+                // You must transform to URI to decode the path (manage a path with a space or non
+                // us character)
+                // like D:/documents%20and%20Settings/SESA170017/.m2/repository
+                // the path can be store in path part or in scheme specific part (if is relatif
+                // path)
+                // the anti-slash character is not a valid character for uri.
+                spec = spec.replaceAll( "\\\\", "/" );
+                spec = spec.replaceAll( " ", "%20" );
+                URI uri = new URI( spec );
+                String path = uri.getPath();
+                if( path == null )
+                    path = uri.getSchemeSpecificPart();
+                m_file = new File( path );
+
+            }
+            catch ( URISyntaxException e )
+            {
+                throw new MalformedURLException( e.getMessage() );
+            }
+        }
+        else
+        {
+            m_file = null;
+        }
+    }
+
+    private String buildSpec( StringBuilder urlBuilder )
+    {
+        String spec = urlBuilder.toString().trim();
+        if( !spec.endsWith( "\\" ) && !spec.endsWith( "/" ) )
+        {
+            spec = spec + "/";
+        }
+        return spec;
+    }
+
+    /**
+     * Getter.
+     *
+     * @return repository id
+     */
+    public String getId()
+    {
+        return m_id;
+    }
+
+    /**
+     * Getter.
+     *
+     * @return repository URL
+     */
+    public URL getURL()
+    {
+        return m_repositoryURL;
+    }
+
+    /**
+     * Getter.
+     *
+     * @return repository file
+     */
+    public File getFile()
+    {
+        return m_file;
+    }
+
+    /**
+     * Getter.
+     *
+     * @return true if the repository contains releases
+     */
+    public boolean isReleasesEnabled()
+    {
+        return m_releasesEnabled;
+    }
+
+    /**
+     * Getter.
+     *
+     * @return true if the repository contains snapshots
+     */
+    public boolean isSnapshotsEnabled()
+    {
+        return m_snapshotsEnabled;
+    }
+
+    public String getReleasesUpdatePolicy() {
+        return m_releasesUpdatePolicy;
+    }
+
+    public String getSnapshotsUpdatePolicy() {
+        return m_snapshotsUpdatePolicy;
+    }
+
+    public String getReleasesChecksumPolicy() {
+        return m_releasesChecksumPolicy;
+    }
+
+    public String getSnapshotsChecksumPolicy() {
+        return m_snapshotsChecksumPolicy;
+    }
+
+    public FROM getFrom() {
+        return m_from;
+    }
+
+    /**
+     * Getter.
+     *
+     * @return true if the repository is a parent path of repos
+     */
+    public boolean isMulti()
+    {
+        return m_multi;
+    }
+
+    /**
+     * Getter.
+     *
+     * @return if the repository is a file based repository.
+     */
+    public boolean isFileRepository()
+    {
+        return m_file != null;
+    }
+
+    @Override
+    public String toString()
+    {
+        return new StringBuilder()
+            .append( m_repositoryURL.toString() )
+            .append( ",releases=" ).append( m_releasesEnabled )
+            .append( ",snapshots=" ).append( m_snapshotsEnabled )
+            .toString();
+    }
+
+    public static enum FROM {
+        PID("PID configuration"),
+        SETTINGS("Maven XML settings"),
+        FALLBACK("Fallback repository");
+
+        private String source;
+
+        FROM(String source) {
+            this.source = source;
+        }
+
+        public String getSource() {
+            return source;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/maven/core/src/main/resources/OSGI-INF/bundle.info
----------------------------------------------------------------------
diff --git a/maven/core/src/main/resources/OSGI-INF/bundle.info b/maven/core/src/main/resources/OSGI-INF/bundle.info
new file mode 100644
index 0000000..509e13b
--- /dev/null
+++ b/maven/core/src/main/resources/OSGI-INF/bundle.info
@@ -0,0 +1,36 @@
+#
+#
+#    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.
+#
+#
+h1. Synopsis
+
+	${project.name}
+
+	${project.description}
+
+	Maven URL:
+		[mvn:${project.groupId}/${project.artifactId}/${project.version}]
+
+h1. Description
+
+	This bundle is the core implementation of Maven diagnostics support and exposes commands
+	to change and examine Maven configuration managed by pax-url-aether bundle and org.ops4j.pax.url.mvn PID.
+
+h1. Commands
+
+	The bundle contains the following commands:
+\${command-list|maven|indent=8,list,cyan}

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/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
new file mode 100644
index 0000000..b0d9110
--- /dev/null
+++ b/maven/core/src/test/java/org/apache/karaf/maven/SettingsTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+import java.io.IOException;
+
+import org.apache.maven.settings.Server;
+import org.apache.maven.settings.Settings;
+import org.apache.maven.settings.io.DefaultSettingsWriter;
+import org.junit.Test;
+import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
+import org.sonatype.plexus.components.cipher.PBECipher;
+
+public class SettingsTest {
+
+    @Test
+    public void readAndWriteSettings() throws IOException {
+        Settings settings = new Settings();
+        Server s = new Server();
+        s.setId("id");
+        settings.getServers().add(s);
+
+        new DefaultSettingsWriter().write(System.out, null, settings);
+    }
+
+    @Test
+    public void encryptMavenPassword() throws Exception {
+        // non-master password ('mvn -ep admin')
+        DefaultPlexusCipher plexusCipher = new DefaultPlexusCipher();
+        System.out.println(plexusCipher.encrypt("admin", "admin"));
+
+        // master password (`mvn -emp admin`)
+        PBECipher cipher = new PBECipher();
+        System.out.println(cipher.encrypt64("admin","settings.security"));
+    }
+
+    @Test
+    public void decryptMavenPassword() throws Exception {
+        // non-master password ('mvn -ep admin')
+        DefaultPlexusCipher plexusCipher = new DefaultPlexusCipher();
+        System.out.println(plexusCipher.decrypt("{EhjazkVpkMoHjAgaUKX+UxeXn9lsJGHst2uFKmhNZ8U=}", "admin"));
+
+        // master password (`mvn -emp admin`)
+        PBECipher cipher = new PBECipher();
+        System.out.println(cipher.decrypt64("oWE12FbirwYHNit93TAMA+OC/GJge2r9FuzI8kOuHlA=","settings.security"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/maven/core/src/test/resources/reference-settings.xml
----------------------------------------------------------------------
diff --git a/maven/core/src/test/resources/reference-settings.xml b/maven/core/src/test/resources/reference-settings.xml
new file mode 100644
index 0000000..25b58fb
--- /dev/null
+++ b/maven/core/src/test/resources/reference-settings.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
+
+    <localRepository>file:///</localRepository>
+
+    <proxies>
+        <proxy>
+            <id>id1</id>
+            <host>localhost</host>
+            <port>3128</port>
+            <protocol>http</protocol>
+            <username>username</username>
+            <password>password</password>
+        </proxy>
+    </proxies>
+
+    <servers>
+        <server>
+            <id>id</id>
+            <username>username</username>
+            <password>password</password>
+        </server>
+    </servers>
+
+    <profiles>
+        <profile>
+            <id>profile1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <repositories>
+                <repository>
+                    <id>id1</id>
+                    <url>http://localhost/maven-repository/releases</url>
+                    <releases>
+                        <enabled>true</enabled>
+                        <updatePolicy>never</updatePolicy>
+                        <checksumPolicy>warn</checksumPolicy>
+                    </releases>
+                    <snapshots>
+                        <enabled>false</enabled>
+                    </snapshots>
+                </repository>
+                <repository>
+                    <id>id2</id>
+                    <url>http://localhost/maven-repository/snapshots</url>
+                    <releases>
+                        <enabled>false</enabled>
+                    </releases>
+                    <snapshots>
+                        <enabled>true</enabled>
+                        <updatePolicy>always</updatePolicy>
+                        <checksumPolicy>warn</checksumPolicy>
+                    </snapshots>
+                </repository>
+            </repositories>
+        </profile>
+    </profiles>
+
+    <activeProfiles>
+        <activeProfile>profile1</activeProfile>
+    </activeProfiles>
+
+</settings>

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/maven/pom.xml
----------------------------------------------------------------------
diff --git a/maven/pom.xml b/maven/pom.xml
new file mode 100644
index 0000000..95a3983
--- /dev/null
+++ b/maven/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.karaf</groupId>
+        <artifactId>karaf</artifactId>
+        <version>4.2.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.apache.karaf.maven</groupId>
+    <artifactId>maven-parent</artifactId>
+    <packaging>pom</packaging>
+    <name>Apache Karaf :: Maven</name>
+
+    <modules>
+        <module>core</module>
+    </modules>
+
+</project>

http://git-wip-us.apache.org/repos/asf/karaf/blob/750fd144/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index a7367d2..2767cb1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,7 @@
         <module>jdbc</module>
         <module>jms</module>
         <module>jpa</module>
+        <module>maven</module>
         <module>services</module>
         <module>subsystem</module>
         <module>profile</module>
@@ -1077,6 +1078,16 @@
                 <version>${maven.version}</version>
             </dependency>
             <dependency>
+                <groupId>org.apache.maven</groupId>
+                <artifactId>maven-settings</artifactId>
+                <version>${maven.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.maven</groupId>
+                <artifactId>maven-settings-builder</artifactId>
+                <version>${maven.version}</version>
+            </dependency>
+            <dependency>
                 <groupId>org.apache.maven.shared</groupId>
                 <artifactId>maven-dependency-tree</artifactId>
                 <version>2.2</version>


[6/9] karaf git commit: [KARAF-5008] maven:repository-change and maven:repository-remove commands

Posted by gg...@apache.org.
[KARAF-5008] maven:repository-change and maven:repository-remove commands


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/32d5c7b7
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/32d5c7b7
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/32d5c7b7

Branch: refs/heads/master
Commit: 32d5c7b769b570824cc50917a78c103ddbfa1e6c
Parents: ede23ba
Author: Grzegorz Grzybek <gr...@gmail.com>
Authored: Wed Jun 21 13:36:40 2017 +0200
Committer: Grzegorz Grzybek <gr...@gmail.com>
Committed: Wed Jun 21 15:31:54 2017 +0200

----------------------------------------------------------------------
 .../command/MavenConfigurationSupport.java      |  38 +++-
 .../maven/command/RepositoryAddCommand.java     | 109 ++--------
 .../maven/command/RepositoryChangeCommand.java  | 197 +++++++++++++++++++
 .../command/RepositoryEditCommandSupport.java   | 148 +++++++++++++-
 .../maven/command/RepositoryRemoveCommand.java  |  59 +++++-
 .../karaf/maven/core/MavenRepositoryURL.java    |  46 ++++-
 6 files changed, 484 insertions(+), 113 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/32d5c7b7/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 b64e583..c028530 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,6 +17,7 @@
 package org.apache.karaf.maven.command;
 
 import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.net.MalformedURLException;
@@ -54,6 +55,8 @@ import org.apache.maven.settings.building.SettingsProblem;
 import org.apache.maven.settings.crypto.DefaultSettingsDecrypter;
 import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
 import org.apache.maven.settings.crypto.SettingsDecryptionResult;
+import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
+import org.ops4j.pax.url.mvn.ServiceConstants;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
@@ -170,8 +173,8 @@ public abstract class MavenConfigurationSupport implements Action {
     /**
      * Performs command action on <strong>existing</strong> <code>org.ops4j.pax.url.mvn</code>
      * PID configuration
-     * @param prefix
-     * @param config
+     * @param prefix prefix for properties inside <code>org.ops4j.pax.url.mvn</code> PID
+     * @param config <code>org.ops4j.pax.url.mvn</code> PID configuration taken from {@link ConfigurationAdmin}
      */
     abstract protected void doAction(String prefix, Dictionary<String, Object> config) throws Exception;
 
@@ -491,17 +494,19 @@ public abstract class MavenConfigurationSupport implements Action {
 
                             if (repo.getReleases() != null) {
                                 if (!repo.getReleases().isEnabled()) {
-                                    builder.append("@noreleases");
+                                    builder.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_DISALLOW_RELEASES);
                                 }
-                                addPolicy(builder, repo.getReleases().getUpdatePolicy(), "releasesUpdate");
+                                SourceAnd<String> up = updatePolicy(repo.getReleases().getUpdatePolicy());
+                                addPolicy(builder, "".equals(up.val()) ? "never" : up.val(), ServiceConstants.OPTION_RELEASES_UPDATE);
                                 // not used in pax-url-aether
                                 //addPolicy(builder, repo.getReleases().getChecksumPolicy(), "releasesChecksum");
                             }
                             if (repo.getSnapshots() != null) {
                                 if (repo.getSnapshots().isEnabled()) {
-                                    builder.append("@snapshots");
+                                    builder.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_ALLOW_SNAPSHOTS);
                                 }
-                                addPolicy(builder, repo.getSnapshots().getUpdatePolicy(), "snapshotsUpdate");
+                                SourceAnd<String> up = updatePolicy(repo.getSnapshots().getUpdatePolicy());
+                                addPolicy(builder, "".equals(up.val()) ? "never" : up.val(), ServiceConstants.OPTION_SNAPSHOTS_UPDATE);
                                 // not used in pax-url-aether
                                 //addPolicy(builder, repo.getSnapshots().getChecksumPolicy(), "snapshotsChecksum");
                             }
@@ -682,6 +687,27 @@ public abstract class MavenConfigurationSupport implements Action {
     }
 
     /**
+     * Stores changed {@link org.apache.maven.settings.Settings} in new settings.xml file and updates
+     * <code>org.ops4j.pax.url.mvn.settings</code> property. Does <string>not</string> update
+     * {@link org.osgi.service.cm.ConfigurationAdmin} config.
+     * @param prefix
+     * @param config
+     */
+    protected void updateSettings(String prefix, Dictionary<String, Object> config) throws IOException {
+        File dataDir = context.getDataFile(".");
+        if (!dataDir.isDirectory()) {
+            throw new RuntimeException("Can't access data directory for " + context.getBundle().getSymbolicName() + " bundle");
+        }
+        File newSettingsFile = nextSequenceFile(dataDir, RE_SETTINGS, PATTERN_SETTINGS);
+        config.put(prefix + PROPERTY_SETTINGS_FILE, newSettingsFile.getCanonicalPath());
+
+        try (FileWriter fw = new FileWriter(newSettingsFile)) {
+            new SettingsXpp3Writer().write(fw, mavenSettings);
+        }
+        System.out.println("New settings stored in \"" + newSettingsFile.getCanonicalPath() + "\"");
+    }
+
+    /**
      * Handy class containing value and information about its origin. <code>valid</code> may be used to indicate
      * if the value is correct. It may be implicit, but the interpretation of <code>valid </code> is not defined.
      * @param <T>

http://git-wip-us.apache.org/repos/asf/karaf/blob/32d5c7b7/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java
index 11034d3..1001ade 100644
--- a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java
@@ -16,26 +16,17 @@
  */
 package org.apache.karaf.maven.command;
 
-import java.io.File;
-import java.io.FileWriter;
-import java.net.MalformedURLException;
-import java.net.URL;
 import java.util.Arrays;
 import java.util.Dictionary;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Objects;
 import java.util.Optional;
-import java.util.stream.Collectors;
 
-import org.apache.felix.utils.properties.InterpolationHelper;
 import org.apache.karaf.maven.core.MavenRepositoryURL;
 import org.apache.karaf.shell.api.action.Argument;
 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.apache.maven.settings.Server;
-import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
 import org.ops4j.pax.url.mvn.ServiceConstants;
 import org.osgi.service.cm.Configuration;
 
@@ -58,36 +49,27 @@ public class RepositoryAddCommand extends RepositoryEditCommandSupport {
     @Option(name = "-cp", aliases = { "--checksum-policy" }, description = "Checksum policy for repository (ignore, warn (default), fail)", required = false, multiValued = false)
     String checksumPolicy = "warn";
 
-    @Argument(description = "Repository URI. It may be file:// based, http(s):// based, may use other known protocol or even property placeholders (like ${karaf.base})")
-    String uri;
-
     @Option(name = "-u", aliases = { "--username" }, description = "Username for remote repository", required = false, multiValued = false)
     String username;
 
     @Option(name = "-p", aliases = { "--password" }, description = "Password for remote repository (may be encrypted, see \"maven:password -ep\")", required = false, multiValued = false)
     String password;
 
-    @Override
-    protected void edit(String prefix, Dictionary<String, Object> config) throws Exception {
-        MavenRepositoryURL[] repositories = repositories(config, !defaultRepository);
+    @Argument(description = "Repository URI. It may be file:// based, http(s):// based, may use other known protocol or even property placeholders (like ${karaf.base})")
+    String uri;
 
-        MavenRepositoryURL[] repositoriesFromPidProperty = Arrays.stream(repositories)
-                .filter((repo) -> repo.getFrom() == MavenRepositoryURL.FROM.PID)
-                .toArray(MavenRepositoryURL[]::new);
+    @Override
+    protected void edit(String prefix, Dictionary<String, Object> config,
+                        MavenRepositoryURL[] allRepos, MavenRepositoryURL[] pidRepos, MavenRepositoryURL[] settingsRepos) throws Exception {
 
-        if (idx > repositoriesFromPidProperty.length) {
+        if (idx > pidRepos.length) {
             // TOCONSIDER: should we allow to add repository to settings.xml too?
             System.err.printf("List of %s repositories has %d elements. Can't insert at position %s.\n",
-                    (defaultRepository ? "default" : "remote"), repositories.length, id);
+                    (defaultRepository ? "default" : "remote"), pidRepos.length, id);
             return;
         }
 
-        if (id == null || "".equals(id.trim())) {
-            System.err.println("Please specify ID of repository");
-            return;
-        }
-
-        Optional<MavenRepositoryURL> first = Arrays.stream(repositories)
+        Optional<MavenRepositoryURL> first = Arrays.stream(allRepos)
                 .filter((repo) -> id.equals(repo.getId())).findAny();
         if (first.isPresent()) {
             System.err.printf("Repository with ID \"%s\" is already configured for URL %s\n", id, first.get().getURL());
@@ -106,34 +88,8 @@ public class RepositoryAddCommand extends RepositoryEditCommandSupport {
             return;
         }
 
-        if (uri == null || "".equals(uri.trim())) {
-            System.err.println("Please specify repository location");
-            return;
-        }
-        String urlResolved = InterpolationHelper.substVars(uri, "uri", null, null, context);
-        URL url = null;
-        try {
-            url = new URL(urlResolved);
-            urlResolved = url.toString();
-
-            if ("file".equals(url.getProtocol()) && new File(url.toURI()).isDirectory()) {
-                System.err.println("Location \"" + urlResolved + "\" is not accessible");
-                return;
-            }
-        } catch (MalformedURLException e) {
-            // a directory?
-            File location = new File(urlResolved);
-            if (!location.exists() || !location.isDirectory()) {
-                System.err.println("Location \"" + urlResolved + "\" is not accessible");
-                return;
-            } else {
-                url = location.toURI().toURL();
-                urlResolved = url.toString();
-            }
-        }
-
-        if (defaultRepository && !"file".equals(url.getProtocol())) {
-            System.err.println("Default repositories should be locally accessible (use file:// protocol or normal directory path)");
+        SourceAnd<String> urlResolved = validateRepositoryURL(uri, defaultRepository);
+        if (!urlResolved.valid) {
             return;
         }
 
@@ -152,43 +108,15 @@ public class RepositoryAddCommand extends RepositoryEditCommandSupport {
         }
 
         // credentials for remote repository can be stored only in settings.xml
-
         if (!defaultRepository && hasCredentials) {
-            if (!confirm("Maven settings will be updated and org.ops4j.pax.url.mvn.settings property will change. Continue? (y/N) ")) {
+            if (!updateCredentials(force, id, username, password, prefix, config)) {
                 return;
             }
-
-            File dataDir = context.getDataFile(".");
-            if (!dataDir.isDirectory()) {
-                System.err.println("Can't access data directory for " + context.getBundle().getSymbolicName() + " bundle");
-                return;
-            }
-
-            Optional<Server> existingServer = mavenSettings.getServers().stream()
-                    .filter((s) -> id.equals(s.getId())).findAny();
-            Server server = null;
-            if (existingServer.isPresent()) {
-                server = existingServer.get();
-            } else {
-                server = new Server();
-                server.setId(id);
-                mavenSettings.getServers().add(server);
-            }
-            server.setUsername(username);
-            server.setPassword(password);
-
-            // prepare configadmin configuration update
-            File newSettingsFile = nextSequenceFile(dataDir, RE_SETTINGS, PATTERN_SETTINGS);
-            config.put(prefix + PROPERTY_SETTINGS_FILE, newSettingsFile.getCanonicalPath());
-
-            try (FileWriter fw = new FileWriter(newSettingsFile)) {
-                new SettingsXpp3Writer().write(fw, mavenSettings);
-            }
-            System.out.println("New settings stored in \"" + newSettingsFile.getCanonicalPath() + "\"");
+            updateSettings(prefix, config);
         }
 
         StringBuilder sb = new StringBuilder();
-        sb.append(urlResolved);
+        sb.append(urlResolved.val());
         sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_ID + "=" + id);
         if (snapshots) {
             sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_ALLOW_SNAPSHOTS);
@@ -200,21 +128,14 @@ public class RepositoryAddCommand extends RepositoryEditCommandSupport {
         sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_CHECKSUM + "=" + checksumPolicy);
 
         MavenRepositoryURL newRepository = new MavenRepositoryURL(sb.toString());
-        List<MavenRepositoryURL> newRepos = new LinkedList<>(Arrays.asList(repositoriesFromPidProperty));
+        List<MavenRepositoryURL> newRepos = new LinkedList<>(Arrays.asList(pidRepos));
         if (idx >= 0) {
             newRepos.add(idx, newRepository);
         } else {
             newRepos.add(newRepository);
         }
 
-        String newList = newRepos.stream().map(MavenRepositoryURL::asRepositorySpec)
-                .collect(Collectors.joining(","));
-
-        if (defaultRepository) {
-            config.put(prefix + PROPERTY_DEFAULT_REPOSITORIES, newList);
-        } else {
-            config.put(prefix + PROPERTY_REPOSITORIES, newList);
-        }
+        updatePidRepositories(prefix, config, defaultRepository, newRepos, settingsRepos.length > 0);
 
         Configuration cmConfig = cm.getConfiguration(PID);
         cmConfig.update(config);

http://git-wip-us.apache.org/repos/asf/karaf/blob/32d5c7b7/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryChangeCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryChangeCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryChangeCommand.java
new file mode 100644
index 0000000..e85876a
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryChangeCommand.java
@@ -0,0 +1,197 @@
+/*
+ * 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.net.URL;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.karaf.maven.core.MavenRepositoryURL;
+import org.apache.karaf.shell.api.action.Argument;
+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.apache.maven.settings.Profile;
+import org.apache.maven.settings.Repository;
+import org.apache.maven.settings.RepositoryPolicy;
+import org.osgi.service.cm.Configuration;
+
+@Command(scope = "maven", name = "repository-change", description = "Changes configuration of Maven repository")
+@Service
+public class RepositoryChangeCommand extends RepositoryEditCommandSupport {
+
+    @Option(name = "-s", aliases = { "--snapshots" }, description = "Enable SNAPSHOT handling in the repository", required = false, multiValued = false)
+    boolean snapshots = false;
+
+    @Option(name = "-nr", aliases = { "--no-releases" }, description = "Disable release handling in this repository", required = false, multiValued = false)
+    boolean noReleases = false;
+
+    @Option(name = "-up", aliases = { "--update-policy" }, description = "Update policy for repository (never, daily (default), interval:N, always)", required = false, multiValued = false)
+    String updatePolicy;
+
+    @Option(name = "-cp", aliases = { "--checksum-policy" }, description = "Checksum policy for repository (ignore, warn (default), fail)", required = false, multiValued = false)
+    String checksumPolicy;
+
+    @Option(name = "-u", aliases = { "--username" }, description = "Username for remote repository", required = false, multiValued = false)
+    String username;
+
+    @Option(name = "-p", aliases = { "--password" }, description = "Password for remote repository (may be encrypted, see \"maven:password -ep\")", required = false, multiValued = false)
+    String password;
+
+    @Argument(description = "Repository URI. It may be file:// based, http(s):// based, may use other known protocol or even property placeholders (like ${karaf.base})", required = false)
+    String uri;
+
+    @Override
+    protected void edit(String prefix, Dictionary<String, Object> config,
+                        MavenRepositoryURL[] allRepos, MavenRepositoryURL[] pidRepos, MavenRepositoryURL[] settingsRepos) throws Exception {
+
+        Optional<MavenRepositoryURL> first = Arrays.stream(allRepos)
+                .filter((repo) -> id.equals(repo.getId())).findFirst();
+        if (!first.isPresent()) {
+            System.err.printf("Can't find %s repository with ID \"%s\"\n", (defaultRepository ? "default" : "remote"), id);
+            return;
+        }
+
+        MavenRepositoryURL changedRepository = first.get();
+
+        changedRepository.setSnapshotsEnabled(snapshots);
+        changedRepository.setReleasesEnabled(!noReleases);
+
+        if (updatePolicy != null) {
+            SourceAnd<String> up = updatePolicy(updatePolicy);
+            if (!up.valid) {
+                System.err.println("Unknown value of update policy: \"" + updatePolicy + "\"");
+                return;
+            }
+            changedRepository.setReleasesUpdatePolicy(up.val());
+            changedRepository.setSnapshotsUpdatePolicy(up.val());
+        }
+
+        if (checksumPolicy != null) {
+            SourceAnd<String> cp = checksumPolicy(checksumPolicy);
+            if (!cp.valid) {
+                System.err.println("Unknown value of checksum policy: \"" + checksumPolicy + "\"");
+                return;
+            }
+            changedRepository.setReleasesChecksumPolicy(cp.val());
+            changedRepository.setSnapshotsChecksumPolicy(cp.val());
+        }
+
+        if (uri != null) {
+            SourceAnd<String> urlResolved = validateRepositoryURL(uri, defaultRepository);
+            if (!urlResolved.valid) {
+                return;
+            }
+            changedRepository.setURL(new URL(urlResolved.val()));
+        }
+
+        boolean hasUsername = username != null && !"".equals(username.trim());
+        boolean hasPassword = password != null && !"".equals(password.trim());
+        boolean hasCredentials = hasUsername && hasPassword;
+
+        if ((hasUsername && !hasPassword) || (!hasUsername && hasPassword)) {
+            System.err.println("Please specify both username and password");
+            return;
+        }
+
+        if (defaultRepository && hasCredentials) {
+            System.out.println("User credentials won't be used for default repository");
+            // no return
+        }
+
+        boolean credentialsUpdated = false;
+        // credentials for remote repository can be stored only in settings.xml
+        if (!defaultRepository && hasCredentials) {
+            if (!updateCredentials(force, id, username, password, prefix, config)) {
+                return;
+            }
+            credentialsUpdated = true;
+        }
+
+        if (!defaultRepository && changedRepository.getFrom() == MavenRepositoryURL.FROM.SETTINGS) {
+            // find <repository> in any active profile to change it
+            for (Profile profile : mavenSettings.getProfiles()) {
+                Optional<Repository> repository = profile.getRepositories().stream().filter((r) -> id.equals(r.getId())).findFirst();
+                if (repository.isPresent()) {
+                    // I can't imagine it's not...
+                    Repository r = repository.get();
+                    r.setUrl(changedRepository.getURL().toString());
+                    if (!changedRepository.isSnapshotsEnabled()) {
+                        r.setSnapshots(new RepositoryPolicy());
+                        r.getSnapshots().setEnabled(false);
+                    } else {
+                        RepositoryPolicy rp = r.getSnapshots() == null ? new RepositoryPolicy() : r.getSnapshots();
+                        rp.setEnabled(true);
+                        if (checksumPolicy != null) {
+                            rp.setChecksumPolicy(changedRepository.getSnapshotsChecksumPolicy());
+                        } else if (rp.getChecksumPolicy() == null) {
+                            rp.setChecksumPolicy("warn");
+                        }
+                        if (updatePolicy != null) {
+                            rp.setUpdatePolicy(changedRepository.getSnapshotsUpdatePolicy());
+                        } else if (rp.getUpdatePolicy() == null) {
+                            rp.setUpdatePolicy("daily");
+                        }
+                        r.setSnapshots(rp);
+                    }
+                    if (!changedRepository.isReleasesEnabled()) {
+                        r.setReleases(new RepositoryPolicy());
+                        r.getReleases().setEnabled(false);
+                    } else {
+                        RepositoryPolicy rp = r.getReleases() == null ? new RepositoryPolicy() : r.getReleases();
+                        rp.setEnabled(true);
+                        if (checksumPolicy != null) {
+                            rp.setChecksumPolicy(changedRepository.getReleasesChecksumPolicy());
+                        } else if (rp.getChecksumPolicy() == null) {
+                            rp.setChecksumPolicy("warn");
+                        }
+                        if (updatePolicy != null) {
+                            rp.setUpdatePolicy(changedRepository.getReleasesUpdatePolicy());
+                        } else if (rp.getUpdatePolicy() == null) {
+                            rp.setUpdatePolicy("daily");
+                        }
+                        r.setReleases(rp);
+                    }
+                    updateSettings(prefix, config);
+                    break;
+                }
+            }
+        } else if (changedRepository.getFrom() == MavenRepositoryURL.FROM.PID) {
+            List<MavenRepositoryURL> newRepos = new LinkedList<>();
+            for (MavenRepositoryURL repo : pidRepos) {
+                MavenRepositoryURL _r = repo;
+                if (id.equals(repo.getId())) {
+                    _r = changedRepository;
+                }
+                newRepos.add(_r);
+            }
+            updatePidRepositories(prefix, config, defaultRepository, newRepos, settingsRepos.length > 0);
+            if (credentialsUpdated) {
+                updateSettings(prefix, config);
+            }
+        }
+
+        Configuration cmConfig = cm.getConfiguration(PID);
+        cmConfig.update(config);
+
+        success = true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/32d5c7b7/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 9f403c1..32dcc8f 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
@@ -16,10 +16,21 @@
  */
 package org.apache.karaf.maven.command;
 
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Arrays;
 import java.util.Dictionary;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
 
+import org.apache.felix.utils.properties.InterpolationHelper;
 import org.apache.karaf.maven.core.MavenRepositoryURL;
 import org.apache.karaf.shell.api.action.Option;
+import org.apache.maven.settings.Server;
 
 public abstract class RepositoryEditCommandSupport extends MavenSecuritySupport {
 
@@ -29,21 +40,150 @@ public abstract class RepositoryEditCommandSupport extends MavenSecuritySupport
     @Option(name = "-d", aliases = { "--default" }, description = "Edit default repository instead of remote one", required = false, multiValued = false)
     boolean defaultRepository = false;
 
+    @Option(name = "-f", aliases = { "--force" }, description = "Do not ask for confirmation", required = false, multiValued = false)
+    boolean force = false;
+
     boolean success = false;
 
     @Override
     public final void doAction(String prefix, Dictionary<String, Object> config) throws Exception {
-        edit(prefix, config);
+        if (id == null || "".equals(id.trim())) {
+            System.err.println("Please specify ID of repository");
+            return;
+        }
+
+        MavenRepositoryURL[] repositories = repositories(config, !defaultRepository);
+
+        MavenRepositoryURL[] repositoriesFromPidProperty = Arrays.stream(repositories)
+                .filter((repo) -> repo.getFrom() == MavenRepositoryURL.FROM.PID)
+                .toArray(MavenRepositoryURL[]::new);
+
+        MavenRepositoryURL[] repositoriesFromSettings = Arrays.stream(repositories)
+                .filter((repo) -> repo.getFrom() == MavenRepositoryURL.FROM.SETTINGS)
+                .toArray(MavenRepositoryURL[]::new);
+
+        edit(prefix, config, repositories, repositoriesFromPidProperty, repositoriesFromSettings);
 
         if (success) {
             if (showPasswords) {
-                session.execute("maven:repository-list -x");
+                session.execute("maven:repository-list -v -x");
             } else {
-                session.execute("maven:repository-list");
+                session.execute("maven:repository-list -v");
             }
         }
     }
 
-    protected abstract void edit(String prefix, Dictionary<String, Object> config) throws Exception;
+    /**
+     * Peform action on repository (add, remove, change)
+     * @param prefix property prefix for <code>org.ops4j.pax.url.mvn</code> PID
+     * @param config
+     * @param allRepos
+     * @param pidRepos
+     * @param settingsRepos
+     * @throws Exception
+     */
+    protected abstract void edit(String prefix, Dictionary<String, Object> config,
+                                 MavenRepositoryURL[] allRepos, MavenRepositoryURL[] pidRepos, MavenRepositoryURL[] settingsRepos) throws Exception;
+
+    /**
+     * Stores new repository list in relevant <code>org.ops4j.pax.url.mvn</code> PID property
+     * @param prefix
+     * @param config
+     * @param defaultRepository default (<code>true</code>) or remote repositories?
+     * @param newRepos new list of repositories
+     * @param hasSettingsRepositories whether we have repositories stored in <code>settings.xml</code> as well
+     */
+    protected void updatePidRepositories(String prefix, Dictionary<String, Object> config, boolean defaultRepository,
+                                         List<MavenRepositoryURL> newRepos, boolean hasSettingsRepositories) {
+        String newList = newRepos.stream().map(MavenRepositoryURL::asRepositorySpec)
+                .collect(Collectors.joining(","));
+
+        if (defaultRepository) {
+            config.put(prefix + PROPERTY_DEFAULT_REPOSITORIES, newList);
+        } else {
+            if (hasSettingsRepositories) {
+                newList = "+" + newList;
+            }
+            config.put(prefix + PROPERTY_REPOSITORIES, newList);
+        }
+    }
+
+    /**
+     * Stores credential information in settings, without persisting them
+     * @param force
+     * @param id
+     * @param username
+     * @param password
+     * @param prefix
+     * @param config
+     */
+    protected boolean updateCredentials(boolean force, String id, String username, String password,
+                                      String prefix, Dictionary<String, Object> config) throws IOException {
+        if (!force && !confirm("Maven settings will be updated and org.ops4j.pax.url.mvn.settings property will change. Continue? (y/N) ")) {
+            return false;
+        }
+
+        Optional<Server> existingServer = mavenSettings.getServers().stream()
+                .filter((s) -> id.equals(s.getId())).findAny();
+        Server server = null;
+        if (existingServer.isPresent()) {
+            server = existingServer.get();
+        } else {
+            server = new Server();
+            server.setId(id);
+            mavenSettings.getServers().add(server);
+        }
+        server.setUsername(username);
+        server.setPassword(password);
+
+        return true;
+    }
+
+    /**
+     * Takes passed-in repository URI and performs basic validation
+     * @param uri
+     * @param defaultRepository
+     * @return
+     */
+    protected SourceAnd<String> validateRepositoryURL(String uri, boolean defaultRepository) throws URISyntaxException, MalformedURLException {
+        SourceAnd<String> result = new SourceAnd<String>();
+        result.valid = false;
+
+        if (uri == null || "".equals(uri.trim())) {
+            System.err.println("Please specify repository location");
+            return result;
+        }
+        String urlResolved = InterpolationHelper.substVars(uri, "uri", null, null, context);
+        URL url = null;
+        try {
+            url = new URL(urlResolved);
+            urlResolved = url.toString();
+
+            if ("file".equals(url.getProtocol()) && new File(url.toURI()).isDirectory()) {
+                System.err.println("Location \"" + urlResolved + "\" is not accessible");
+                return result;
+            }
+        } catch (MalformedURLException e) {
+            // a directory?
+            File location = new File(urlResolved);
+            if (!location.exists() || !location.isDirectory()) {
+                System.err.println("Location \"" + urlResolved + "\" is not accessible");
+                return result;
+            } else {
+                url = location.toURI().toURL();
+                urlResolved = url.toString();
+            }
+        }
+
+        if (defaultRepository && !"file".equals(url.getProtocol())) {
+            System.err.println("Default repositories should be locally accessible (use file:// protocol or normal directory path)");
+            return result;
+        }
+
+        result.valid = true;
+        result.value = urlResolved;
+
+        return result;
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/32d5c7b7/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java
index eaf62e5..28c0716 100644
--- a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java
@@ -16,18 +16,75 @@
  */
 package org.apache.karaf.maven.command;
 
+import java.util.Arrays;
 import java.util.Dictionary;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
 
+import org.apache.karaf.maven.core.MavenRepositoryURL;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.maven.settings.Profile;
+import org.apache.maven.settings.Repository;
+import org.apache.maven.settings.Server;
+import org.osgi.service.cm.Configuration;
 
 @Command(scope = "maven", name = "repository-remove", description = "Removes Maven repository")
 @Service
 public class RepositoryRemoveCommand extends RepositoryEditCommandSupport {
 
     @Override
-    protected void edit(String prefix, Dictionary<String, Object> config) throws Exception {
+    protected void edit(String prefix, Dictionary<String, Object> config,
+                        MavenRepositoryURL[] allRepos, MavenRepositoryURL[] pidRepos, MavenRepositoryURL[] settingsRepos) throws Exception {
 
+        Optional<MavenRepositoryURL> first = Arrays.stream(allRepos)
+                .filter((repo) -> id.equals(repo.getId())).findAny();
+        if (!first.isPresent()) {
+            System.err.printf("Can't find %s repository with ID \"%s\"\n", (defaultRepository ? "default" : "remote"), id);
+            return;
+        }
+
+        if (force || confirm(String.format("Are you sure to remove repository with ID \"%s\" for URL %s? (y/N) ", id, first.get().getURL()))) {
+            if (!defaultRepository && first.get().getFrom() == MavenRepositoryURL.FROM.SETTINGS) {
+                // remove <server> (credentials) if available
+                List<Server> newServers = mavenSettings.getServers().stream()
+                        .filter((s) -> !id.equals(s.getId())).collect(Collectors.toList());
+                mavenSettings.setServers(newServers);
+
+                // find <repository> in any active profile and remove it
+                for (Profile profile : mavenSettings.getProfiles()) {
+                    if (profile.getRepositories().stream().anyMatch((r) -> id.equals(r.getId()))) {
+                        List<Repository> newRepos = profile.getRepositories().stream()
+                                .filter((r) -> !id.equals(r.getId())).collect(Collectors.toList());
+                        profile.setRepositories(newRepos);
+                        System.out.printf("Repository with ID \"%s\" was removed from profile \"%s\"\n", id, profile.getId());
+                        break;
+                    }
+                }
+
+                updateSettings(prefix, config);
+            } else if (first.get().getFrom() == MavenRepositoryURL.FROM.PID) {
+                List<MavenRepositoryURL> newRepos = Arrays.stream(pidRepos).filter((r) -> !id.equals(r.getId())).collect(Collectors.toList());
+
+                updatePidRepositories(prefix, config, defaultRepository, newRepos, settingsRepos.length > 0);
+
+                // if there are credentials for this repository, we have to remove them from settings.xml
+                if (mavenSettings.getServers().stream().anyMatch((s) -> id.equals(s.getId()))) {
+                    // remove <server> (credentials) if available
+                    List<Server> newServers = mavenSettings.getServers().stream()
+                            .filter((s) -> !id.equals(s.getId())).collect(Collectors.toList());
+                    mavenSettings.setServers(newServers);
+
+                    updateSettings(prefix, config);
+                }
+            }
+
+            Configuration cmConfig = cm.getConfiguration(PID);
+            cmConfig.update(config);
+
+            success = true;
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/32d5c7b7/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java b/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java
index 1c422c8..7bc5a73 100644
--- a/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java
+++ b/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java
@@ -61,7 +61,7 @@ public class MavenRepositoryURL
     /**
      * Repository URL.
      */
-    private final URL m_repositoryURL;
+    private URL m_repositoryURL;
     /**
      * Repository file (only if URL is a file URL).
      */
@@ -69,27 +69,27 @@ public class MavenRepositoryURL
     /**
      * True if the repository contains snapshots.
      */
-    private final boolean m_snapshotsEnabled;
+    private boolean m_snapshotsEnabled;
     /**
      * True if the repository contains releases.
      */
-    private final boolean m_releasesEnabled;
+    private boolean m_releasesEnabled;
     /**
      * Repository update policy
      */
-    private final String m_releasesUpdatePolicy;
+    private String m_releasesUpdatePolicy;
     /**
      * Repository update policy
      */
-    private final String m_snapshotsUpdatePolicy;
+    private String m_snapshotsUpdatePolicy;
     /**
      * Repository checksum policy
      */
-    private final String m_releasesChecksumPolicy;
+    private String m_releasesChecksumPolicy;
     /**
      * Repository checksum policy
      */
-    private final String m_snapshotsChecksumPolicy;
+    private String m_snapshotsChecksumPolicy;
 
     private final boolean m_multi;
     /**
@@ -288,6 +288,10 @@ public class MavenRepositoryURL
         return m_repositoryURL;
     }
 
+    public void setURL(URL url) {
+        this.m_repositoryURL = url;
+    }
+
     /**
      * Getter.
      *
@@ -308,6 +312,11 @@ public class MavenRepositoryURL
         return m_releasesEnabled;
     }
 
+    public void setReleasesEnabled(boolean enabled)
+    {
+        m_releasesEnabled = enabled;
+    }
+
     /**
      * Getter.
      *
@@ -318,6 +327,11 @@ public class MavenRepositoryURL
         return m_snapshotsEnabled;
     }
 
+    public void setSnapshotsEnabled(boolean enabled)
+    {
+        m_snapshotsEnabled = enabled;
+    }
+
     public String getReleasesUpdatePolicy() {
         return m_releasesUpdatePolicy;
     }
@@ -334,6 +348,22 @@ public class MavenRepositoryURL
         return m_snapshotsChecksumPolicy;
     }
 
+    public void setReleasesUpdatePolicy(String policy) {
+        m_releasesUpdatePolicy = policy;
+    }
+
+    public void setSnapshotsUpdatePolicy(String policy) {
+        m_snapshotsUpdatePolicy = policy;
+    }
+
+    public void setReleasesChecksumPolicy(String policy) {
+        m_releasesChecksumPolicy = policy;
+    }
+
+    public void setSnapshotsChecksumPolicy(String policy) {
+        m_snapshotsChecksumPolicy = policy;
+    }
+
     public FROM getFrom() {
         return m_from;
     }
@@ -402,7 +432,7 @@ public class MavenRepositoryURL
             }
         }
         if (m_snapshotsEnabled && m_releasesEnabled) {
-            // compact snapshots & release update & checksum policies?
+            // compact snapshots & release update & checksum policies
             if (m_releasesUpdatePolicy != null && Objects.equals(m_releasesUpdatePolicy, m_snapshotsUpdatePolicy)) {
                 sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_UPDATE + "=" + m_releasesUpdatePolicy);
             }


[4/9] karaf git commit: [KARAF-5008] maven:password command

Posted by gg...@apache.org.
[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
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


[8/9] karaf git commit: [KARAF-5008] Add documentation section about maven:* commands

Posted by gg...@apache.org.
[KARAF-5008] Add documentation section about maven:* commands


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/2aeb865f
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/2aeb865f
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/2aeb865f

Branch: refs/heads/master
Commit: 2aeb865f6ca8b557a851468ef2ffd9fac790dac4
Parents: 995a482
Author: Grzegorz Grzybek <gr...@gmail.com>
Authored: Thu Jun 22 13:14:13 2017 +0200
Committer: Grzegorz Grzybek <gr...@gmail.com>
Committed: Thu Jun 22 13:14:13 2017 +0200

----------------------------------------------------------------------
 manual/pom.xml                                |   5 +
 manual/src/main/asciidoc/user-guide/urls.adoc | 295 ++++++++++++++++++++-
 2 files changed, 294 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/2aeb865f/manual/pom.xml
----------------------------------------------------------------------
diff --git a/manual/pom.xml b/manual/pom.xml
index 109dd9f..d0f5b4d 100644
--- a/manual/pom.xml
+++ b/manual/pom.xml
@@ -177,6 +177,11 @@
 						<artifactId>org.apache.karaf.subsystem.core</artifactId>
 						<version>${project.version}</version>
 					</dependency>
+					<dependency>
+						<groupId>org.apache.karaf.maven</groupId>
+						<artifactId>org.apache.karaf.maven.core</artifactId>
+						<version>${project.version}</version>
+					</dependency>
 					<!-- Additional Required Dependencies -->
 					<dependency>
 						<groupId>org.apache.felix</groupId>

http://git-wip-us.apache.org/repos/asf/karaf/blob/2aeb865f/manual/src/main/asciidoc/user-guide/urls.adoc
----------------------------------------------------------------------
diff --git a/manual/src/main/asciidoc/user-guide/urls.adoc b/manual/src/main/asciidoc/user-guide/urls.adoc
index 15f6911..f149f75 100644
--- a/manual/src/main/asciidoc/user-guide/urls.adoc
+++ b/manual/src/main/asciidoc/user-guide/urls.adoc
@@ -16,6 +16,7 @@
 
 The main information provided by a feature is the set of OSGi bundles that defines the application.  Such bundles are URLs pointing to the actual bundle jars.  For example, one would write the following definition:
 
+[source,options="nowrap"]
 ----
 <bundle>http://repo1.maven.org/maven2/org/apache/servicemix/nmr/org.apache.servicemix.nmr.api/1.0.0-m2/org.apache.servicemix.nmr.api-1.0.0-m2.jar</bundle>
 ----
@@ -45,16 +46,298 @@ The equivalent of the above bundle would be:
 
 In addition to being less verbose, the Maven url handlers can also resolve snapshots and can use a local copy of the jar if one is available in your Maven local repository.
 
-The `org.ops4j.pax.url.mvn` bundle resolves `mvn` URLs. It can be configured using the file `etc/org.ops4j.pax.url.cfg`
+The `org.ops4j.pax.url.mvn` bundle resolves `mvn` URLs. It can be configured using the file `etc/org.ops4j.pax.url.cfg`.
+Full reference of `org.ops4j.pax.url.mvn` PID configuration can be found https://ops4j1.jira.com/wiki/display/paxurl/Aether+Configuration[on pax-web Wiki page].
 
-The most important property is :
+The most important property is:
 
-* `org.ops4j.pax.url.mvn.repositories` : Comma separated list of repository remote repository URLs that are checked in order of occurence when resolving maven artifacts
+* `org.ops4j.pax.url.mvn.repositories` : Comma separated list of remote repository URLs that are checked in order of occurence when resolving maven artifacts
 
-By default, snapshots are disabled. To enable an URL for snapshots append @snapshots to a repository entry. For example
+Two other significant properties are:
 
+* `org.ops4j.pax.url.mvn.defaulRepositories` : Comma separated list of locations that are checked before querying remote repositories. These can be treated as read-only repositories, as nothing is written there during artifact resolution.
+* `org.ops4j.pax.url.mvn.localRepository` : by default (implicitly) it's standard `~/.m2/repository` location. This
+  local repository is used to store artifacts downloaded from one of remote repositories, so at next resolution attempt
+  no remote request is issued.
+
+By default, snapshots are disabled. To enable an URL for snapshots append `@snapshots` to a repository URI. For example
+
+----
+org.ops4j.pax.url.mvn.repositories = http://www.example.org/repo@snapshots
+----
+
+Repositories on the local machine are supported through `file:/` URLs.
+
+==== Maven configuration commands
+
+Full configuration of `org.ops4j.pax.url.mvn` bundle can be done using `org.ops4j.pax.url.mvn` PID (see `etc/org.ops4j.pax.url.mvn.cfg` file). This however may be cumbersome in some scenarios.
+
+In order to make user's life easier and provide more _domain_ oriented approach, Karaf provides several shell commands that makes Maven configuration easier.
+
+===== maven:summary
+
+This command shows quick summary about current `org.ops4j.pax.url.mvn` PID configuration. For example:
+
+[source,options="nowrap"]
 ----
-http://www.example.org/repo@snapshots
+karaf@root()> maven:summary -s
+
+Option                    │ Value                                                          │ Source
+──────────────────────────┼────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────
+Local repository          │ /home/ggrzybek/.m2/repository                                  │ Implicit ${user.home}/.m2/repository
+Settings file             │ /home/ggrzybek/.m2/settings.xml                                │ Implicit ${user.home}/.m2/settings.xml
+Security settings file    │ /home/ggrzybek/.m2/settings-security.xml                       │ Implicit ${user.home}/.m2/settings-security.xml
+Global update policy      │                                                                │ Implicit "never", but doesn't override repository-specific value
+Global checksum policy    │ warn                                                           │ Default "warn"
+Update releases           │ false                                                          │ Default "false"
+Require Config Admin      │ true                                                           │ BundleContext property (org.ops4j.pax.url.mvn.requireConfigAdminConfig)
+Use fallback repository   │ false                                                          │ Explicit org.ops4j.pax.url.mvn PID configuration (org.ops4j.pax.url.mvn.useFallbackRepositories)
+Offline mode              │ false                                                          │ Default "false"
+SSL/TLS certificate check │ true                                                           │ Explicit org.ops4j.pax.url.mvn PID configuration (org.ops4j.pax.url.mvn.certificateCheck)
+Remote repositories       │ http://repo1.maven.org/maven2/                                 │ PID configuration
+                          │ http://repository.apache.org/content/groups/snapshots-group/   │ PID configuration
+                          │ https://oss.sonatype.org/content/repositories/snapshots/       │ PID configuration
+                          │ https://oss.sonatype.org/content/repositories/ops4j-snapshots/ │ PID configuration
+Default repositories      │ file:/data/servers/apache-karaf-4.2.0-SNAPSHOT/system/         │ PID configuration
+                          │ file:/data/servers/apache-karaf-4.2.0-SNAPSHOT/data/kar/       │ PID configuration
+HTTP proxies              │ proxy.everfree.forest:3128                                     │ Maven XML settings
 ----
 
-Repositories on the local machine are supported through `file:/` URLs.
\ No newline at end of file
+* `-s` option show where the value of the option come from. It may be implicit, explicit or default. We can also see
+  whether the value was configured in PID or in `settings.xml` file.
+* `-p` option uses original option names from `org.ops4j.pax.url.mvn` PID instead of descriptive option names
+* `-d` option shows additional description, explaining what given option should be used for
+* `-x` option turns on password display - if there's master password configured, it'll be displayed in clear text.
+  This option may be used only by user with `admin` role.
+
+
+===== maven:repository-list
+
+This command displays all configured Maven repositories - in much more readable way than plain `config:proplist --pid org.ops4j.pax.url.mvn` command does.
+
+[source,options="nowrap"]
+----
+karaf@root()> maven:repository-list -v
+
+== Remote repositories
+ID                              │ URL                                                            │ Releases    │ Snapshots   │ Defined in
+────────────────────────────────┼────────────────────────────────────────────────────────────────┼─────────────┼─────────────┼───────────
+central                         │ http://repo1.maven.org/maven2/                                 │ yes (daily) │ no          │ PID
+apache                          │ http://repository.apache.org/content/groups/snapshots-group/   │ no          │ yes (daily) │ PID
+sonatype.snapshots.deploy       │ https://oss.sonatype.org/content/repositories/snapshots/       │ no          │ yes (daily) │ PID
+ops4j.sonatype.snapshots.deploy │ https://oss.sonatype.org/content/repositories/ops4j-snapshots/ │ no          │ yes (daily) │ PID
+special                         │ https://repository.everfree.forest/                            │ yes (daily) │ no          │ SETTINGS
+
+== Default repositories
+ID                      │ URL                                                      │ Releases    │ Snapshots
+────────────────────────┼──────────────────────────────────────────────────────────┼─────────────┼────────────
+system.repository       │ file:/data/servers/apache-karaf-4.2.0-SNAPSHOT/system/   │ yes (daily) │ yes (daily)
+kar.repository          │ file:/data/servers/apache-karaf-4.2.0-SNAPSHOT/data/kar/ │ yes (daily) │ yes (daily)
+child.system.repository │ file:/data/servers/apache-karaf-4.2.0-SNAPSHOT/system/   │ yes (daily) │ yes (daily)
+----
+
+* `-v` option shows additional information about policies related to given repository
+* `-x` shows credentials for given repository (if defined)
+
+===== maven:password
+
+`org.ops4j.pax.url.mvn` bundle uses Aether library to handle Maven resolution. It uses `settings.xml` file if
+credentials have to be used when accessing remote Maven repositories. This isn't done by `org.ops4j.pax.url.mvn`,
+but by Aether itself (or rather maven-settings library). When dealing with `settings.xml` file, passwords that
+are stored there may need to be decrypted.
+Outside of Karaf, we can use `mvn -emp` and `mvn -ep` passwords and manually configure `~/.m2/settings-security.xml`
+file.
+
+Karaf makes the task of managing credentials easier.
+
+In order to use encrypted repository (or http proxy) passwords inside `settings.xml` file, Maven must know the _master
+password_ stored inside `settings-security.xml` file. This file isn't usually present inside `~/.m2` directory and if
+there's a need to use it, one has to be created manually.
+
+Here's the way to encrypt Maven _master password_ (which is used to encrypt ordinary passwords for repository or http proxies):
+
+[source,options="nowrap"]
+----
+karaf@root()> maven:password -emp
+Master password to encrypt: *****
+Encrypted master password: {y+p9TiYuwVEHMHV14ej0Ni34zBnXXQrIOqjww/3Ro6U=}
+----
+
+The above usage simply prints encrypted _master password_. We can however make this password persistent. This will
+result in new `settings-security.xml` file to be created and change in `org.ops4j.pax.url.mvn.security` property.
+
+NOTE: Karaf maven commands will never overwrite your current `~/.m2/settings.xml` or `~/.m2/settings-security.xml` files.
+If there's a need to change these files, maven commands will make a copy of existing file and set relevant `org.ops4j.pax.url.mvn` PID options
+to point to new locations.
+
+[source,options="nowrap"]
+----
+karaf@root()> maven:password -emp --persist
+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) y
+Master password to encrypt: *****
+Encrypted master password: {lPPIFSUcPrMHnhwdauttAJYZcOe1D9sYGj4rwoaTwnY=}
+New security settings stored in "/data/servers/apache-karaf-4.2.0-SNAPSHOT/data/cache/bundle53/data/maven-security-settings-1498120766139.xml"
+karaf@root()> maven:summary -x
+
+Option                    │ Value
+──────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────
+...
+Security settings file    │ /data/servers/apache-karaf-4.2.0-SNAPSHOT/data/cache/bundle53/data/maven-security-settings-1498120766139.xml
+Master password           │ admin
+...
+----
+
+Now, when Maven _master password_ is set, we can encrypt ordinary passwords that may be then used when defining/changing
+remote repositories or http proxies:
+
+[source,options="nowrap"]
+----
+karaf@root()> maven:password -ep
+Password to encrypt: *****
+Encrypted password: {fHl8U3pINkEH7RR1CufRT+utj5gJHfqsRgd6wTo92Eo=}
+You can use this encrypted password when defining repositories and proxies
+----
+
+===== Configuring repositories (default and remote)
+
+As mentioned before, there are two kinds of repositories that are used/queried by `org.ops4j.pax.url.mvn` bundle when resolving
+`mvn:` based URIs:
+
+default repositories:: These are read-only local repositories that are simply queried before performing any remote access.
+ The best example of such repository is `$KARAF_HOME/system` directory.
+
+remote repositories:: These are well-known Maven remote repositories - usually accessible over http(s) protocol. Popular
+ repositories are Sonatype Nexus or JFrog Artifactory.
+
+Both kinds of repositories may be created using `maven:repository-add` command.
+
+Here's how default repository may be created:
+
+[source,options="nowrap"]
+----
+karaf@root()> maven:repository-add --default -id my.default.repository --snapshots '${karaf.home}/special-repository'
+
+...
+== Default repositories
+ID                      │ URL                                                                │ Releases    │ Snapshots
+────────────────────────┼────────────────────────────────────────────────────────────────────┼─────────────┼────────────
+...
+my.default.repository   │ file:/data/servers/apache-karaf-4.2.0-SNAPSHOT/special-repository/ │ yes (daily) │ yes (daily)
+----
+
+For remote repository, we can specify more options (like credentials or update policies):
+
+[source,options="nowrap"]
+----
+karaf@root()> maven:repository-add -idx 0 -id my.remote.repository --snapshots -up never --username admin --password '{fHl8U3pINkEH7RR1CufRT+utj5gJHfqsRgd6wTo92Eo=}' http://localhost/cloud-repository
+Maven settings will be updated and org.ops4j.pax.url.mvn.settings property will change. Continue? (y/N) y
+New settings stored in "/data/servers/apache-karaf-4.2.0-SNAPSHOT/data/cache/bundle53/data/maven-settings-1498121385253.xml"
+
+karaf@root()> maven:repository-list -x
+
+== Remote repositories
+ID                              │ URL                                                            │ Username │ Password
+────────────────────────────────┼────────────────────────────────────────────────────────────────┼──────────┼─────────
+my.remote.repository            │ http://localhost/cloud-repository/                             │ admin    │ admin
+...
+----
+
+In the above example, new `settings.xml` file was created. The reason is that although new repository itself was added
+to `org.ops4j.pax.url.mvn.repositories` property, the credentials had to be stored in `settings.xml` file:
+
+[source,options="nowrap"]
+----
+<servers>
+  <server>
+    <username>admin</username>
+    <password>{fHl8U3pINkEH7RR1CufRT+utj5gJHfqsRgd6wTo92Eo=}</password>
+    <id>my.remote.repository</id>
+  </server>
+</servers>
+----
+
+Here's summary of all options for `maven:repository-add` command:
+
+* `-id` mandatory identifier of repository
+* `-d` option may be used to configure default repositories instead of remote ones
+* `-nr` option disables non-SNAPSHOT artifacts resolution in this repository
+* `-s` option enables SNAPSHOT artifacts resolution in this repository
+* `-up` sets _update policy_ for given repository (`daily`, `always`, `never`, `interval:MINUTES`)
+* `-cp` sets _checksum policy_ for given repository (`fail`, `warn`, `ignore`)
+* `-f` disables confirmation prompts for commands
+* `-idx` allows to insert a repository at given position (instead of simply appending new repository at the end of current list of repositories)
+* `-u` sets username for remote repository access
+* `-p` sets password for remote repository access (may be encrypted using `maven:password -ep`)
+
+After creating a repository, it may be deleted (using `maven:repository-remove` command) or changed (`maven:repository-change` command).
+All the options are the same as in `maven:repository-add` command. When removing a repository, only `-id` (and possibly `-d`) options are needed.
+
+[source,options="nowrap"]
+----
+karaf@root()> repository-remove -d -id my.default.repository
+Are you sure to remove repository with ID "my.default.repository" for URL file:/data/servers/apache-karaf-4.2.0-SNAPSHOT/special-repository/? (y/N) y
+
+karaf@root()> repository-change -id special --username discord --password d1sc0rd
+Maven settings will be updated and org.ops4j.pax.url.mvn.settings property will change. Continue? (y/N) y
+New settings stored in "/data/servers/apache-karaf-4.2.0-SNAPSHOT/data/cache/bundle53/data/maven-settings-1498122026388.xml"
+----
+
+===== Configuring HTTP proxies
+
+When accessing remote repositories using `org.ops4j.pax.url.mvn` (Aether library) there may be a need to let Maven/Aether
+know about HTTP proxies to use. HTTP proxies *can't be configured* inside `etc/org.ops4j.pax.url.mvn.cfg` file. It has to
+be done in `settings.xml` and its location has to be set in `org.ops4j.pax.url.mvn.settings` PID property.
+
+`maven:http-proxy` command can be used to add/change/remove HTTP proxy definition. It automatically does a copy
+of existing `settings.xml` file and changes `org.ops4j.pax.url.mvn.settings` PID property.
+
+For example:
+
+[source,options="nowrap"]
+----
+karaf@root()> maven:http-proxy-list -x
+
+ID       │ Host                  │ Port │ Non-proxy hosts │ Username │ Password
+─────────┼───────────────────────┼──────┼─────────────────┼──────────┼─────────────
+my.proxy │ proxy.everfree.forest │ 3128 │ 192.168.2.*     │ admin    │ super-secret
+
+karaf@root()> maven:http-proxy --remove -id my.proxy
+New settings stored in "/data/servers/apache-karaf-4.2.0-SNAPSHOT/data/cache/bundle53/data/maven-settings-1498122255098.xml"
+
+No HTTP proxies configured in /data/servers/apache-karaf-4.2.0-SNAPSHOT/data/cache/bundle53/data/maven-settings-1498122255098.xml
+
+karaf@root()> maven:http-proxy --add -id my.proxy --username discord --password '{fHl8U3pINkEH7RR1CufRT+utj5gJHfqsRgd6wTo92Eo=}' --non-proxy-hosts '127.*|192.168.*|localhost' proxy.everfree.forest:3128
+New settings stored in "/data/servers/apache-karaf-4.2.0-SNAPSHOT/data/cache/bundle53/data/maven-settings-1498122328731.xml"
+
+karaf@root()> maven:http-proxy-list -x
+
+ID       │ Host                  │ Port │ Non-proxy hosts           │ Username │ Password
+─────────┼───────────────────────┼──────┼───────────────────────────┼──────────┼─────────
+my.proxy │ proxy.everfree.forest │ 3128 │ 127.*|192.168.*|localhost │ discord  │ admin
+----
+
+Here's summary of options for `maven:http-proxy` command:
+
+* `-id` identifier of HTTP proxy
+* `-add` / `--change` / `--remove` is an operation to perform on proxy
+* `-f` disables confirmation prompts for commands
+* `-u` sets username for remote HTTP proxy
+* `-p` sets password for remote HTTP proxy (may be encrypted using `maven:password -ep`)
+* `-n` sets _non proxy hosts_ option, which is `|`-separated list of glob patterns for IP addresses/host names that should be
+  accessed bypassing HTTP proxy
+
+`maven:http-proxy` configures for example this section in `settings.xml`:
+
+[source,options="nowrap"]
+----
+<proxies>
+  <proxy>
+    <username>discord</username>
+    <password>{fHl8U3pINkEH7RR1CufRT+utj5gJHfqsRgd6wTo92Eo=}</password>
+    <port>3128</port>
+    <host>proxy.everfree.forest</host>
+    <nonProxyHosts>127.*|192.168.*|localhost</nonProxyHosts>
+    <id>my.proxy</id>
+  </proxy>
+</proxies>
+----


[9/9] karaf git commit: [KARAF-5008] Add "maven" feature with ACL configuration

Posted by gg...@apache.org.
[KARAF-5008] Add "maven" feature with ACL configuration


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/e4585a99
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/e4585a99
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/e4585a99

Branch: refs/heads/master
Commit: e4585a99c5ec6d1f770f7a496a3648d9aa189b08
Parents: 2aeb865
Author: Grzegorz Grzybek <gr...@gmail.com>
Authored: Thu Jun 22 13:14:44 2017 +0200
Committer: Grzegorz Grzybek <gr...@gmail.com>
Committed: Thu Jun 22 13:15:06 2017 +0200

----------------------------------------------------------------------
 .../standard/src/main/feature/feature.xml       | 23 +++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/e4585a99/assemblies/features/standard/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/standard/src/main/feature/feature.xml b/assemblies/features/standard/src/main/feature/feature.xml
index 0993c27..d120d88 100644
--- a/assemblies/features/standard/src/main/feature/feature.xml
+++ b/assemblies/features/standard/src/main/feature/feature.xml
@@ -955,7 +955,28 @@
         </config>
         <bundle>mvn:org.jolokia/jolokia-osgi/${jolokia.version}</bundle>
     </feature>
-    
+
+    <feature name="maven" description="Commands for Maven configuration of services from pax-url-aether" version="${project.version}">
+        <feature>shell</feature>
+        <bundle start-level="30" start="true">mvn:org.apache.karaf.maven/org.apache.karaf.maven.core/${project.version}</bundle>
+        <config name="org.apache.karaf.command.acl.maven">
+            #
+            # This configuration file defines the ACLs for maven configuration commands
+            #
+            summary[/.*[-][x].*/] = admin
+            summary = viewer
+            http-proxy-list[/.*[-][x].*/] = admin
+            http-proxy-list = viewer
+            repository-list[/.*[-][x].*/] = admin
+            repository-list = viewer
+            http-proxy = admin
+            password = admin
+            repository-add = admin
+            repository-change = admin
+            repository-remove = admin
+        </config>
+    </feature>
+
     <feature name="standard" description="Wrap feature describing all features part of a standard distribution" version="${project.version}">
         <feature>wrap</feature>
         <feature>aries-blueprint</feature>


[3/9] karaf git commit: [KARAF-5008] maven:repository-add command

Posted by gg...@apache.org.
[KARAF-5008] maven:repository-add command


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/ede23baa
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/ede23baa
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/ede23baa

Branch: refs/heads/master
Commit: ede23baa73e76256db60187b4cbc5916aafa1231
Parents: ecc21a3
Author: Grzegorz Grzybek <gr...@gmail.com>
Authored: Wed Jun 21 09:33:51 2017 +0200
Committer: Grzegorz Grzybek <gr...@gmail.com>
Committed: Wed Jun 21 15:31:54 2017 +0200

----------------------------------------------------------------------
 maven/core/pom.xml                              |   6 +
 .../command/MavenConfigurationSupport.java      |  81 +++++++-
 .../karaf/maven/command/PasswordCommand.java    |   2 +
 .../maven/command/RepositoryAddCommand.java     | 196 ++++++++++++++++++-
 .../command/RepositoryEditCommandSupport.java   |  22 ++-
 .../maven/command/RepositoryRemoveCommand.java  |   4 +-
 .../karaf/maven/command/SummaryCommand.java     |  51 -----
 .../karaf/maven/core/MavenRepositoryURL.java    |  47 +++++
 .../org/apache/karaf/maven/SettingsTest.java    |   2 +
 .../command/MavenConfigurationSupportTest.java  |   8 +-
 .../maven/core/MavenRepositoryURLTest.java      |  64 ++++++
 11 files changed, 420 insertions(+), 63 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/ede23baa/maven/core/pom.xml
----------------------------------------------------------------------
diff --git a/maven/core/pom.xml b/maven/core/pom.xml
index a2a724f..403835e 100644
--- a/maven/core/pom.xml
+++ b/maven/core/pom.xml
@@ -66,6 +66,11 @@
             <artifactId>maven-embedder</artifactId>
             <version>3.0.3</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.utils</artifactId>
+            <scope>provided</scope>
+        </dependency>
 
         <dependency>
             <groupId>org.slf4j</groupId>
@@ -111,6 +116,7 @@
                             *,
                         </Import-Package>
                         <Private-Package>
+                            org.apache.felix.utils.properties;-split-package:=merge-first,
                             org.apache.karaf.maven.command,
                             org.apache.karaf.maven.core,
                             org.apache.maven.settings.*;-split-package:=merge-first,

http://git-wip-us.apache.org/repos/asf/karaf/blob/ede23baa/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 0588afd..b64e583 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
@@ -460,7 +460,7 @@ public abstract class MavenConfigurationSupport implements Action {
         String[] repositories = listOfValues((String) config.get(property));
 
         if (remote) {
-            if (repositories.length == 0 || repositories.length > 0 && repositories[0].charAt(0) == '+') {
+            if (repositories.length == 0 || repositories[0].charAt(0) == '+') {
                 if (repositories.length > 0) {
                     repositories[0] = repositories[0].substring(1);
                 }
@@ -604,13 +604,92 @@ public abstract class MavenConfigurationSupport implements Action {
         return result;
     }
 
+    /**
+     * This method controls whether passwords are tried to be decrypted.
+     * @return
+     */
     protected boolean showPasswords() {
         return false;
     }
 
+    /**
+     * Parses update policy value and returns {@link SourceAnd}<code>&lt;String&gt;</code> about the value
+     * @param policy
+     * @return
+     */
+    protected SourceAnd<String> updatePolicy(String policy) {
+        SourceAnd<String> result = new SourceAnd<>();
+        result.value = policy;
+
+        if (policy == null || "".equals(policy.trim())) {
+            result.value = "";
+            result.valid = false;
+            result.source = "Implicit \"never\", but doesn't override repository-specific value";
+            return result;
+        }
+
+        result.source = String.format(PATTERN_PID_PROPERTY, PID, PID + "." + PROPERTY_GLOBAL_UPDATE_POLICY);
+        if ("always".equals(policy) || "never".equals(policy) || "daily".equals(policy)) {
+            // ok
+            result.valid = true;
+        } else if (policy.startsWith("interval")) {
+            int minutes = 1440;
+            try {
+                String n = policy.substring("interval".length() + 1);
+                minutes = Integer.parseInt(n);
+                result.valid = true;
+            } catch (Exception e) {
+                result.valid = false;
+                result.value = "interval:1440";
+                result.source = "Implicit \"interval:1440\" (error parsing \"" + policy + "\")";
+            }
+        } else {
+            result.valid = false;
+            result.value = "never";
+            result.source = "Implicit \"never\" (unknown value \"" + policy + "\")";
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses checksum policy value and returns {@link SourceAnd}<code>&lt;String&gt;</code> about the value
+     * @param policy
+     * @return
+     */
+    protected SourceAnd<String> checksumPolicy(String policy) {
+        SourceAnd<String> result = new SourceAnd<>();
+        result.value = policy;
+
+        if (policy == null || "".equals(policy.trim())) {
+            result.valid = false;
+            result.value = "warn";
+            result.source = "Default \"warn\"";
+            return result;
+        }
+
+        result.source = String.format(PATTERN_PID_PROPERTY, PID, PID + "." + PROPERTY_GLOBAL_CHECKSUM_POLICY);
+        if ("ignore".equals(policy) || "warn".equals(policy) || "fail".equals(policy)) {
+            // ok
+            result.valid = true;
+        } else {
+            result.valid = false;
+            result.value = "warn";
+            result.source = "Implicit \"warn\" (unknown value \"" + policy + "\")";
+        }
+
+        return result;
+    }
+
+    /**
+     * Handy class containing value and information about its origin. <code>valid</code> may be used to indicate
+     * if the value is correct. It may be implicit, but the interpretation of <code>valid </code> is not defined.
+     * @param <T>
+     */
     protected static class SourceAnd<T> {
         String source;
         T value;
+        boolean valid;
 
         public SourceAnd() {
         }

http://git-wip-us.apache.org/repos/asf/karaf/blob/ede23baa/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
index ed44b35..4243212 100644
--- 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
@@ -61,6 +61,7 @@ public class PasswordCommand extends MavenConfigurationSupport {
             }
             String password = session.readLine("Password to encrypt: ", '*');
             System.out.println("Encrypted password: " + cipher.encryptAndDecorate(password, masterPassword));
+            System.out.println("You can use this encrypted password when defining repositories and proxies");
             return;
         }
 
@@ -79,6 +80,7 @@ public class PasswordCommand extends MavenConfigurationSupport {
                 File dataDir = context.getDataFile(".");
                 if (!dataDir.isDirectory()) {
                     System.err.println("Can't access data directory for " + context.getBundle().getSymbolicName() + " bundle");
+                    return;
                 }
                 File newSecuritySettingsFile = nextSequenceFile(dataDir, RE_SECURITY_SETTINGS, PATTERN_SECURITY_SETTINGS);
                 try (FileWriter fw = new FileWriter(newSecuritySettingsFile)) {

http://git-wip-us.apache.org/repos/asf/karaf/blob/ede23baa/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java
index 13bcbdb..11034d3 100644
--- a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryAddCommand.java
@@ -16,20 +16,210 @@
  */
 package org.apache.karaf.maven.command;
 
+import java.io.File;
+import java.io.FileWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.apache.felix.utils.properties.InterpolationHelper;
+import org.apache.karaf.maven.core.MavenRepositoryURL;
+import org.apache.karaf.shell.api.action.Argument;
 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.apache.maven.settings.Server;
+import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
+import org.ops4j.pax.url.mvn.ServiceConstants;
+import org.osgi.service.cm.Configuration;
 
 @Command(scope = "maven", name = "repository-add", description = "Adds Maven repository")
 @Service
 public class RepositoryAddCommand extends RepositoryEditCommandSupport {
 
-    @Option(name = "-x", aliases = { "--show-passwords" }, description = "Do not hide passwords related to Maven encryption", required = false, multiValued = false)
-    boolean showPasswords;
+    @Option(name = "-idx", description = "Index at which new repository is to be inserted (0-based) (defaults to last - repository will be appended)", required = false, multiValued = false)
+    int idx = -1;
+
+    @Option(name = "-s", aliases = { "--snapshots" }, description = "Enable SNAPSHOT handling in the repository", required = false, multiValued = false)
+    boolean snapshots = false;
+
+    @Option(name = "-nr", aliases = { "--no-releases" }, description = "Disable release handling in this repository", required = false, multiValued = false)
+    boolean noReleases = false;
+
+    @Option(name = "-up", aliases = { "--update-policy" }, description = "Update policy for repository (never, daily (default), interval:N, always)", required = false, multiValued = false)
+    String updatePolicy = "daily";
+
+    @Option(name = "-cp", aliases = { "--checksum-policy" }, description = "Checksum policy for repository (ignore, warn (default), fail)", required = false, multiValued = false)
+    String checksumPolicy = "warn";
+
+    @Argument(description = "Repository URI. It may be file:// based, http(s):// based, may use other known protocol or even property placeholders (like ${karaf.base})")
+    String uri;
+
+    @Option(name = "-u", aliases = { "--username" }, description = "Username for remote repository", required = false, multiValued = false)
+    String username;
+
+    @Option(name = "-p", aliases = { "--password" }, description = "Password for remote repository (may be encrypted, see \"maven:password -ep\")", required = false, multiValued = false)
+    String password;
 
     @Override
-    protected void edit() {
+    protected void edit(String prefix, Dictionary<String, Object> config) throws Exception {
+        MavenRepositoryURL[] repositories = repositories(config, !defaultRepository);
+
+        MavenRepositoryURL[] repositoriesFromPidProperty = Arrays.stream(repositories)
+                .filter((repo) -> repo.getFrom() == MavenRepositoryURL.FROM.PID)
+                .toArray(MavenRepositoryURL[]::new);
+
+        if (idx > repositoriesFromPidProperty.length) {
+            // TOCONSIDER: should we allow to add repository to settings.xml too?
+            System.err.printf("List of %s repositories has %d elements. Can't insert at position %s.\n",
+                    (defaultRepository ? "default" : "remote"), repositories.length, id);
+            return;
+        }
+
+        if (id == null || "".equals(id.trim())) {
+            System.err.println("Please specify ID of repository");
+            return;
+        }
+
+        Optional<MavenRepositoryURL> first = Arrays.stream(repositories)
+                .filter((repo) -> id.equals(repo.getId())).findAny();
+        if (first.isPresent()) {
+            System.err.printf("Repository with ID \"%s\" is already configured for URL %s\n", id, first.get().getURL());
+            return;
+        }
+
+        SourceAnd<String> up = updatePolicy(updatePolicy);
+        if (!up.valid) {
+            System.err.println("Unknown value of update policy: \"" + updatePolicy + "\"");
+            return;
+        }
+
+        SourceAnd<String> cp = checksumPolicy(checksumPolicy);
+        if (!cp.valid) {
+            System.err.println("Unknown value of checksum policy: \"" + checksumPolicy + "\"");
+            return;
+        }
+
+        if (uri == null || "".equals(uri.trim())) {
+            System.err.println("Please specify repository location");
+            return;
+        }
+        String urlResolved = InterpolationHelper.substVars(uri, "uri", null, null, context);
+        URL url = null;
+        try {
+            url = new URL(urlResolved);
+            urlResolved = url.toString();
+
+            if ("file".equals(url.getProtocol()) && new File(url.toURI()).isDirectory()) {
+                System.err.println("Location \"" + urlResolved + "\" is not accessible");
+                return;
+            }
+        } catch (MalformedURLException e) {
+            // a directory?
+            File location = new File(urlResolved);
+            if (!location.exists() || !location.isDirectory()) {
+                System.err.println("Location \"" + urlResolved + "\" is not accessible");
+                return;
+            } else {
+                url = location.toURI().toURL();
+                urlResolved = url.toString();
+            }
+        }
+
+        if (defaultRepository && !"file".equals(url.getProtocol())) {
+            System.err.println("Default repositories should be locally accessible (use file:// protocol or normal directory path)");
+            return;
+        }
+
+        boolean hasUsername = username != null && !"".equals(username.trim());
+        boolean hasPassword = password != null && !"".equals(password.trim());
+        boolean hasCredentials = hasUsername && hasPassword;
+
+        if ((hasUsername && !hasPassword) || (!hasUsername && hasPassword)) {
+            System.err.println("Please specify both username and password");
+            return;
+        }
+
+        if (defaultRepository && hasCredentials) {
+            System.out.println("User credentials won't be used for default repository");
+            // no return
+        }
+
+        // credentials for remote repository can be stored only in settings.xml
+
+        if (!defaultRepository && hasCredentials) {
+            if (!confirm("Maven settings will be updated and org.ops4j.pax.url.mvn.settings property will change. Continue? (y/N) ")) {
+                return;
+            }
+
+            File dataDir = context.getDataFile(".");
+            if (!dataDir.isDirectory()) {
+                System.err.println("Can't access data directory for " + context.getBundle().getSymbolicName() + " bundle");
+                return;
+            }
+
+            Optional<Server> existingServer = mavenSettings.getServers().stream()
+                    .filter((s) -> id.equals(s.getId())).findAny();
+            Server server = null;
+            if (existingServer.isPresent()) {
+                server = existingServer.get();
+            } else {
+                server = new Server();
+                server.setId(id);
+                mavenSettings.getServers().add(server);
+            }
+            server.setUsername(username);
+            server.setPassword(password);
+
+            // prepare configadmin configuration update
+            File newSettingsFile = nextSequenceFile(dataDir, RE_SETTINGS, PATTERN_SETTINGS);
+            config.put(prefix + PROPERTY_SETTINGS_FILE, newSettingsFile.getCanonicalPath());
+
+            try (FileWriter fw = new FileWriter(newSettingsFile)) {
+                new SettingsXpp3Writer().write(fw, mavenSettings);
+            }
+            System.out.println("New settings stored in \"" + newSettingsFile.getCanonicalPath() + "\"");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append(urlResolved);
+        sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_ID + "=" + id);
+        if (snapshots) {
+            sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_ALLOW_SNAPSHOTS);
+        }
+        if (noReleases) {
+            sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_DISALLOW_RELEASES);
+        }
+        sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_UPDATE + "=" + updatePolicy);
+        sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_CHECKSUM + "=" + checksumPolicy);
+
+        MavenRepositoryURL newRepository = new MavenRepositoryURL(sb.toString());
+        List<MavenRepositoryURL> newRepos = new LinkedList<>(Arrays.asList(repositoriesFromPidProperty));
+        if (idx >= 0) {
+            newRepos.add(idx, newRepository);
+        } else {
+            newRepos.add(newRepository);
+        }
+
+        String newList = newRepos.stream().map(MavenRepositoryURL::asRepositorySpec)
+                .collect(Collectors.joining(","));
+
+        if (defaultRepository) {
+            config.put(prefix + PROPERTY_DEFAULT_REPOSITORIES, newList);
+        } else {
+            config.put(prefix + PROPERTY_REPOSITORIES, newList);
+        }
+
+        Configuration cmConfig = cm.getConfiguration(PID);
+        cmConfig.update(config);
 
+        success = true;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/ede23baa/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 12700d0..9f403c1 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
@@ -23,13 +23,27 @@ import org.apache.karaf.shell.api.action.Option;
 
 public abstract class RepositoryEditCommandSupport extends MavenSecuritySupport {
 
+    @Option(name = "-id", description = "Identifier of repository", required = true, multiValued = false)
+    String id;
+
+    @Option(name = "-d", aliases = { "--default" }, description = "Edit default repository instead of remote one", required = false, multiValued = false)
+    boolean defaultRepository = false;
+
+    boolean success = false;
+
     @Override
-    public void doAction(String prefix, Dictionary<String, Object> config) throws Exception {
-        edit();
+    public final void doAction(String prefix, Dictionary<String, Object> config) throws Exception {
+        edit(prefix, config);
 
-        session.execute("maven:repository-list");
+        if (success) {
+            if (showPasswords) {
+                session.execute("maven:repository-list -x");
+            } else {
+                session.execute("maven:repository-list");
+            }
+        }
     }
 
-    protected abstract void edit();
+    protected abstract void edit(String prefix, Dictionary<String, Object> config) throws Exception;
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/ede23baa/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java
index 46f82c8..eaf62e5 100644
--- a/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/RepositoryRemoveCommand.java
@@ -16,6 +16,8 @@
  */
 package org.apache.karaf.maven.command;
 
+import java.util.Dictionary;
+
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 
@@ -24,7 +26,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
 public class RepositoryRemoveCommand extends RepositoryEditCommandSupport {
 
     @Override
-    protected void edit() {
+    protected void edit(String prefix, Dictionary<String, Object> config) throws Exception {
 
     }
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/ede23baa/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 3f7e91a..c4e4c03 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
@@ -185,55 +185,4 @@ public class SummaryCommand extends MavenSecuritySupport {
         }
     }
 
-    private SourceAnd<String> updatePolicy(String cmProperty) {
-        SourceAnd<String> result = new SourceAnd<>();
-        result.value = cmProperty;
-
-        if (cmProperty == null || "".equals(cmProperty.trim())) {
-            result.value = "";
-            result.source = "Implicit \"never\", but doesn't override repository-specific value";
-            return result;
-        }
-
-        result.source = String.format(PATTERN_PID_PROPERTY, PID, PID + "." + PROPERTY_GLOBAL_UPDATE_POLICY);
-        if ("always".equals(cmProperty) || "never".equals(cmProperty) || "daily".equals(cmProperty)) {
-            // ok
-        } else if (cmProperty.startsWith("interval")) {
-            int minutes = 1440;
-            try {
-                String n = cmProperty.substring("interval".length() + 1);
-                minutes = Integer.parseInt(n);
-            } catch (Exception e) {
-                result.value = "interval:1440";
-                result.source = "Implicit \"interval:1440\" (error parsing \"" + cmProperty + "\")";
-            }
-        } else {
-            result.value = "never";
-            result.source = "Implicit \"never\" (unknown value \"" + cmProperty + "\")";
-        }
-
-        return result;
-    }
-
-    private SourceAnd<String> checksumPolicy(String cmProperty) {
-        SourceAnd<String> result = new SourceAnd<>();
-        result.value = cmProperty;
-
-        if (cmProperty == null || "".equals(cmProperty.trim())) {
-            result.value = "warn";
-            result.source = "Default \"warn\"";
-            return result;
-        }
-
-        result.source = String.format(PATTERN_PID_PROPERTY, PID, PID + "." + PROPERTY_GLOBAL_CHECKSUM_POLICY);
-        if ("ignore".equals(cmProperty) || "warn".equals(cmProperty) || "fail".equals(cmProperty)) {
-            // ok
-        } else {
-            result.value = "warn";
-            result.source = "Implicit \"warn\" (unknown value \"" + cmProperty + "\")";
-        }
-
-        return result;
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/ede23baa/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java b/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java
index 630976b..1c422c8 100644
--- a/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java
+++ b/maven/core/src/main/java/org/apache/karaf/maven/core/MavenRepositoryURL.java
@@ -22,6 +22,7 @@ import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.util.Objects;
 
 import org.ops4j.pax.url.mvn.ServiceConstants;
 import org.slf4j.Logger;
@@ -367,6 +368,52 @@ public class MavenRepositoryURL
             .toString();
     }
 
+    public String asRepositorySpec() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(m_repositoryURL.toString());
+        if (m_id != null) {
+            sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_ID + "=" + m_id);
+        }
+        if (!m_releasesEnabled) {
+            sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_DISALLOW_RELEASES);
+        }
+        if (m_snapshotsEnabled) {
+            sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_ALLOW_SNAPSHOTS);
+        }
+        if (m_releasesEnabled) {
+            if (!m_snapshotsEnabled) {
+                if (m_releasesUpdatePolicy != null) {
+                    sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_RELEASES_UPDATE + "=" + m_releasesUpdatePolicy);
+                }
+                if (m_releasesChecksumPolicy != null) {
+                    sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_RELEASES_CHECKSUM + "=" + m_releasesChecksumPolicy);
+                }
+            }
+        }
+        if (m_snapshotsEnabled) {
+            if (!m_releasesEnabled) {
+                if (m_snapshotsUpdatePolicy != null) {
+                    sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_SNAPSHOTS_UPDATE + "=" + m_snapshotsUpdatePolicy);
+                }
+                if (m_snapshotsChecksumPolicy != null) {
+                    sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_SNAPSHOTS_CHECKSUM + "=" + m_snapshotsChecksumPolicy);
+                }
+            }
+        }
+        if (m_snapshotsEnabled && m_releasesEnabled) {
+            // compact snapshots & release update & checksum policies?
+            if (m_releasesUpdatePolicy != null && Objects.equals(m_releasesUpdatePolicy, m_snapshotsUpdatePolicy)) {
+                sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_UPDATE + "=" + m_releasesUpdatePolicy);
+            }
+            if (m_releasesChecksumPolicy != null && Objects.equals(m_releasesChecksumPolicy, m_snapshotsChecksumPolicy)) {
+                sb.append(ServiceConstants.SEPARATOR_OPTIONS + ServiceConstants.OPTION_CHECKSUM + "=" + m_releasesChecksumPolicy);
+            }
+        }
+
+        return sb.toString();
+    }
+
     public static enum FROM {
         PID("PID configuration"),
         SETTINGS("Maven XML settings"),

http://git-wip-us.apache.org/repos/asf/karaf/blob/ede23baa/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 3ecaf30..901efc3 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
@@ -32,6 +32,8 @@ public class SettingsTest {
         Settings settings = new Settings();
         Server s = new Server();
         s.setId("id");
+        s.setUsername("admin");
+        s.setPassword("admin");
         settings.getServers().add(s);
 
         new DefaultSettingsWriter().write(System.out, null, settings);

http://git-wip-us.apache.org/repos/asf/karaf/blob/ede23baa/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
index 16ffc3f..991ace1 100644
--- 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
@@ -22,11 +22,13 @@ import java.io.IOException;
 import java.util.Dictionary;
 import java.util.regex.Pattern;
 
+import org.hamcrest.CoreMatchers;
 import org.junit.Test;
 import shaded.org.apache.commons.io.FileUtils;
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 public class MavenConfigurationSupportTest {
 
@@ -42,19 +44,19 @@ public class MavenConfigurationSupportTest {
         };
 
         File newFile = support.nextSequenceFile(dataDir, Pattern.compile("file-(\\d+).txt"), "file-%04d.txt");
-        assertThat(newFile.getName(), equalTo("file-0001.txt"));
+        assertTrue(Pattern.compile("^file-\\d+\\.txt$").matcher(newFile.getName()).matches());
 
         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"));
+        assertTrue(Pattern.compile("^file-\\d+\\.txt$").matcher(newFile.getName()).matches());
 
         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"));
+        assertTrue(Pattern.compile("^file-\\d+\\.txt$").matcher(newFile.getName()).matches());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/ede23baa/maven/core/src/test/java/org/apache/karaf/maven/core/MavenRepositoryURLTest.java
----------------------------------------------------------------------
diff --git a/maven/core/src/test/java/org/apache/karaf/maven/core/MavenRepositoryURLTest.java b/maven/core/src/test/java/org/apache/karaf/maven/core/MavenRepositoryURLTest.java
new file mode 100644
index 0000000..d460e0e
--- /dev/null
+++ b/maven/core/src/test/java/org/apache/karaf/maven/core/MavenRepositoryURLTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.core;
+
+import java.net.MalformedURLException;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class MavenRepositoryURLTest {
+
+    @Test
+    public void uris() throws MalformedURLException {
+        String uri1, uri2;
+        MavenRepositoryURL mavenURI;
+
+        uri1 = "http://localhost/@id=id1@snapshots@update=interval:42@_from=" + MavenRepositoryURL.FROM.SETTINGS;
+        uri2 = "http://localhost/@id=id1@snapshots@update=interval:42";
+        mavenURI = new MavenRepositoryURL(uri1);
+        assertThat(mavenURI.asRepositorySpec(), equalTo(uri2));
+
+        uri1 = "http://localhost/@id=id1@snapshots@checksum=fail@_from=" + MavenRepositoryURL.FROM.SETTINGS;
+        uri2 = "http://localhost/@id=id1@snapshots@checksum=fail";
+        mavenURI = new MavenRepositoryURL(uri1);
+        assertThat(mavenURI.asRepositorySpec(), equalTo(uri2));
+
+        uri1 = "http://localhost/@id=id1@snapshots@noreleases@update=interval:42@_from=" + MavenRepositoryURL.FROM.SETTINGS;
+        uri2 = "http://localhost/@id=id1@noreleases@snapshots@snapshotsUpdate=interval:42";
+        mavenURI = new MavenRepositoryURL(uri1);
+        assertThat(mavenURI.asRepositorySpec(), equalTo(uri2));
+
+        uri1 = "http://localhost/@id=id1@update=interval:42@_from=" + MavenRepositoryURL.FROM.SETTINGS;
+        uri2 = "http://localhost/@id=id1@releasesUpdate=interval:42";
+        mavenURI = new MavenRepositoryURL(uri1);
+        assertThat(mavenURI.asRepositorySpec(), equalTo(uri2));
+
+        uri1 = "http://localhost/@id=id1@snapshots@noreleases@checksum=fail@_from=" + MavenRepositoryURL.FROM.SETTINGS;
+        uri2 = "http://localhost/@id=id1@noreleases@snapshots@snapshotsChecksum=fail";
+        mavenURI = new MavenRepositoryURL(uri1);
+        assertThat(mavenURI.asRepositorySpec(), equalTo(uri2));
+
+        uri1 = "http://localhost/@id=id1@checksum=fail@_from=" + MavenRepositoryURL.FROM.SETTINGS;
+        uri2 = "http://localhost/@id=id1@releasesChecksum=fail";
+        mavenURI = new MavenRepositoryURL(uri1);
+        assertThat(mavenURI.asRepositorySpec(), equalTo(uri2));
+    }
+
+}


[7/9] karaf git commit: [manual] Fix adoc indentation

Posted by gg...@apache.org.
[manual] Fix adoc indentation


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/995a4829
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/995a4829
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/995a4829

Branch: refs/heads/master
Commit: 995a48299e234a9784050c3f081038946ae07253
Parents: 6a67663
Author: Grzegorz Grzybek <gr...@gmail.com>
Authored: Thu Jun 22 09:12:51 2017 +0200
Committer: Grzegorz Grzybek <gr...@gmail.com>
Committed: Thu Jun 22 09:12:51 2017 +0200

----------------------------------------------------------------------
 .../main/asciidoc/user-guide/os-integration.adoc  | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/995a4829/manual/src/main/asciidoc/user-guide/os-integration.adoc
----------------------------------------------------------------------
diff --git a/manual/src/main/asciidoc/user-guide/os-integration.adoc b/manual/src/main/asciidoc/user-guide/os-integration.adoc
index 19ae83c..b6310a1 100644
--- a/manual/src/main/asciidoc/user-guide/os-integration.adoc
+++ b/manual/src/main/asciidoc/user-guide/os-integration.adoc
@@ -26,7 +26,7 @@ The above methods allow you to directly integrate Apache Karaf:
 * like a native Windows Service
 * like a Unix daemon process
 
-===== Service Wrapper
+==== Service Wrapper
 
 The "Service Wrapper" correctly handles "user's log outs" under Windows, service dependencies, and the ability to run services which interact with the desktop.
 
@@ -34,7 +34,7 @@ It also includes advanced fault detection software which monitors an application
 The "Service Wrapper" is able to detect crashes, freezes, out of memory and other exception events, then automatically react by restarting Apache Karaf with a minimum of delay.
 It guarantees the maximum possible uptime of Apache Karaf.
 
-====== Supported platforms
+===== Supported platforms
 
 * Windows 8, 7, 2008 R2, 2003, Vista (32 and 64 bits architecture)
 * Linux RedHat Enterprise Linux, Debian, CentOS, Fedora, Ubuntu (32 and 64 bits architecture)
@@ -45,7 +45,7 @@ It guarantees the maximum possible uptime of Apache Karaf.
 * SGI Irix
 * MacOS X
 
-====== Installation
+===== Installation
 
 Apache Karaf Service Wrapper is an optional feature. You have to install the "Service Wrapper" installer first.
 
@@ -377,7 +377,7 @@ wrapper.ntservice.starttype=AUTO_START
 wrapper.ntservice.interactive=false
 ----
 
-===== Service Script Templates
+==== Service Script Templates
 
 By using the "Service Script Templates", you can run Apache Karaf with the help of operating system specific init scripts.
 
@@ -390,7 +390,7 @@ You can find these templates under the bin/contrib directory.
 
 {nbsp} +
 
-====== Unix
+===== Unix
 
 The karaf-service.sh utility helps you to generate ready to use scripts by automatically identify the operating system, the default init system and the template to use.
 
@@ -419,7 +419,7 @@ Command line option, Environment variable, Description
 
 {nbsp} +
 
-====== Systemd
+===== Systemd
 
 When karaf-service.sh detect Systemd, it generates three files:
 
@@ -442,7 +442,7 @@ $ systemctl enable karaf-4.service
 
 {nbsp} +
 
-====== SysV
+===== SysV
 
 When karaf-service.sh detect a SysV system, it generates two files:
 
@@ -466,7 +466,7 @@ To enable service startup upon boot, please consult your operating system init g
 
 {nbsp} +
 
-====== Solaris SMF
+===== Solaris SMF
 
 When karaf-service.sh detect a Solaris system, it generates a single file:
 
@@ -486,7 +486,7 @@ The generated SMF descriptor is defined as transient so the start method is exec
 
 {nbsp} +
 
-====== Windows
+===== Windows
 
 Installation of Apache Karaf as windows service is supported through https://github.com/kohsuke/winsw[winsw].
 


[5/9] karaf git commit: [KARAF-5008] maven:http-proxy and maven:http-proxy-list commands

Posted by gg...@apache.org.
[KARAF-5008] maven:http-proxy and maven:http-proxy-list commands


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/6a676630
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/6a676630
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/6a676630

Branch: refs/heads/master
Commit: 6a6766303030ac9437ba3920d98a4abea1edb605
Parents: 32d5c7b
Author: Grzegorz Grzybek <gr...@gmail.com>
Authored: Wed Jun 21 15:26:45 2017 +0200
Committer: Grzegorz Grzybek <gr...@gmail.com>
Committed: Wed Jun 21 15:31:54 2017 +0200

----------------------------------------------------------------------
 .../karaf/maven/command/HttpProxyCommand.java   | 126 ++++++++++++++-----
 .../maven/command/HttpProxyListCommand.java     |  70 +++++++++++
 .../src/test/resources/reference-settings.xml   |   1 +
 3 files changed, 168 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/6a676630/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 129aca5..d7a3bfa 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
@@ -17,13 +17,17 @@
 package org.apache.karaf.maven.command;
 
 import java.util.Dictionary;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
 
+import org.apache.karaf.shell.api.action.Argument;
 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.apache.karaf.shell.support.table.Row;
-import org.apache.karaf.shell.support.table.ShellTable;
 import org.apache.maven.settings.Proxy;
+import org.osgi.service.cm.Configuration;
 
 @Command(scope = "maven", name = "http-proxy", description = "Manage HTTP proxy configuration for Maven remote repositories")
 @Service
@@ -32,53 +36,117 @@ public class HttpProxyCommand extends MavenSecuritySupport {
     @Option(name = "--add", description = "Adds HTTP proxy configuration to Maven settings", required = false, multiValued = false)
     boolean add;
 
+    @Option(name = "--change", description = "Changes HTTP proxy configuration in Maven settings", required = false, multiValued = false)
+    boolean change;
+
     @Option(name = "--remove", description = "Removes HTTP proxy configuration from Maven settings", required = false, multiValued = false)
     boolean remove;
 
+    @Option(name = "-id", description = "Identifier of HTTP proxy", required = true, multiValued = false)
+    String id;
+
+    @Option(name = "-f", aliases = { "--force" }, description = "Do not ask for confirmation", required = false, multiValued = false)
+    boolean force = false;
+
+    @Option(name = "-u", aliases = { "--username" }, description = "Username for remote repository", required = false, multiValued = false)
+    String username;
+
+    @Option(name = "-p", aliases = { "--password" }, description = "Password for remote repository (may be encrypted, see \"maven:password -ep\")", required = false, multiValued = false)
+    String password;
+
+    @Option(name = "-n", aliases = { "--non-proxy-hosts" }, description = "Non-proxied hosts (in the format '192.168.*|localhost|...')", required = false, multiValued = false)
+    String nonProxyHosts;
+
+    @Argument(description = "host:port of HTTP proxy", required = false, multiValued = false)
+    String hostPort;
+
     @Override
     public void doAction(String prefix, Dictionary<String, Object> config) throws Exception {
-        if (add && remove) {
-            System.err.println("Please specify only one of --add and --remove");
+        if (add && (change || remove) || change && remove) {
+            System.err.println("Please specify only one of --add/--change/--remove");
             return;
         }
 
+        if (id == null || "".equals(id.trim())) {
+            System.err.println("Please specify ID of HTTP proxy");
+            return;
+        }
+
+        if (mavenSettings.getProxies() == null) {
+            mavenSettings.setProxies(new LinkedList<>());
+        }
+        Optional<Proxy> existingProxy = mavenSettings.getProxies().stream()
+                .filter((p) -> id.equals(p.getId())).findAny();
+
         if (add) {
-            // add
+            if (hostPort == null || "".equals(hostPort.trim())) {
+                System.err.println("Please specify host:port of new HTTP proxy");
+                return;
+            }
+            if (existingProxy.isPresent()) {
+                System.err.printf("HTTP proxy with ID \"%s\" is already configured\n", id);
+                return;
+            }
+        } else if (!existingProxy.isPresent()) {
+            System.err.printf("Can't find HTTP proxy with ID \"%s\"\n", id);
+            return;
         }
 
-        if (remove) {
+        boolean hasUsername = username != null && !"".equals(username.trim());
+        boolean hasPassword = password != null && !"".equals(password.trim());
+        boolean hasCredentials = hasUsername && hasPassword;
+
+        if ((hasUsername && !hasPassword) || (!hasUsername && hasPassword)) {
+            System.err.println("Please specify both username and password");
+            return;
+        }
+
+        Proxy proxy = null;
+        if (add) {
+            proxy = new Proxy();
+            proxy.setId(id);
+            mavenSettings.getProxies().add(proxy);
+        } else if (change) {
+            proxy = existingProxy.get(); // should be there
+        } else /*if (remove)*/ {
             // remove
+            List<Proxy> newProxies = mavenSettings.getProxies().stream()
+                    .filter((p) -> !id.equals(p.getId())).collect(Collectors.toList());
+            mavenSettings.setProxies(newProxies);
         }
 
-        // list (also after --add or --remove)
-        System.out.println();
-        if (mavenSettings != null && mavenSettings.getProxies() != null && mavenSettings.getProxies().size() > 0) {
-            ShellTable table = new ShellTable();
-            table.column("ID");
-            table.column("Host");
-            table.column("Port");
-            table.column("Username");
-            if (showPasswords) {
-                table.column("Password");
+        if (add || change) {
+            proxy.setActive(true);
+            proxy.setProtocol("http");
+            if (nonProxyHosts != null && !"".equals(nonProxyHosts.trim())) {
+                proxy.setNonProxyHosts(nonProxyHosts);
             }
-            for (Proxy proxy : mavenSettings.getProxies()) {
-                Row row = table.addRow();
-                row.addContent(proxy.getId(), proxy.getHost(), proxy.getPort());
-                row.addContent(proxy.getUsername() != null ? proxy.getUsername() : "");
-                if (showPasswords) {
-                    addPasswordInfo(row, proxyPasswords, proxy.getId(), proxy.getPassword());
+            if (hostPort != null && !"".equals(hostPort.trim())) {
+                if (hostPort.contains(":")) {
+                    proxy.setHost(hostPort.substring(0, hostPort.indexOf(':')));
+                    proxy.setPort(Integer.parseInt(hostPort.substring(hostPort.indexOf(':') + 1)));
+                } else {
+                    proxy.setHost(hostPort);
+                    proxy.setPort(3128);
                 }
             }
-            table.print(System.out);
-        } else {
-            System.out.print("No HTTP proxies configured");
-            if (settings != null && settings.value != null) {
-                System.out.print(" in " + settings.value);
+            if (hasCredentials) {
+                proxy.setUsername(username);
+                proxy.setPassword(password);
             }
-            System.out.println();
         }
 
-        System.out.println();
+        updateSettings(prefix, config);
+
+        Configuration cmConfig = cm.getConfiguration(PID);
+        cmConfig.update(config);
+
+        // list (also after --add or --remove)
+        if (showPasswords) {
+            session.execute("maven:http-proxy-list -x");
+        } else {
+            session.execute("maven:http-proxy-list");
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/6a676630/maven/core/src/main/java/org/apache/karaf/maven/command/HttpProxyListCommand.java
----------------------------------------------------------------------
diff --git a/maven/core/src/main/java/org/apache/karaf/maven/command/HttpProxyListCommand.java b/maven/core/src/main/java/org/apache/karaf/maven/command/HttpProxyListCommand.java
new file mode 100644
index 0000000..799b0da
--- /dev/null
+++ b/maven/core/src/main/java/org/apache/karaf/maven/command/HttpProxyListCommand.java
@@ -0,0 +1,70 @@
+/*
+ * 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.util.Dictionary;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.apache.karaf.shell.api.action.Argument;
+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.apache.karaf.shell.support.table.Row;
+import org.apache.karaf.shell.support.table.ShellTable;
+import org.apache.maven.settings.Proxy;
+
+@Command(scope = "maven", name = "http-proxy-list", description = "Lists HTTP proxy configurations for Maven remote repositories")
+@Service
+public class HttpProxyListCommand extends MavenSecuritySupport {
+
+    @Override
+    public void doAction(String prefix, Dictionary<String, Object> config) throws Exception {
+        System.out.println();
+        if (mavenSettings != null && mavenSettings.getProxies() != null && mavenSettings.getProxies().size() > 0) {
+            ShellTable table = new ShellTable();
+            table.column("ID");
+            table.column("Host");
+            table.column("Port");
+            table.column("Non-proxy hosts");
+            table.column("Username");
+            if (showPasswords) {
+                table.column("Password");
+            }
+            for (Proxy _p : mavenSettings.getProxies()) {
+                Row row = table.addRow();
+                row.addContent(_p.getId(), _p.getHost(), _p.getPort(), _p.getNonProxyHosts());
+                row.addContent(_p.getUsername() != null ? _p.getUsername() : "");
+                if (showPasswords) {
+                    addPasswordInfo(row, proxyPasswords, _p.getId(), _p.getPassword());
+                }
+            }
+            table.print(System.out);
+        } else {
+            System.out.print("No HTTP proxies configured");
+            if (settings != null && settings.value != null) {
+                System.out.print(" in " + settings.value);
+            }
+            System.out.println();
+        }
+
+        System.out.println();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/6a676630/maven/core/src/test/resources/reference-settings.xml
----------------------------------------------------------------------
diff --git a/maven/core/src/test/resources/reference-settings.xml b/maven/core/src/test/resources/reference-settings.xml
index 25b58fb..979ab11 100644
--- a/maven/core/src/test/resources/reference-settings.xml
+++ b/maven/core/src/test/resources/reference-settings.xml
@@ -29,6 +29,7 @@
             <protocol>http</protocol>
             <username>username</username>
             <password>password</password>
+            <nonProxyHosts></nonProxyHosts>
         </proxy>
     </proxies>