org.aludratest.hpalm.infrastructure.HpAlmSession.java Source code

Java tutorial

Introduction

Here is the source code for org.aludratest.hpalm.infrastructure.HpAlmSession.java

Source

/*
 * Copyright (C) 2015 Hamburg Sud and the contributors.
 *
 * 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.aludratest.hpalm.infrastructure;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.bind.JAXBException;

import org.aludratest.hpalm.entity.Entity;
import org.aludratest.hpalm.entity.EntityResultSet;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
import org.codehaus.plexus.util.xml.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HpAlmSession {

    private RestConnector connector;

    private static final Logger LOG = LoggerFactory.getLogger(HpAlmSession.class);

    private static final String LWSSO_COOKIE_KEY = "LWSSO_COOKIE_KEY";
    private static final String QCESSION_COOKIE_KEY = "QCSession";

    private static final Map<String, String> JSON_ACCEPT_HEADER = new HashMap<String, String>();
    private static final Map<String, String> XML_ACCEPT_HEADER = new HashMap<String, String>();
    private static final Map<String, String> XML_POST_HEADERS = new HashMap<String, String>();
    private static final Map<String, String> FILE_POST_HEADERS = new HashMap<String, String>();

    private static final Pattern PATTERN_LWSSO_REALM = Pattern.compile("LWSSO realm=\"(http(s?)://[^\"]+)\"");

    private boolean preVersion12;

    static {
        JSON_ACCEPT_HEADER.put("Accept", "application/json");
        XML_ACCEPT_HEADER.put("Accept", "application/xml");

        XML_POST_HEADERS.putAll(XML_ACCEPT_HEADER);
        XML_POST_HEADERS.put("Content-Type", "application/xml");

        FILE_POST_HEADERS.putAll(XML_ACCEPT_HEADER);
        FILE_POST_HEADERS.put("Content-Type", "application/octet-stream");
    }

    private HpAlmSession(RestConnector connector) {
        this.connector = connector;
    }

    public static HpAlmSession create(String serverUrl, String domain, String project, String userName,
            String password) throws IOException, HpAlmException {
        return create(serverUrl, domain, project, userName, password, 60000, 60000);
    }

    public static HpAlmSession create(String serverUrl, String domain, String project, String userName,
            String password, int connectTimeout, int requestTimeout) throws IOException, HpAlmException {
        RestConnector connector = new RestConnector(new HashMap<String, String>(), serverUrl, domain, project);
        connector.setConnectTimeout(connectTimeout);
        connector.setRequestTimeout(requestTimeout);

        // check HP ALM version using api/authentication/sign-in availability
        Response response = connector.httpGet(connector.buildUrl("api/authentication/sign-in"), null,
                XML_ACCEPT_HEADER);
        boolean preVersion12 = response.getStatusCode() == HttpStatus.SC_NOT_FOUND;

        // query is-authenticated
        response = connector.httpGet(connector.buildUrl("rest/is-authenticated"), null, XML_ACCEPT_HEADER);

        if (response.getStatusCode() != HttpStatus.SC_UNAUTHORIZED) {
            throw new HpAlmException(
                    "Unexpected HTTP status code during authentication: " + response.getStatusCode());
        }

        String authInfo = getSingleHeaderValue(response, "WWW-Authenticate");
        if (authInfo == null) {
            throw new HpAlmException("Server did not return authentication method information");
        }
        authInfo = authInfo.trim();
        Matcher m = PATTERN_LWSSO_REALM.matcher(authInfo);
        if (!m.matches()) {
            throw new HpAlmException("Server returned unsupported authentication realm: " + authInfo);
        }

        String authUrl = m.group(1) + "/alm-authenticate";

        // build authentication XML
        StringWriter sw = new StringWriter();
        XMLWriter writer = new PrettyPrintXMLWriter(sw);
        writer.startElement("alm-authentication");
        writer.startElement("user");
        writer.writeText(userName);
        writer.endElement();
        writer.startElement("password");
        writer.writeText(password);
        writer.endElement();
        writer.endElement();

        // send it via POST
        response = connector.httpPost(authUrl, sw.toString().getBytes("UTF-8"), XML_POST_HEADERS);
        if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
            throw new HpAlmException("Invalid user name or password");
        }

        // extract Cookie
        String cookie = getCookie(response, LWSSO_COOKIE_KEY);
        if (cookie == null) {
            throw new HpAlmException("Login response did not contain required session cookie");
        }

        connector.getCookies().put(LWSSO_COOKIE_KEY, cookie);

        // request a session with a custom limited timeout
        sw = new StringWriter();
        writer = new PrettyPrintXMLWriter(sw);
        writer.startElement("session-parameters");
        writer.startElement("time-out");
        writer.writeText("5");
        writer.endElement();
        writer.endElement();

        response = connector.httpPost(connector.buildUrl("rest/site-session"), sw.toString().getBytes("UTF-8"),
                XML_POST_HEADERS);
        if (response.getStatusCode() != HttpStatus.SC_OK && response.getStatusCode() != HttpStatus.SC_CREATED) {
            throw new HpAlmException("Could not start HP ALM Session for user " + userName);
        }

        cookie = getCookie(response, QCESSION_COOKIE_KEY);
        if (cookie == null) {
            throw new HpAlmException("Server did not send a session cookie");
        }

        connector.getCookies().put(QCESSION_COOKIE_KEY, cookie);

        HpAlmSession session = new HpAlmSession(connector);
        session.preVersion12 = preVersion12;
        return session;
    }

    /** Returns <code>true</code> if the HP ALM Server is a version lower than 12.0, <code>false</code> otherwise.
     * 
     * @return <code>true</code> if the HP ALM Server is a version lower than 12.0, <code>false</code> otherwise. */
    public boolean isPreVersion12() {
        return preVersion12;
    }

    public void extendTimeout() throws IOException {
        connector.httpGet(connector.buildUrl("rest/site-session"), null, XML_ACCEPT_HEADER);
    }

    public void logout() throws IOException {
        connector.httpGet(connector.buildUrl("authentication-point/logout"), null, XML_ACCEPT_HEADER);
    }

    // TODO provide better API to access fields information (parse XML into objects)
    public String getFieldsXml(String entityType, boolean required) throws IOException {
        Response response = connector.httpGet(
                connector.buildUrl("rest/domains/" + connector.domain + "/projects/" + connector.project
                        + "/customization/entities/" + entityType + "/fields"),
                "required=" + required, XML_ACCEPT_HEADER);
        return new String(response.getResponseData(), "UTF-8");
    }

    public ServerTime getServerTime() throws IOException, HpAlmException {
        Response response = connector.httpGet(connector.buildUrl("rest/server/time"), null, XML_ACCEPT_HEADER);
        if (response.getStatusCode() != HttpStatus.SC_OK) {
            raiseHpAlmException(response);
        }

        String xml = new String(response.getResponseData(), "UTF-8");
        try {
            return EntityMarshallingUtils.marshal(ServerTime.class, xml);
        } catch (JAXBException e) {
            throw new IOException("Could not unmarshal Server Time", e);
        }
    }

    public TimeZone determineServerTimeZone() throws IOException, HpAlmException {
        // get time values from server
        ServerTime serverTime = getServerTime();

        long millis = Long.parseLong(serverTime.getTimeInMillis());

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));

        long compare;
        try {
            compare = sdf.parse(serverTime.getDateTime()).getTime();
        } catch (ParseException pe) {
            throw new HpAlmException("HP ALM returned unparseable datetime string: " + serverTime.getDateTime());
        }
        long diff = compare - millis;
        int diffHours = (int) Math.round(diff / (1000l * 60 * 60.0));
        int diffMinutes = (int) Math.abs(Math.round((diff - (diffHours * 1000l * 60 * 60)) / (1000l * 60.0)));

        String determinedTimeZone = "" + diffMinutes;
        if (determinedTimeZone.length() == 1) {
            determinedTimeZone = "0" + determinedTimeZone;
        }
        determinedTimeZone = "GMT" + (diffHours < 0 ? "" : "+") + diffHours + ":" + determinedTimeZone;
        return TimeZone.getTimeZone(determinedTimeZone);
    }

    public Entity createEntity(Entity entity) throws IOException, HpAlmException {
        LOG.debug(
                "Creating entity of type " + entity.getType() + " with name " + entity.getStringFieldValue("name"));
        try {
            String xml = EntityMarshallingUtils.unmarshal(Entity.class, entity);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Full creation XML: " + xml);
            }
            Response response = connector.httpPost(connector.buildEntityCollectionUrl(entity.getType()),
                    xml.getBytes("UTF-8"), XML_POST_HEADERS);
            if (response.getStatusCode() != HttpStatus.SC_CREATED) {
                raiseHpAlmException(response);
            }
            xml = new String(response.getResponseData(), "UTF-8");
            Entity result = EntityMarshallingUtils.marshal(Entity.class, xml);
            LOG.debug("Created entity got ID " + result.getId());
            return result;
        } catch (JAXBException e) {
            throw new IOException("Could not unmarshal Entity", e);
        }
    }

    public Entity updateEntity(long id, Entity updateValues) throws IOException, HpAlmException {
        LOG.debug("Updating entity of type " + updateValues.getType() + " with ID " + id);
        try {
            String xml = EntityMarshallingUtils.unmarshal(Entity.class, updateValues);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Full update XML: " + xml);
            }
            Response response = connector.httpPut(
                    connector.buildEntityCollectionUrl(updateValues.getType()) + "/" + id, xml.getBytes("UTF-8"),
                    XML_POST_HEADERS);
            if (response.getStatusCode() != HttpStatus.SC_OK) {
                raiseHpAlmException(response);
            }
            xml = new String(response.getResponseData(), "UTF-8");
            return EntityMarshallingUtils.marshal(Entity.class, xml);
        } catch (JAXBException e) {
            throw new IOException("Could not unmarshal Entity", e);
        }
    }

    public void deleteEntity(String entityType, long id) throws IOException, HpAlmException {
        Response response = connector.httpDelete(connector.buildEntityCollectionUrl(entityType) + "/" + id,
                XML_ACCEPT_HEADER);
        if (response.getStatusCode() != HttpStatus.SC_OK) {
            raiseHpAlmException(response);
        }
    }

    public void deleteEntity(Entity entity) throws IOException, HpAlmException {
        deleteEntity(entity.getType(), entity.getId());
    }

    public Entity createAttachment(Entity entity, String fileName, InputStream attachmentData)
            throws IOException, HpAlmException {
        Map<String, String> headerMap = new LinkedHashMap<String, String>(FILE_POST_HEADERS);
        headerMap.put("Slug", fileName);

        // cache data
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        IOUtils.copy(attachmentData, baos);
        byte[] data = baos.toByteArray();

        String url = connector.buildEntityCollectionUrl(entity.getType());
        url += "/" + entity.getId() + "/attachments";

        Response response = connector.httpPost(url, data, headerMap);

        if (response.getStatusCode() != HttpStatus.SC_CREATED) {
            raiseHpAlmException(response);
        }

        String xml = new String(response.getResponseData(), "UTF-8");
        try {
            return EntityMarshallingUtils.marshal(Entity.class, xml);
        } catch (JAXBException e) {
            throw new HpAlmException("Invalid XML received after attachment creation", e);
        }
    }

    public EntityCollection getTestRuns() throws IOException, HpAlmException {
        return getEntityCollection("run", "test runs");
    }

    public EntityCollection getTests() throws IOException, HpAlmException {
        return getEntityCollection("test", "tests");
    }

    public EntityCollection getTestSets() throws IOException, HpAlmException {
        return getEntityCollection("test-set", "test sets");
    }

    public EntityCollection getTestFolders() throws IOException, HpAlmException {
        return getEntityCollection("test-folder", "test folders");
    }

    public EntityCollection getTestSetFolders() throws IOException, HpAlmException {
        return getEntityCollection("test-set-folder", "test set folders");
    }

    public Entity getTest(long id) throws IOException, HpAlmException {
        return getEntity("test", id);
    }

    public Entity getTestSet(long id) throws IOException, HpAlmException {
        return getEntity("test-set", id);
    }

    public Entity getTestFolder(long id) throws IOException, HpAlmException {
        return getEntity("test-folder", id);
    }

    public Entity getTestSetFolder(long id) throws IOException, HpAlmException {
        return getEntity("test-set-folder", id);
    }

    public EntityCollection queryEntities(String entityName, String query) throws IOException, HpAlmException {
        if (query != null) {
            query = encodeQuery(query);
        }

        String url = connector.buildEntityCollectionUrl(entityName)
                + (query != null ? ("?query={" + query + "}") : "");
        return new PagedEntityCollectionImpl(this, url, doGet(url));
    }

    public EntityCollection getAssetRelations(Entity entity) throws IOException, HpAlmException {
        String url = connector.buildEntityCollectionUrl(entity.getType()) + "/"
                + HpAlmUtil.DF_ID.format(entity.getId()) + "/asset-relations";
        return new PagedEntityCollectionImpl(this, url, doGet(url));
    }

    EntityResultSet doGet(String url) throws IOException, HpAlmException {
        Response response = connector.httpGet(url, null, XML_ACCEPT_HEADER);
        if (response.getStatusCode() != HttpStatus.SC_OK) {
            raiseHpAlmException(response);
        }

        try {
            // unmarshal response
            String xml = new String(response.getResponseData(), "UTF-8");
            return EntityMarshallingUtils.marshal(EntityResultSet.class, xml);
        } catch (JAXBException e) {
            throw new HpAlmException("Invalid XML format returned by HP ALM", e);
        }
    }

    //
    // Private helpers
    //

    private void raiseHpAlmException(Response response) throws HpAlmException {
        try {
            if (response.getResponseData() != null && response.getResponseData().length > 0) {
                // unmarshal response
                String xml = new String(response.getResponseData(), "UTF-8");
                QCRestException qcex = EntityMarshallingUtils.marshal(QCRestException.class, xml);

                if (qcex.getTitle() != null && qcex.getId() != null) {
                    throw new HpAlmException(qcex.getTitle(), qcex.getId());
                }
                if (qcex.getTitle() != null) {
                    throw new HpAlmException(qcex.getTitle());
                }
                if (qcex.getId() != null) {
                    throw new HpAlmException(qcex.getId());
                }
            }
        } catch (JAXBException e) { // NOPMD
            // ignore; fallthrough to general exception
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("No UTF-8 available here. Cannot continue.");
        }
        throw new HpAlmException(
                "Unexpected HP ALM status code, no further information: " + response.getStatusCode());
    }

    public Entity getEntity(String entityName, long id) throws IOException, HpAlmException {
        Response response = connector.httpGet(
                connector.buildEntityCollectionUrl(entityName) + "/" + HpAlmUtil.DF_ID.format(id), null,
                XML_ACCEPT_HEADER);
        if (response.getStatusCode() != HttpStatus.SC_OK) {
            raiseHpAlmException(response);
        }

        try {
            // unmarshal response
            String xml = new String(response.getResponseData(), "UTF-8");
            return EntityMarshallingUtils.marshal(Entity.class, xml);
        } catch (JAXBException e) {
            throw new HpAlmException("Invalid XML format in " + entityName, e);
        }
    }

    private EntityCollection getEntityCollection(String entityName, String entitiesDisplayName)
            throws IOException, HpAlmException {
        String url = connector.buildEntityCollectionUrl(entityName);
        EntityResultSet resultSet = doGet(url);
        return new PagedEntityCollectionImpl(this, url, resultSet);
    }

    private static String getSingleHeaderValue(Response response, String header) {
        Map<String, ? extends Iterable<String>> headers = response.getResponseHeaders();
        if (headers != null) {
            Iterable<String> values = headers.get(header);
            if (values != null) {
                Iterator<String> iter = values.iterator();
                if (iter.hasNext()) {
                    return iter.next();
                }
            }
        }

        return null;
    }

    private static String getCookie(Response response, String key) {
        Map<String, ? extends Iterable<String>> headers = response.getResponseHeaders();
        if (headers != null) {
            Iterable<String> values = headers.get("Set-Cookie");
            if (values != null) {
                for (String cookie : values) {
                    cookie = cookie.trim();
                    if (cookie.contains(";")) {
                        cookie = cookie.substring(0, cookie.indexOf(';')).trim();
                    }
                    if (cookie.contains("=")) {
                        String k = cookie.substring(0, cookie.indexOf('=')).trim();
                        if (key.equals(k)) {
                            return cookie.substring(cookie.indexOf('=') + 1).trim();
                        }
                    }
                }
            }
        }

        return null;
    }

    private static String encodeQuery(String query) {
        return new URLEncoder().encode(query);
    }

}