Java tutorial
/* * 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); } } }); } }