Package com.sonatype.maven.shell.nexus.internal

Source Code of com.sonatype.maven.shell.nexus.internal.NexusClientImpl

/*******************************************************************************
* Copyright (c) 2009-2011 Sonatype, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
*   http://www.eclipse.org/legal/epl-v10.html
* The Apache License v2.0 is available at
*   http://www.apache.org/licenses/LICENSE-2.0.html
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/

package com.sonatype.maven.shell.nexus.internal;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.sonatype.maven.shell.nexus.NexusClient;
import com.sonatype.maven.shell.nexus.internal.marshal.NexusMarshallerFactory;
import com.sonatype.maven.shell.nexus.internal.wink.BasicAuthSecurityHandler;
import com.sonatype.maven.shell.nexus.internal.wink.LoggingHandler;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.wink.client.ApacheHttpClientConfig;
import org.apache.wink.client.ClientConfig;
import org.apache.wink.client.ClientResponse;
import org.apache.wink.client.Resource;
import org.apache.wink.client.RestClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonatype.nexus.rest.model.ErrorMessage;
import org.sonatype.nexus.rest.model.ErrorResponse;
import org.sonatype.security.rest.model.AuthenticationLoginResource;
import org.sonatype.security.rest.model.AuthenticationLoginResourceResponse;

import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* Nexus client implementation.
*
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
* @since 0.9
*/
@Singleton
public class NexusClientImpl
    implements NexusClient
{
    private static final Logger log = LoggerFactory.getLogger(NexusClientImpl.class);

    private static final String DEFAULT_INSTANCE = "local";

    private static final String AUTH_COOKIE_NAME = "JSESSIONID";

    private final NexusMarshallerFactory marshallerFactory = new NexusMarshallerFactory();

    private final Map<String,Extension> extensions;

    private URI baseUri;

    private String instance;

    private RestClient client;

    private Cookie authCookie;

    @Inject
    public NexusClientImpl(final Map<String,Extension> extensions) {
        assert extensions != null;
        this.extensions = extensions;

        if (log.isDebugEnabled()) {
            log.debug("Extensions:");
            for (Map.Entry<String,Extension> entry : extensions.entrySet()) {
                log.debug("  {} -> {}", entry.getKey(), entry.getValue());
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            close();
        }
        finally {
            super.finalize();
        }
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "{" +
            "baseUri=" + baseUri +
            ", instance='" + instance + '\'' +
            ", open=" + isOpen() +
            ", auth=" + isAuthenticated() +
            '}';
    }

    public URI getBaseUri() {
        return baseUri;
    }

    public String getInstance() {
        return instance;
    }

    private void open(final URI baseUri, final String instance, final String username, final String password) {
        if (isOpen()) {
            close();
        }

        assert baseUri != null;
        this.baseUri = baseUri;
        this.instance = instance != null ? instance : DEFAULT_INSTANCE;

        log.debug("Opening w/username: {}", username);

        // Using Apache HttpClient here, as it appears the default wink client does not handle redirects properly, even when configured to do so
        HttpClient httpClient = new DefaultHttpClient();
        ClientConfig config = new ApacheHttpClientConfig(httpClient);
        config.followRedirects(true);

        config.handlers(
            new LoggingHandler(log, /*debug*/ false),
            new BasicAuthSecurityHandler(username, password)
        );

        this.client = new RestClient(config);
    }

    public void open(final URI baseUri, final String instance) {
        open(baseUri, instance, null, null);
    }

    public boolean isOpen() {
        return client != null;
    }

    public void ensureOpened() {
        if (!isOpen()) {
            throw new IllegalStateException("Not opened");
        }
    }

    public void close() {
        if (isOpen()) {
            log.debug("Closing");
            client = null;
        }
    }

    private RestClient getClient() {
        ensureOpened();
        return client;
    }

    //
    // Marshaling
    //

    @SuppressWarnings({"unchecked"})
    public String marshal(final Class type, final Object content) {
        return marshallerFactory.create(type).marshal(content);
    }

    public <T> T unmarshal(final Class<T> type, final String content) {
        return marshallerFactory.create(type).unmarshal(content);
    }

    //
    // Resource Access
    //

    private Resource resource(final URI uri, final MultivaluedMap<String,String> params, final MediaType type, final MediaType accept) {
        assert uri != null;
        // params could be null
        // type could be null
        // accept could be null

        Resource resource = getClient().resource(uri);

        if (params != null) {
            resource = resource.queryParams(params);
        }

        // Default content and accept types to XML if not configured
        resource = (type != null) ? resource.contentType(type) : resource.contentType(MediaType.APPLICATION_XML_TYPE);
        resource = (accept != null) ? resource.accept(type) : resource.accept(MediaType.APPLICATION_XML_TYPE);

        if (authCookie != null) {
            resource = resource.cookie(authCookie);
        }

        return resource;
    }

    private Resource resource(final URI uri, final MediaType type, final MediaType accept) {
        return resource(uri, null, type, accept);
    }

    private Resource resource(final URI uri) {
        return resource(uri, null, null, null);
    }

    private Resource resource(final URI uri, final MultivaluedMap<String,String> params) {
        return resource(uri, params, null, null);
    }

    private Response.StatusType statusForCode(final int code) {
        Response.StatusType status = Response.Status.fromStatusCode(code);
        if (status == null) {
            status = new Response.StatusType()
            {
                public int getStatusCode() {
                    return code;
                }

                public Response.Status.Family getFamily() {
                    switch(code/100) {
                        case 1: return Response.Status.Family.INFORMATIONAL;
                        case 2: return Response.Status.Family.SUCCESSFUL;
                        case 3: return Response.Status.Family.REDIRECTION;
                        case 4: return Response.Status.Family.CLIENT_ERROR;
                        case 5: return Response.Status.Family.SERVER_ERROR;
                    }
                    return Response.Status.Family.OTHER;
                }

                public String getReasonPhrase() {
                    return "Unknown Status";
                }
            };
        }
        return status;
    }
   
    private String getContent(final ClientResponse response) {
        assert response != null;

        final int code = response.getStatusCode();
        log.debug("Response status code: {}", code);

        Response.StatusType status = statusForCode(code);

        if (status.getFamily() != Response.Status.Family.SUCCESSFUL) {
            List<ErrorMessage> errors;

            String content = response.getEntity(String.class);
            if (content.contains("<nexus-error")) {
                errors = unmarshal(ErrorResponse.class, content).getErrors();
            }
            else {
                ErrorMessage msg = new ErrorMessage();
                msg.setId("unknown-error-response");
                msg.setMsg(content);
                errors = new ArrayList<ErrorMessage>(1);
                errors.add(msg);
            }

            throw new RequestFailed(status, errors);
        }

        if (status != Response.Status.NO_CONTENT) {
            return response.getEntity(String.class);
        }
        else {
            return null;
        }
    }

    //
    // Extension Request Access
    //

    public String get(final URI uri) {
        assert uri != null;

        log.debug("Requesting(GET): {}", uri);
        ClientResponse response = resource(uri).get(ClientResponse.class);
        return getContent(response);
    }

    public String get(final URI uri, final MultivaluedMap<String,String> params) {
        assert uri != null;

        log.debug("Requesting(GET): {} w/params: {}", uri, params);
        ClientResponse response = resource(uri, params).get(ClientResponse.class);
        return getContent(response);
    }

    public String head(final URI uri) {
        assert uri != null;

        log.debug("Requesting(HEAD): {}", uri);
        ClientResponse response = resource(uri).head();
        return getContent(response);
    }

    public String post(final URI uri, final String payload) {
        assert uri != null;

        log.debug("Requesting(POST): {}, payload: {}", uri, payload);
        ClientResponse response = resource(uri).post(ClientResponse.class, payload);
        return getContent(response);
    }

    public String put(final URI uri, final String payload) {
        assert uri != null;

        log.debug("Requesting(PUT): {}, payload: {}", uri, payload);
        ClientResponse response = resource(uri).put(ClientResponse.class, payload);
        return getContent(response);
    }

    public String delete(final URI uri) {
        assert uri != null;

        log.debug("Requesting(DELETE): {}", uri);
        ClientResponse response = resource(uri).delete(ClientResponse.class);
        return getContent(response);
    }

    public String request(final URI uri, final String method, final MediaType type, final MediaType accept, final String payload) {
        assert uri != null;
        assert method != null;
        assert type != null;
        assert accept != null;
        assert payload != null;

        log.debug("Requesting({}): {}", method, uri);
        ClientResponse response = resource(uri, type, accept).invoke(method, ClientResponse.class, payload);
        return getContent(response);
    }

    public UriBuilder baseUri() {
        return UriBuilder.fromUri(getBaseUri()).path("service").path(getInstance());
    }

    //
    // API Muck
    //

    public AuthenticationLoginResource login(final String username, final String password) {
        assert username != null;
        assert password != null;

        open(getBaseUri(), getInstance(), username, password);

        URI uri = baseUri().path("authentication/login").build();

        ClientResponse response = resource(uri).get(ClientResponse.class);

        // Get content first, to handle any errors
        String content = getContent(response);

        // Wink has no nice way to get cookies out of the response, so do it here the ugly way
        MultivaluedMap<String,String> headers = response.getHeaders();
        for (Map.Entry<String,List<String>> entry : headers.entrySet()) {
            if (HttpHeaders.SET_COOKIE.equals(entry.getKey())) {
                for (String raw : entry.getValue()) {
                    if (raw.contains(AUTH_COOKIE_NAME)) {
                        authCookie = NewCookie.valueOf(raw).toCookie();
                    }
                }
            }
        }

        if (authCookie == null) {
            log.warn("Missing session cookie in response");
        }

        return unmarshal(AuthenticationLoginResourceResponse.class, content).getData();
    }

    public boolean isAuthenticated() {
        return authCookie != null;
    }

    public void ensureAuthenticated() {
        if (!isAuthenticated()) {
            throw new IllegalStateException("Not authenticated");
        }

        ensureOpened();
    }
   
    public void logout() {
        ensureAuthenticated();

        URI uri = baseUri().path("authentication/logout").build();
        get(uri);
    }

    //
    // Extensions
    //
   
    @SuppressWarnings({"unchecked"})
    public <T extends Extension> T ext(final Class<T> type) {
        T ext = (T)extensions.get(type.getName());
        if (ext == null) {
            throw new IllegalArgumentException("Unknown client extension type: " + type);
        }
        return ext;
    }
}
TOP

Related Classes of com.sonatype.maven.shell.nexus.internal.NexusClientImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.