KNOX-1410 - Knox Shell support for remote Alias management (#210)

diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/AbstractAliasRequest.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/AbstractAliasRequest.java
new file mode 100644
index 0000000..39da1bd
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/AbstractAliasRequest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.knox.gateway.shell.alias;
+
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.knox.gateway.shell.AbstractRequest;
+import org.apache.knox.gateway.shell.ErrorResponse;
+import org.apache.knox.gateway.shell.KnoxSession;
+import org.apache.knox.gateway.shell.KnoxShellException;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+public abstract class AbstractAliasRequest extends AbstractRequest<AliasResponse> {
+
+  public static final String SERVICE_PATH = "/admin/api/v1/aliases";
+
+  protected static final String GATEWAY_CLUSTER_NAME = "__gateway";
+
+  protected enum RequestType { GET, PUT, POST, DELETE }
+
+  private HttpRequestBase httpRequest;
+
+  protected URI requestURI;
+
+  protected String clusterName;
+
+  protected abstract RequestType getRequestType();
+
+  AbstractAliasRequest(final KnoxSession session) {
+    this(session, null, null);
+  }
+
+  AbstractAliasRequest(final KnoxSession session, final String clusterName) {
+    this(session, clusterName, null);
+  }
+
+  AbstractAliasRequest(final KnoxSession session, final String clusterName, final String doAsUser) {
+    super(session, doAsUser);
+    this.clusterName = clusterName != null ? clusterName : GATEWAY_CLUSTER_NAME;
+  }
+
+
+  public URI getRequestURI() {
+    return requestURI;
+  }
+
+  public HttpRequestBase getRequest() {
+    return httpRequest;
+  }
+
+  @Override
+  protected Callable<AliasResponse> callable() {
+    return () -> {
+      httpRequest = createRequest();
+      try {
+        return new AliasResponse(execute(httpRequest));
+      } catch (ErrorResponse e) {
+        return new AliasResponse(e.getResponse());
+      }
+    };
+  }
+
+  protected URI buildURI() {
+    try {
+      URIBuilder uri = uri(getPathElements().toArray(new String[]{}));
+      return uri.build();
+    } catch (URISyntaxException e) {
+      throw new KnoxShellException(e);
+    }
+  }
+
+  protected List<String> getPathElements() {
+    List<String> elements = new ArrayList<>();
+    elements.add(SERVICE_PATH);
+    if (clusterName != null) {
+      elements.add("/");
+      elements.add(clusterName);
+    }
+    return elements;
+  }
+
+  protected HttpRequestBase createRequest() {
+    HttpRequestBase request;
+
+    switch (getRequestType()) {
+      case POST:
+        request = new HttpPost(requestURI);
+        break;
+      case PUT:
+        request = new HttpPut(requestURI);
+        break;
+      case DELETE:
+        request = new HttpDelete(requestURI);
+        break;
+      case GET:
+      default:
+        request = new HttpGet(requestURI);
+        break;
+    }
+    return request;
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/Alias.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/Alias.java
new file mode 100644
index 0000000..e8a9f1a
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/Alias.java
@@ -0,0 +1,53 @@
+/*
+ * 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.knox.gateway.shell.alias;
+
+import org.apache.knox.gateway.shell.KnoxSession;
+
+public class Alias {
+
+  public static AbstractAliasRequest list(final KnoxSession session) {
+    return new ListRequest(session);
+  }
+
+  public static AbstractAliasRequest list(final KnoxSession session,
+                                          final String      clusterName) {
+    return new ListRequest(session, clusterName);
+  }
+
+  public static AbstractAliasRequest add(final KnoxSession session, final String alias, final String pwd) {
+    return new PostRequest(session, alias, pwd);
+  }
+
+  public static AbstractAliasRequest add(final KnoxSession session,
+                                         final String      clusterName,
+                                         final String      alias,
+                                         final String      pwd) {
+    return new PostRequest(session, clusterName, alias, pwd);
+  }
+
+  public static AbstractAliasRequest remove(final KnoxSession session, final String alias) {
+    return new DeleteRequest(session, alias);
+  }
+
+  public static AbstractAliasRequest remove(final KnoxSession session,
+                                            final String      clusterName,
+                                            final String      alias) {
+    return new DeleteRequest(session, clusterName, alias);
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/AliasResponse.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/AliasResponse.java
new file mode 100644
index 0000000..544764f
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/AliasResponse.java
@@ -0,0 +1,28 @@
+/*
+ * 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.knox.gateway.shell.alias;
+
+import org.apache.http.HttpResponse;
+import org.apache.knox.gateway.shell.BasicResponse;
+
+public class AliasResponse extends BasicResponse {
+
+  AliasResponse(HttpResponse response) {
+    super(response);
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/DeleteRequest.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/DeleteRequest.java
new file mode 100644
index 0000000..28101c0
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/DeleteRequest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.knox.gateway.shell.alias;
+
+import org.apache.knox.gateway.shell.KnoxSession;
+
+import java.util.List;
+
+public class DeleteRequest extends AbstractAliasRequest {
+
+  private String alias;
+
+  DeleteRequest(final KnoxSession session, final String alias) {
+    this(session, null, alias);
+  }
+
+  DeleteRequest(final KnoxSession session, final String clusterName, final String alias) {
+    this(session, clusterName, alias, null);
+  }
+
+  DeleteRequest(final KnoxSession session,
+                final String      clusterName,
+                final String      alias,
+                final String      doAsUser) {
+    super(session, clusterName, doAsUser);
+    this.alias = alias;
+    requestURI = buildURI();
+  }
+
+  @Override
+  protected RequestType getRequestType() {
+    return RequestType.DELETE;
+  }
+
+  @Override
+  protected List<String> getPathElements() {
+    List<String> elements = super.getPathElements();
+    elements.add("/");
+    elements.add(alias);
+    return elements;
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/ListRequest.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/ListRequest.java
new file mode 100644
index 0000000..7b0f33d
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/ListRequest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.knox.gateway.shell.alias;
+
+import org.apache.knox.gateway.shell.KnoxSession;
+
+public class ListRequest extends AbstractAliasRequest {
+
+  ListRequest(KnoxSession session) {
+    this(session, null);
+  }
+
+  ListRequest(final KnoxSession session, final String clusterName) {
+    this(session, clusterName, null);
+  }
+
+  ListRequest(final KnoxSession session, final String clusterName, final String doAsUser) {
+    super(session, clusterName, doAsUser);
+    requestURI = buildURI();
+  }
+
+  @Override
+  protected RequestType getRequestType() {
+    return RequestType.GET;
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/PostRequest.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/PostRequest.java
new file mode 100644
index 0000000..11af9c1
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/alias/PostRequest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.knox.gateway.shell.alias;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.knox.gateway.shell.KnoxSession;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PostRequest extends AbstractAliasRequest {
+
+  static final String FORM_PARAM_VALUE = "value";
+
+  private String alias;
+  private String pwd;
+
+  PostRequest(final KnoxSession session, final String alias, final String pwd) {
+    this(session, null, alias, pwd);
+  }
+
+  PostRequest(final KnoxSession session, final String clusterName, final String alias, final String pwd) {
+    this(session, clusterName, alias, pwd, null);
+  }
+
+  PostRequest(final KnoxSession session,
+              final String      clusterName,
+              final String      alias,
+              final String      pwd,
+              final String      doAsUser) {
+    super(session, clusterName, doAsUser);
+    this.alias = alias;
+    this.pwd   = pwd;
+    requestURI = buildURI();
+  }
+
+  @Override
+  protected RequestType getRequestType() {
+    return RequestType.POST;
+  }
+
+  @Override
+  protected List<String> getPathElements() {
+    List<String> elements = super.getPathElements();
+    elements.add("/");
+    elements.add(alias);
+    return elements;
+  }
+
+  @Override
+  protected HttpRequestBase createRequest() {
+    HttpRequestBase request = super.createRequest();
+    List<NameValuePair> formData = new ArrayList<>();
+    formData.add(new BasicNameValuePair(FORM_PARAM_VALUE, pwd));
+    ((HttpPost) request).setEntity(new UrlEncodedFormEntity(formData, StandardCharsets.UTF_8));
+    return request;
+  }
+
+}
diff --git a/gateway-shell/src/test/java/org/apache/knox/gateway/shell/alias/AliasTest.java b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/alias/AliasTest.java
new file mode 100644
index 0000000..0a9f900
--- /dev/null
+++ b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/alias/AliasTest.java
@@ -0,0 +1,255 @@
+/*
+ * 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.knox.gateway.shell.alias;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpRequest;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.util.EntityUtils;
+import org.apache.knox.gateway.shell.KnoxSession;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.concurrent.Callable;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class AliasTest {
+
+  @Test
+  public void testAddGatewayAlias() {
+    KnoxSession session = null;
+    try {
+      session = createMockKnoxSession();
+    } catch (Exception e) {
+      fail(e.getMessage());
+    }
+
+    final String alias = "aliasPut1";
+    final String pwd   = "aliasPut1_pwd";
+
+    final String expectedEndpointPath =
+        session.base() + AbstractAliasRequest.SERVICE_PATH + "/" + AbstractAliasRequest.GATEWAY_CLUSTER_NAME + "/" + alias;
+
+    AbstractAliasRequest request = Alias.add(session, alias, pwd);
+    assertTrue(request instanceof PostRequest);
+    assertEquals("Endpoint mismatch", expectedEndpointPath, request.getRequestURI().toString());
+
+    Callable callable = request.callable();
+    try {
+      callable.call();
+    } catch (Exception e) {
+      // expected
+    }
+
+    assertEquals("Unexpected HTTP method.", HttpPost.METHOD_NAME, request.getRequest().getMethod());
+
+    HttpRequestBase httpRequest = request.getRequest();
+    assertTrue(httpRequest instanceof HttpPost);
+    HttpEntity entity = ((HttpPost) httpRequest).getEntity();
+    assertNotNull("Missing expected form data.", entity);
+    assertTrue(entity instanceof UrlEncodedFormEntity);
+    String formData = null;
+    try {
+      formData = EntityUtils.toString(entity);
+    } catch (IOException e) {
+      fail("Failed to consume request entity: " + e.getMessage());
+    }
+    assertNotNull(formData);
+    assertEquals("Form data mismatch",
+                 PostRequest.FORM_PARAM_VALUE + "=" + pwd,
+                 formData);
+  }
+
+  @Test
+  public void testAddClusterAlias() {
+    KnoxSession session = null;
+    try {
+      session = createMockKnoxSession();
+    } catch (Exception e) {
+      fail(e.getMessage());
+    }
+
+    final String clusterName = "myCluster";
+    final String alias       = "aliasPut1";
+    final String pwd         = "aliasPut1_pwd";
+
+    final String expectedEndpointPath = session.base() + AbstractAliasRequest.SERVICE_PATH + "/" + clusterName + "/" + alias;
+
+    AbstractAliasRequest request = Alias.add(session, clusterName, alias, pwd);
+    assertTrue(request instanceof PostRequest);
+    assertEquals("Endpoint mismatch", expectedEndpointPath, request.getRequestURI().toString());
+
+    Callable callable = request.callable();
+    try {
+      callable.call();
+    } catch (Exception e) {
+      // expected
+    }
+
+    assertEquals("Unexpected HTTP method.", HttpPost.METHOD_NAME, request.getRequest().getMethod());
+
+    HttpRequestBase httpRequest = request.getRequest();
+    assertTrue(httpRequest instanceof HttpPost);
+    HttpEntity entity = ((HttpPost) httpRequest).getEntity();
+    assertNotNull("Missing expected form data.", entity);
+    assertTrue(entity instanceof UrlEncodedFormEntity);
+    String formData = null;
+    try {
+      formData = EntityUtils.toString(entity);
+    } catch (IOException e) {
+      fail("Failed to consume request entity: " + e.getMessage());
+    }
+    assertNotNull(formData);
+    assertEquals("Form data mismatch",
+                 PostRequest.FORM_PARAM_VALUE + "=" + pwd,
+                 formData);
+  }
+
+  @Test
+  public void testRemoveGatewayAlias() {
+    KnoxSession session = null;
+    try {
+      session = createMockKnoxSession();
+    } catch (Exception e) {
+      fail(e.getMessage());
+    }
+
+    final String alias = "aliasPut1";
+
+    final String expectedEndpointPath =
+        session.base() + AbstractAliasRequest.SERVICE_PATH + "/" + AbstractAliasRequest.GATEWAY_CLUSTER_NAME + "/" + alias;
+
+    AbstractAliasRequest request = Alias.remove(session, alias);
+    assertTrue(request instanceof DeleteRequest);
+    assertEquals("Endpoint mismatch", expectedEndpointPath, request.getRequestURI().toString());
+
+    Callable callable = request.callable();
+    try {
+      callable.call();
+    } catch (Exception e) {
+      // expected
+    }
+
+    assertEquals("Unexpected HTTP method.", HttpDelete.METHOD_NAME, request.getRequest().getMethod());
+  }
+
+  @Test
+  public void testRemoveClusterAlias() {
+    KnoxSession session = null;
+    try {
+      session = createMockKnoxSession();
+    } catch (Exception e) {
+      fail(e.getMessage());
+    }
+
+    final String clusterName = "myCluster";
+    final String alias       = "aliasPut1";
+
+    final String expectedEndpointPath = session.base() + AbstractAliasRequest.SERVICE_PATH + "/" + clusterName + "/" + alias;
+
+    AbstractAliasRequest request = Alias.remove(session, clusterName, alias);
+    assertTrue(request instanceof DeleteRequest);
+    assertEquals("Endpoint mismatch", expectedEndpointPath, request.getRequestURI().toString());
+
+    Callable callable = request.callable();
+    try {
+      callable.call();
+    } catch (Exception e) {
+      // expected
+    }
+
+    assertEquals("Unexpected HTTP method.", HttpDelete.METHOD_NAME, request.getRequest().getMethod());
+  }
+
+  @Test
+  public void testListGatewayAliases() {
+    KnoxSession session = null;
+    try {
+      session = createMockKnoxSession();
+    } catch (Exception e) {
+      fail(e.getMessage());
+    }
+
+    final String expectedEndpointPath =
+        session.base() + AbstractAliasRequest.SERVICE_PATH + "/" + AbstractAliasRequest.GATEWAY_CLUSTER_NAME;
+
+    AbstractAliasRequest request = Alias.list(session);
+    assertTrue(request instanceof ListRequest);
+    assertEquals("Endpoint mismatch", expectedEndpointPath, request.getRequestURI().toString());
+
+    Callable callable = request.callable();
+    try {
+      callable.call();
+    } catch (Exception e) {
+      // expected
+    }
+
+    assertEquals("Unexpected HTTP method.", HttpGet.METHOD_NAME, request.getRequest().getMethod());
+  }
+
+
+  @Test
+  public void testListClusterAliases() {
+    KnoxSession session = null;
+    try {
+      session = createMockKnoxSession();
+    } catch (Exception e) {
+      fail(e.getMessage());
+    }
+
+    final String clusterName = "myCluster";
+    final String expectedEndpointPath = session.base() + AbstractAliasRequest.SERVICE_PATH + "/" + clusterName;
+
+    AbstractAliasRequest request = Alias.list(session, clusterName);
+    assertTrue(request instanceof ListRequest);
+    assertEquals("Endpoint mismatch", expectedEndpointPath, request.getRequestURI().toString());
+
+    Callable callable = request.callable();
+    try {
+      callable.call();
+    } catch (Exception e) {
+      // expected
+    }
+
+    assertEquals("Unexpected HTTP method.", HttpGet.METHOD_NAME, request.getRequest().getMethod());
+  }
+
+
+  private KnoxSession createMockKnoxSession() throws Exception {
+    KnoxSession knoxSession = createMock(KnoxSession.class);
+    expect(knoxSession.base()).andReturn("http://localhost/base").atLeastOnce();
+    expect(knoxSession.getHeaders()).andReturn(Collections.emptyMap()).atLeastOnce();
+    expect(knoxSession.executeNow(isA(HttpRequest.class))).andReturn(null).atLeastOnce();
+    replay(knoxSession);
+    return knoxSession;
+  }
+
+
+}