All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redmine.ta.internal.Transport Maven / Gradle / Ivy

Go to download

Free open-source Java API for Redmine and Chiliproject bug/task management systems.

The newest version!
package org.redmine.ta.internal;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
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.entity.AbstractHttpEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONException;
import org.json.JSONObject;
import org.redmine.ta.RedmineOptions;
import org.redmine.ta.NotFoundException;
import org.redmine.ta.RedmineAuthenticationException;
import org.redmine.ta.RedmineException;
import org.redmine.ta.RedmineFormatException;
import org.redmine.ta.RedmineInternalError;
import org.redmine.ta.RedmineManager;
import org.redmine.ta.beans.Identifiable;
import org.redmine.ta.beans.Issue;
import org.redmine.ta.beans.IssueCategory;
import org.redmine.ta.beans.IssueRelation;
import org.redmine.ta.beans.IssueStatus;
import org.redmine.ta.beans.Membership;
import org.redmine.ta.beans.News;
import org.redmine.ta.beans.Project;
import org.redmine.ta.beans.Role;
import org.redmine.ta.beans.SavedQuery;
import org.redmine.ta.beans.TimeEntry;
import org.redmine.ta.beans.Tracker;
import org.redmine.ta.beans.User;
import org.redmine.ta.beans.Version;
import org.redmine.ta.internal.comm.BaseCommunicator;
import org.redmine.ta.internal.comm.BasicHttpResponse;
import org.redmine.ta.internal.comm.Communicator;
import org.redmine.ta.internal.comm.Communicators;
import org.redmine.ta.internal.comm.ContentHandler;
import org.redmine.ta.internal.comm.SimpleCommunicator;
import org.redmine.ta.internal.comm.redmine.RedmineAuthenticator;
import org.redmine.ta.internal.comm.redmine.RedmineErrorHandler;
import org.redmine.ta.internal.json.JsonInput;
import org.redmine.ta.internal.json.JsonObjectParser;
import org.redmine.ta.internal.json.JsonObjectWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Redmine transport utilities.
 * 
 * @author maxkar
 * 
 */
public final class Transport {
	private static final Map, EntityConfig> OBJECT_CONFIGS = new HashMap, EntityConfig>();
	private static final String CONTENT_TYPE = "application/json; charset=utf-8";
	private static final int DEFAULT_OBJECTS_PER_PAGE = 25;
	private static final String KEY_TOTAL_COUNT = "total_count";
	private final Logger logger = LoggerFactory.getLogger(RedmineManager.class);
	private SimpleCommunicator communicator;
	private final Communicator errorCheckingCommunicator;
	private final BaseCommunicator baseCommunicator;
	private final RedmineAuthenticator authenticator;
	private final Communicator coreCommunicator;

	static {
		OBJECT_CONFIGS.put(
				Project.class,
				config("project", "projects",
						RedmineJSONBuilder.PROJECT_WRITER,
						RedmineJSONParser.PROJECT_PARSER));
		OBJECT_CONFIGS.put(
				Issue.class,
				config("issue", "issues", RedmineJSONBuilder.ISSUE_WRITER,
						RedmineJSONParser.ISSUE_PARSER));
		OBJECT_CONFIGS.put(
				User.class,
				config("user", "users", RedmineJSONBuilder.USER_WRITER,
						RedmineJSONParser.USER_PARSER));
		OBJECT_CONFIGS.put(
				IssueCategory.class,
				config("issue_category", "issue_categories",
						RedmineJSONBuilder.CATEGORY_WRITER,
						RedmineJSONParser.CATEGORY_PARSER));
		OBJECT_CONFIGS.put(
				Version.class,
				config("version", "versions",
						RedmineJSONBuilder.VERSION_WRITER,
						RedmineJSONParser.VERSION_PARSER));
		OBJECT_CONFIGS.put(
				TimeEntry.class,
				config("time_entry", "time_entries",
						RedmineJSONBuilder.TIME_ENTRY_WRITER,
						RedmineJSONParser.TIME_ENTRY_PARSER));
		OBJECT_CONFIGS.put(News.class,
				config("news", "news", null, RedmineJSONParser.NEWS_PARSER));
		OBJECT_CONFIGS.put(
				IssueRelation.class,
				config("relation", "relations",
						RedmineJSONBuilder.RELATION_WRITER,
						RedmineJSONParser.RELATION_PARSER));
		OBJECT_CONFIGS.put(
				Tracker.class,
				config("tracker", "trackers", null,
						RedmineJSONParser.TRACKER_PARSER));
		OBJECT_CONFIGS.put(
				IssueStatus.class,
				config("status", "issue_statuses", null,
						RedmineJSONParser.STATUS_PARSER));
		OBJECT_CONFIGS
				.put(SavedQuery.class,
						config("query", "queries", null,
								RedmineJSONParser.QUERY_PARSER));
		OBJECT_CONFIGS.put(Role.class,
				config("role", "roles", null, RedmineJSONParser.ROLE_PARSER));
		OBJECT_CONFIGS.put(
				Membership.class,
				config("membership", "memberships",
						RedmineJSONBuilder.MEMBERSHIP_WRITER,
						RedmineJSONParser.MEMBERSHIP_PARSER));
	}

	private final URIConfigurator configurator;
	private String login;
	private String password;
	private int objectsPerPage = DEFAULT_OBJECTS_PER_PAGE;
	private static final String CHARSET = "UTF-8";

	public Transport(URIConfigurator configurator, RedmineOptions options) {
		this.configurator = configurator;
		this.baseCommunicator = new BaseCommunicator(options);
		this.authenticator = new RedmineAuthenticator(
				baseCommunicator, CHARSET);
		final ContentHandler errorProcessor = new RedmineErrorHandler();
		errorCheckingCommunicator = Communicators.fmap(
				authenticator,
				Communicators.compose(errorProcessor,
						Communicators.transportDecoder()));
		coreCommunicator = Communicators.fmap(errorCheckingCommunicator,
				Communicators.contentReader());
		this.communicator = Communicators.simplify(coreCommunicator,
				Communicators. identityHandler());
	}

	public User getCurrentUser(NameValuePair... params) throws RedmineException {
		URI uri = getURIConfigurator().createURI("users/current.json", params);
		HttpGet http = new HttpGet(uri);
		String response = getCommunicator().sendRequest(http);
		return parseResponse(response, "user", RedmineJSONParser.USER_PARSER);
	}

	/**
	 * Performs an "add object" request.
	 * 
	 * @param object
	 *            object to use.
	 * @param params
	 *            name params.
	 * @return object to use.
	 * @throws RedmineException
	 *             if something goes wrong.
	 */
	public  T addObject(T object, NameValuePair... params)
			throws RedmineException {
		final EntityConfig config = getConfig(object.getClass());
		URI uri = getURIConfigurator().getObjectsURI(object.getClass(), params);
		HttpPost httpPost = new HttpPost(uri);
		String body = RedmineJSONBuilder.toSimpleJSON(config.singleObjectName,
				object, config.writer);
		setEntity(httpPost, body);
		String response = getCommunicator().sendRequest(httpPost);
		logger.debug(response);
		return parseResponse(response, config.singleObjectName, config.parser);
	}

	/**
	 * Performs an "add child object" request.
	 * 
	 * @param parentClass
	 *            parent object id.
	 * @param object
	 *            object to use.
	 * @param params
	 *            name params.
	 * @return object to use.
	 * @throws RedmineException
	 *             if something goes wrong.
	 */
	public  T addChildEntry(Class parentClass, String parentId, T object,
			NameValuePair... params) throws RedmineException {
		final EntityConfig config = getConfig(object.getClass());
		URI uri = getURIConfigurator().getChildObjectsURI(parentClass,
				parentId, object.getClass(), params);
		HttpPost httpPost = new HttpPost(uri);
		String body = RedmineJSONBuilder.toSimpleJSON(config.singleObjectName,
				object, config.writer);
		setEntity(httpPost, body);
		String response = getCommunicator().sendRequest(httpPost);
		logger.debug(response);
		return parseResponse(response, config.singleObjectName, config.parser);
	}

	/*
	 * note: This method cannot return the updated object from Redmine because
	 * the server does not provide any XML in response.
	 * 
	 * @since 1.8.0
	 */
	public  void updateObject(T obj,
			NameValuePair... params) throws RedmineException {
		final EntityConfig config = getConfig(obj.getClass());
		final URI uri = getURIConfigurator().getObjectURI(obj.getClass(),
				Integer.toString(obj.getId()));
		final HttpPut http = new HttpPut(uri);

		final String body = RedmineJSONBuilder.toSimpleJSON(
				config.singleObjectName, obj, config.writer);
		setEntity(http, body);

		getCommunicator().sendRequest(http);
	}

	/**
	 * Deletes an object.
	 * 
	 * @param classs
	 *            object class.
	 * @param id
	 *            object id.
	 * @throws RedmineException
	 *             if something goes wrong.
	 */
	public  void deleteObject(Class classs, String id)
			throws RedmineException {
		final URI uri = getURIConfigurator().getObjectURI(classs, id);
		final HttpDelete http = new HttpDelete(uri);
		getCommunicator().sendRequest(http);
	}

	/**
	 * @param classs
	 *            target class
	 * @param key
	 *            item key
	 * @param args
	 *            extra arguments.
	 * @throws RedmineAuthenticationException
	 *             invalid or no API access key is used with the server, which
	 *             requires authorization. Check the constructor arguments.
	 * @throws NotFoundException
	 *             the object with the given key is not found
	 * @throws RedmineException
	 */
	public  T getObject(Class classs, String key, NameValuePair... args)
			throws RedmineException {
		final EntityConfig config = getConfig(classs);
		final URI uri = getURIConfigurator().getObjectURI(classs, key, args);
		final HttpGet http = new HttpGet(uri);
		String response = getCommunicator().sendRequest(http);
		logger.debug(response);
		return parseResponse(response, config.singleObjectName, config.parser);
	}

	/**
	 * Downloads a redmine content.
	 * 
	 * @param uri
	 *            target uri.
	 * @param handler
	 *            content handler.
	 * @return handler result.
	 * @throws RedmineException
	 *             if something goes wrong.
	 */
	public  R download(String uri,
			ContentHandler handler)
			throws RedmineException {
		final HttpGet request = new HttpGet(uri);
		return errorCheckingCommunicator.sendRequest(request, handler);
	}

	/**
	 * UPloads content on a server.
	 * 
	 * @param content
	 *            content stream.
	 * @return uploaded item token.
	 * @throws RedmineException
	 *             if something goes wrong.
	 */
	public String upload(InputStream content) throws RedmineException {
		final URI uploadURI = getURIConfigurator().getUploadURI();
		final HttpPost request = new HttpPost(uploadURI);
		final AbstractHttpEntity entity = new InputStreamEntity(content, -1);
		/* Content type required by a Redmine */
		entity.setContentType("application/octet-stream");
		request.setEntity(entity);

		final String result = getCommunicator().sendRequest(request);
		return parseResponse(result, "upload",
            RedmineJSONParser.UPLOAD_TOKEN_PARSER);
	}

	/**
	 * @param classs
	 *            target class
	 * @param key
	 *            item key
	 * @param args
	 *            extra arguments.
	 * @throws RedmineAuthenticationException
	 *             invalid or no API access key is used with the server, which
	 *             requires authorization. Check the constructor arguments.
	 * @throws NotFoundException
	 *             the object with the given key is not found
	 * @throws RedmineException
	 */
	public  T getObject(Class classs, Integer key, NameValuePair... args)
			throws RedmineException {
		return getObject(classs, key.toString(), args);
	}

	public  List getObjectsList(Class objectClass,
			NameValuePair... params) throws RedmineException {
		return getObjectsList(objectClass, Arrays.asList(params));
	}

	/**
	 * Returns an object list.
	 * 
	 * @return objects list, never NULL
	 */
	public  List getObjectsList(Class objectClass,
			Collection params) throws RedmineException {
		final EntityConfig config = getConfig(objectClass);
		final List result = new ArrayList();

		final List newParams = new ArrayList(
				params);

		newParams.add(new BasicNameValuePair("limit", String
				.valueOf(objectsPerPage)));
		int offset = 0;

		int totalObjectsFoundOnServer;
		do {
			List paramsList = new ArrayList(
					newParams);
			paramsList.add(new BasicNameValuePair("offset", String
					.valueOf(offset)));

			final URI uri = getURIConfigurator().getObjectsURI(objectClass,
					paramsList);

			logger.debug(uri.toString());
			final HttpGet http = new HttpGet(uri);

			final String response = getCommunicator().sendRequest(http);
			logger.debug("received: " + response);

			final List foundItems;
			try {
				final JSONObject responseObject = RedmineJSONParser
						.getResponse(response);
				foundItems = JsonInput.getListOrNull(responseObject,
						config.multiObjectName, config.parser);
				result.addAll(foundItems);

				/* Necessary for trackers */
				if (!responseObject.has(KEY_TOTAL_COUNT)) {
					break;
				}
				totalObjectsFoundOnServer = JsonInput.getInt(responseObject,
						KEY_TOTAL_COUNT);
			} catch (JSONException e) {
				throw new RedmineFormatException(e);
			}

			if (foundItems.size() == 0) {
				break;
			}

			offset += foundItems.size();
		} while (offset < totalObjectsFoundOnServer);

		return result;
	}

	/**
	 * This number of objects (tasks, projects, users) will be requested from
	 * Redmine server in 1 request.
	 */
	public int getObjectsPerPage() {
		return objectsPerPage;
	}

	/**
	 * Delivers a list of a child entries.
	 * 
	 * @param classs
	 *            target class.
	 */
	public  List getChildEntries(Class parentClass, String parentId,
			Class classs) throws RedmineException {
		final EntityConfig config = getConfig(classs);
		final URI uri = getURIConfigurator().getChildObjectsURI(parentClass,
				parentId, classs);

		HttpGet http = new HttpGet(uri);
		String response = getCommunicator().sendRequest(http);
		final JSONObject responseObject;
		try {
			responseObject = RedmineJSONParser.getResponse(response);
			return JsonInput.getListNotNull(responseObject,
					config.multiObjectName, config.parser);
		} catch (JSONException e) {
			throw new RedmineFormatException("Bad categories response " + response, e);
		}
	}

	/**
	 * This number of objects (tasks, projects, users) will be requested from
	 * Redmine server in 1 request.
	 */
	public void setObjectsPerPage(int pageSize) {
		if (pageSize <= 0) {
			throw new IllegalArgumentException(
					"Page size must be >= 0. You provided: " + pageSize);
		}
		this.objectsPerPage = pageSize;
	}

	private SimpleCommunicator getCommunicator()
			throws RedmineException {
		return communicator;
	}

	private static  T parseResponse(String response, String tag,
                                     JsonObjectParser parser) throws RedmineFormatException {
		try {
			return parser.parse(RedmineJSONParser.getResponseSingleObject(response, tag));
		} catch (JSONException e) {
			throw new RedmineFormatException(e);
		}
	}

	private void setEntity(HttpEntityEnclosingRequest request, String body) {
		StringEntity entity;
		try {
			entity = new StringEntity(body, CHARSET);
		} catch (UnsupportedEncodingException e) {
			throw new RedmineInternalError("Required charset " + CHARSET
					+ " is not supported", e);
		}
		entity.setContentType(CONTENT_TYPE);
		request.setEntity(entity);
	}

	@SuppressWarnings("unchecked")
	private  EntityConfig getConfig(Class class1) {
		final EntityConfig guess = OBJECT_CONFIGS.get(class1);
		if (guess == null)
			throw new RedmineInternalError("Unsupported class " + class1);
		return (EntityConfig) guess;
	}

	private URIConfigurator getURIConfigurator() {
		return configurator;
	}

	private static  EntityConfig config(String objectField,
			String urlPrefix, JsonObjectWriter writer,
			JsonObjectParser parser) {
		return new EntityConfig(objectField, urlPrefix, writer, parser);
	}

	public void setCredentials(String login, String password) {
		this.login = login;
		this.password = password;
		authenticator.setCredentials(login, password);
	}

	public void shutdown() {
		baseCommunicator.shutdown();
	}

	public void setPassword(String password) {
		setCredentials(login, password);
	}

	public void setLogin(String login) {
		setCredentials(login, password);
	}

	/**
	 * Entity config.
	 */
	static class EntityConfig {
		final String singleObjectName;
		final String multiObjectName;
		final JsonObjectWriter writer;
		final JsonObjectParser parser;

		public EntityConfig(String objectField, String urlPrefix,
				JsonObjectWriter writer, JsonObjectParser parser) {
			super();
			this.singleObjectName = objectField;
			this.multiObjectName = urlPrefix;
			this.writer = writer;
			this.parser = parser;
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy