com.hp.alm.ali.idea.services.EntityService.java Source code

Java tutorial

Introduction

Here is the source code for com.hp.alm.ali.idea.services.EntityService.java

Source

/*
 * Copyright 2013 Hewlett-Packard Development Company, L.P
 *
 * 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 com.hp.alm.ali.idea.services;

import com.hp.alm.ali.rest.client.XMLOutputterFactory;
import com.hp.alm.ali.idea.entity.EntityCrossFilter;
import com.hp.alm.ali.idea.entity.EntityFilter;
import com.hp.alm.ali.idea.entity.EntityQuery;
import com.hp.alm.ali.idea.entity.EntityRef;
import com.hp.alm.ali.idea.model.Field;
import com.hp.alm.ali.idea.entity.CachingEntityListener;
import com.hp.alm.ali.idea.entity.EntityListener;
import com.hp.alm.ali.idea.model.Metadata;
import com.hp.alm.ali.idea.model.ServerStrategy;
import com.hp.alm.ali.idea.model.parser.DefectLinkList;
import com.hp.alm.ali.idea.rest.MyResultInfo;
import com.hp.alm.ali.idea.translate.TranslateService;
import com.hp.alm.ali.idea.model.Entity;
import com.hp.alm.ali.idea.model.parser.EntityList;
import com.hp.alm.ali.idea.rest.RestException;
import com.hp.alm.ali.idea.rest.RestService;
import com.hp.alm.ali.idea.util.ApplicationUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.lang.StringUtils;
import org.jdom.Document;

import javax.swing.SortOrder;
import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class EntityService {

    private Project project;
    private RestService restService;
    private MetadataService metadataService;
    private ErrorService errorService;
    private WeakListeners<EntityListener> listeners;
    final private Map<EntityRef, List<EntityListener>> asyncRequests;

    public EntityService(Project project, RestService restService, MetadataService metadataService,
            ErrorService errorService) {
        this.project = project;
        this.restService = restService;
        this.metadataService = metadataService;
        this.errorService = errorService;

        listeners = new WeakListeners<EntityListener>();
        asyncRequests = new HashMap<EntityRef, List<EntityListener>>();
    }

    public void addEntityListener(EntityListener listener) {
        listeners.add(listener);
    }

    public void removeEntityListener(EntityListener listener) {
        listeners.remove(listener);
    }

    public void refreshEntity(EntityRef ref) {
        getEntityAsync(ref, null, EntityListener.Event.REFRESH);
    }

    public void getEntityAsync(EntityRef ref, EntityListener callback) {
        getEntityAsync(ref, callback, EntityListener.Event.GET);
    }

    private void getEntityAsync(final EntityRef ref, final EntityListener callback,
            final EntityListener.Event event) {
        synchronized (asyncRequests) {
            List<EntityListener> list = asyncRequests.get(ref);
            if (list != null) {
                if (callback != null) {
                    list.add(callback);
                }
                return;
            }
            list = new LinkedList<EntityListener>();
            if (callback != null) {
                list.add(callback);
            }
            asyncRequests.put(ref, list);
        }
        ApplicationUtil.executeOnPooledThread(new Runnable() {
            public void run() {
                List<EntityListener> list;
                Entity entity;
                try {
                    entity = getEntity(ref);
                } catch (Exception e) {
                    synchronized (asyncRequests) {
                        list = asyncRequests.remove(ref);
                    }
                    for (EntityListener listener : list) {
                        listener.entityNotFound(ref, false);
                    }
                    fireEntityNotFound(ref, false);
                    return;
                }
                synchronized (asyncRequests) {
                    list = asyncRequests.remove(ref);
                }
                for (EntityListener listener : list) {
                    listener.entityLoaded(entity, event);
                }
                fireEntityLoaded(entity, event);
            }
        });
    }

    public Entity getEntity(EntityRef ref) {
        EntityQuery query = new EntityQuery(ref.type);
        Metadata metadata = metadataService.getEntityMetadata(ref.type);
        for (Field field : metadata.getAllFields().values()) {
            // list all fields to have compound fields fetched too
            query.addColumn(field.getName(), 1);
        }
        query.setValue("id", String.valueOf(ref.id));
        query.setPropertyResolved("id", true);
        EntityList list = doQuery(query, true);
        if (list.isEmpty()) {
            throw new RuntimeException("Entity not found: " + ref);
        } else {
            return list.get(0);
        }
    }

    private EntityList parse(InputStream is, boolean complete) {
        return EntityList.create(is, complete);
    }

    public EntityList query(EntityQuery query) {
        return doQuery(query, false);
    }

    public InputStream queryForStream(EntityQuery query) {
        EntityRef parent = query.getParent();
        if (parent == null) {
            return restService.getForStream("{0}s?{1}", query.getEntityType(), queryToString(query));
        } else {
            return restService.getForStream("{0}s/{1}/{2}s?{3}", parent.type, parent.id, query.getEntityType(),
                    queryToString(query));
        }
    }

    private EntityList doQuery(EntityQuery query, boolean complete) {
        InputStream is = queryForStream(query);
        if ("defect-link".equals(query.getEntityType())
                && restService.getServerStrategy().hasSecondLevelDefectLink()) {
            return DefectLinkList.create(is, complete);
        } else {
            return parse(is, complete);
        }
    }

    public Entity getDefectLink(int defectId, int linkId) {
        if (!restService.getServerStrategy().hasSecondLevelDefectLink()) {
            EntityQuery linkQuery = new EntityQuery("defect-link");
            linkQuery.setValue("id", String.valueOf(linkId));
            linkQuery.setPropertyResolved("id", true);
            return doQuery(linkQuery, true).get(0);
        } else {
            // in ALM 11 the two level query doesn't work correctly on the second level and ID must be specified in the path
            return DefectLinkList
                    .create(restService.getForStream("defects/{0}/defect-links/{1}", defectId, linkId), true)
                    .get(0);
        }
    }

    private String queryToString(EntityQuery query) {
        ServerStrategy cust = restService.getServerStrategy();
        EntityQuery clone = cust.preProcess(query.clone());
        StringBuffer buf = new StringBuffer();
        buf.append("fields=");
        LinkedHashMap<String, Integer> columns = clone.getColumns();
        buf.append(StringUtils.join(columns.keySet().toArray(new String[columns.size()]), ","));
        buf.append("&query=");
        buf.append(EntityQuery
                .encode("{" + filterToString(clone, project, project.getComponent(MetadataService.class)) + "}"));
        buf.append("&order-by=");
        buf.append(EntityQuery.encode("{" + orderToString(clone) + "}"));
        if (query.getPageSize() != null) {
            buf.append("&page-size=");
            buf.append(query.getPageSize());
        }
        if (query.getStartIndex() != null) {
            buf.append("&start-index=");
            buf.append(query.getStartIndex());
        }
        return buf.toString();
    }

    private String filterToString(EntityFilter<? extends EntityFilter> filter, String prefix, Project project,
            MetadataService metadataService) {
        StringBuffer buf = new StringBuffer();
        for (String prop : filter.getPropertyMap().keySet()) {
            String val = filter.getPropertyMap().get(prop);
            if ("".equals(val)) {
                continue;
            }

            if (buf.length() > 0) {
                buf.append("; ");
            }
            if (prefix != null) {
                buf.append(prefix).append(".");
            }
            buf.append(prop);
            buf.append("[");

            if (!filter.isResolved(prop)) {
                Field field = metadataService.getEntityMetadata(filter.getEntityType()).getField(prop);
                val = project.getComponent(TranslateService.class).convertQueryModelToREST(field, val);
            }

            buf.append(val);
            buf.append("]");
        }
        return buf.toString();
    }

    private String filterToString(EntityQuery query, Project project, MetadataService metadataService) {
        StringBuffer buf = new StringBuffer();
        buf.append(filterToString(query, null, project, metadataService));
        Map<String, EntityCrossFilter> crossFilters = query.getCrossFilters();
        for (String alias : crossFilters.keySet()) {
            EntityCrossFilter cf = crossFilters.get(alias);
            if (buf.length() > 0) {
                buf.append("; ");
            }
            buf.append(filterToString(cf, alias, project, metadataService));

            if (!cf.isInclusive()) {
                buf.append("; ").append(cf.getEntityType()).append(".inclusive-filter[false]");
            }
        }
        return buf.toString();
    }

    private String orderToString(EntityQuery query) {
        StringBuffer buf = new StringBuffer();
        LinkedHashMap<String, SortOrder> order = query.getOrder();
        for (String name : order.keySet()) {
            if (buf.length() > 0) {
                buf.append("; ");
            }
            buf.append(name);
            buf.append("[");
            buf.append(order.get(name) == SortOrder.ASCENDING ? "ASC" : "DESC");
            buf.append("]");
        }
        return buf.toString();
    }

    private Entity updateOldDefectLink(Entity entity, boolean silent, boolean fireUpdate) {
        String xml = XMLOutputterFactory.getXMLOutputter()
                .outputString(new Document(DefectLinkList.linkToXml(entity)));
        MyResultInfo result = new MyResultInfo();
        if (restService.put(xml, result, "defects/{0}/defect-links/{1}",
                entity.getPropertyValue("first-endpoint-id"), entity.getId()) != HttpStatus.SC_OK) {
            if (!silent) {
                errorService.showException(new RestException(result));
            }
            return null;
        } else {
            Entity resultEntity = DefectLinkList.create(result.getBodyAsStream()).get(0);
            if (fireUpdate) {
                fireEntityLoaded(resultEntity, EntityListener.Event.GET);
            }
            return resultEntity;
        }
    }

    public Entity updateEntity(Entity entity, Set<String> fieldsToUpdate, boolean silent) {
        return updateEntity(entity, fieldsToUpdate, silent, false, true);
    }

    public Entity updateEntity(Entity entity, Set<String> fieldsToUpdate, boolean silent, boolean reloadOnFailure) {
        return updateEntity(entity, fieldsToUpdate, silent, reloadOnFailure, true);
    }

    public Entity updateEntity(Entity entity, Set<String> fieldsToUpdate, boolean silent, boolean reloadOnFailure,
            boolean fireUpdate) {
        if ("defect-link".equals(entity.getType()) && restService.getServerStrategy().hasSecondLevelDefectLink()) {
            return updateOldDefectLink(entity, silent, fireUpdate);
        }
        String xml = XMLOutputterFactory.getXMLOutputter()
                .outputString(new Document(entity.toElement(fieldsToUpdate)));
        MyResultInfo result = new MyResultInfo();
        if (restService.put(xml, result, "{0}s/{1}", entity.getType(), entity.getId()) != HttpStatus.SC_OK) {
            if (!silent) {
                errorService.showException(new RestException(result));
            }
            if (reloadOnFailure) {
                try {
                    return getEntity(new EntityRef(entity));
                } catch (Exception e) {
                    // do not report another failure if reload fails
                }
            }
            return null;
        } else {
            if (fireUpdate) {
                return parseEntityAndFireEvent(result.getBodyAsStream(), EntityListener.Event.GET);
            } else {
                return parse(result.getBodyAsStream(), true).get(0);
            }
        }
    }

    private Entity createOldDefectLink(Entity entity, boolean silent) {
        String xml = XMLOutputterFactory.getXMLOutputter()
                .outputString(new Document(DefectLinkList.linkToXml(entity)));
        MyResultInfo result = new MyResultInfo();
        if (restService.post(xml, result, "defects/{0}/defect-links",
                entity.getPropertyValue("first-endpoint-id")) != HttpStatus.SC_CREATED) {
            if (!silent) {
                errorService.showException(new RestException(result));
            }
            return null;
        } else {
            Entity resultEntity = DefectLinkList.create(result.getBodyAsStream()).get(0);
            fireEntityLoaded(resultEntity, EntityListener.Event.CREATE);
            return resultEntity;
        }
    }

    public Entity createEntity(Entity entity, boolean silent) {
        if ("defect-link".equals(entity.getType()) && restService.getServerStrategy().hasSecondLevelDefectLink()) {
            return createOldDefectLink(entity, silent);
        }
        String xml = XMLOutputterFactory.getXMLOutputter().outputString(new Document(entity.toElement(null)));
        MyResultInfo result = new MyResultInfo();
        if (restService.post(xml, result, "{0}s", entity.getType()) != HttpStatus.SC_CREATED) {
            if (!silent) {
                errorService.showException(new RestException(result));
            }
            return null;
        } else {
            return parseEntityAndFireEvent(result.getBodyAsStream(), EntityListener.Event.CREATE);
        }
    }

    private Entity parseEntityAndFireEvent(InputStream is, EntityListener.Event event) {
        Entity resultEntity = parse(is, true).get(0);
        fireEntityLoaded(resultEntity, event);
        return resultEntity;
    }

    public Entity lockEntity(Entity entity, boolean silent) {
        Entity locked = doLock(new EntityRef(entity), silent);
        if (locked != null) {
            if (!locked.matches(entity)) {
                if (!silent) {
                    Messages.showDialog(
                            "Item has been recently modified on the server. Local values have been updated to match the up-to-date revision.",
                            "Entity Update", new String[] { "Continue" }, 0, Messages.getInformationIcon());
                }
                fireEntityLoaded(locked, EntityListener.Event.GET);
            }
        }
        return locked;
    }

    private boolean deleteOldDefectLink(Entity entity) {
        MyResultInfo result = new MyResultInfo();
        if (restService.delete(result, "defects/{0}/defect-links/{1}", entity.getPropertyValue("first-endpoint-id"),
                entity.getId()) != HttpStatus.SC_OK) {
            errorService.showException(new RestException(result));
            return false;
        } else {
            fireEntityNotFound(new EntityRef(entity), true);
            return true;
        }
    }

    public boolean deleteEntity(Entity entity) {
        if ("defect-link".equals(entity.getType()) && restService.getServerStrategy().hasSecondLevelDefectLink()) {
            return deleteOldDefectLink(entity);
        }
        MyResultInfo result = new MyResultInfo();
        if (restService.delete(result, "{0}s/{1}", entity.getType(), entity.getId()) != HttpStatus.SC_OK) {
            errorService.showException(new RestException(result));
            return false;
        } else {
            fireEntityNotFound(new EntityRef(entity), true);
            return true;
        }
    }

    private Entity doLock(EntityRef ref, boolean silent) {
        MyResultInfo result = new MyResultInfo();
        int httpStatus = restService.post("", result, "{0}s/{1}/lock", ref.type, ref.id);
        if (httpStatus != HttpStatus.SC_OK && httpStatus != HttpStatus.SC_CREATED) {
            if (!silent) {
                errorService.showException(new RestException(result));
            }
            return null;
        }
        return parse(result.getBodyAsStream(), true).get(0);
    }

    public void unlockEntity(Entity entity) {
        restService.delete("{0}s/{1}/lock", entity.getType(), String.valueOf(entity.getId()));
    }

    public void fireEntityLoaded(final Entity entity, final EntityListener.Event event) {
        listeners.fire(new WeakListeners.Action<EntityListener>() {
            public void fire(EntityListener listener) {
                listener.entityLoaded(entity, event);
            }
        });
    }

    public void fireEntityNotFound(final EntityRef ref, final boolean removed) {
        listeners.fire(new WeakListeners.Action<EntityListener>() {
            public void fire(EntityListener listener) {
                listener.entityNotFound(ref, removed);
            }
        });
    }

    public void requestCachedEntity(final EntityRef ref, final List<String> properties,
            final EntityListener callback) {
        ApplicationUtil.executeOnPooledThread(new Runnable() {
            public void run() {
                final LinkedList<Entity> done = new LinkedList<Entity>();
                listeners.fire(new WeakListeners.Action<EntityListener>() {
                    public void fire(EntityListener listener) {
                        if (done.isEmpty() && listener instanceof CachingEntityListener) {
                            Entity cached = ((CachingEntityListener) listener).lookup(ref);
                            if (cached != null) {
                                for (String property : properties) {
                                    if (!cached.isInitialized(property)) {
                                        return;
                                    }
                                }
                                done.add(cached);
                            }
                        }
                    }
                });
                if (done.isEmpty()) {
                    // all properties are fetched. possible optimization is to request only properties from the
                    // current request + properties initialized in cached value (if any)
                    getEntityAsync(ref, callback);
                } else {
                    callback.entityLoaded(done.getFirst(), EntityListener.Event.CACHE);
                }
            }
        });
    }

}