diff --git a/oslcjira/.checkstyle b/oslcjira/.checkstyle
new file mode 100644
index 0000000..75246d3
--- /dev/null
+++ b/oslcjira/.checkstyle
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/oslcjira/.classpath b/oslcjira/.classpath
new file mode 100644
index 0000000..eca4bc2
--- /dev/null
+++ b/oslcjira/.classpath
@@ -0,0 +1,278 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/oslcjira/.project b/oslcjira/.project
new file mode 100644
index 0000000..14cf940
--- /dev/null
+++ b/oslcjira/.project
@@ -0,0 +1,18 @@
+
+
+ oslcjira
+ This is the OSLC plugin for Atlassian JIRA. NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse.
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
+
diff --git a/oslcjira/LICENSE b/oslcjira/LICENSE
new file mode 100644
index 0000000..72e1d67
--- /dev/null
+++ b/oslcjira/LICENSE
@@ -0,0 +1,23 @@
+
+Copyright (c) 2015, Ericsson AB. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/oslcjira/NOTICE b/oslcjira/NOTICE
new file mode 100644
index 0000000..bd24f77
--- /dev/null
+++ b/oslcjira/NOTICE
@@ -0,0 +1,35 @@
+Ericsson Jira OSLC Plugin
+Copyright (C) 2015 Ericsson AB.
+
+This product includes software developed at
+Ericsson AB. (www.ericsson.com).
+
+This product includes code available under a
+Eclipse Distribution License v. 1.0
+
+Copyright (c) 2011-2013 IBM Corporation.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+* Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/oslcjira/pom.xml b/oslcjira/pom.xml
new file mode 100644
index 0000000..accd96e
--- /dev/null
+++ b/oslcjira/pom.xml
@@ -0,0 +1,235 @@
+
+
+
+ 4.0.0
+ com.ericsson.jira.oslc
+ oslcjira
+ 1.0
+
+ Ericsson
+ http://www.ericsson.se
+
+
+
+ The BSD 2-Clause License
+ https://opensource.org/licenses/BSD-2-Clause
+ repo
+
+
+
+ JIRA OSLC Provider
+ This is the OSLC plugin for Atlassian JIRA.
+ atlassian-plugin
+
+
+ lyo-releases
+ https://repo.eclipse.org/content/repositories/lyo-releases/
+
+
+
+
+
+
+
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+ 3.0.1
+ provided
+
+
+ com.google.code.gson
+ gson
+ 2.2.4
+
+
+ org.apache.commons
+ commons-lang3
+ 3.1
+
+
+ javax.ws.rs
+ jsr311-api
+ 1.1.1
+ provided
+
+
+ org.slf4j
+ slf4j-api
+ 1.6.6
+ provided
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.1
+ provided
+
+
+
+ com.atlassian.jira
+ jira-api
+ ${jira.version}
+ provided
+
+
+ com.atlassian.jira
+ jira-core
+ ${jira.version}
+ provided
+
+
+ com.atlassian.plugins.rest
+ atlassian-rest-common
+ 1.0.2
+ provided
+
+
+ com.atlassian.sal
+ sal-api
+ 2.6.0
+ provided
+
+
+ com.atlassian.templaterenderer
+ atlassian-template-renderer-api
+ 1.3.1
+ provided
+
+
+ org.apache.httpcomponents
+ httpcore
+ 4.1.1
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.1.1
+
+
+ com.atlassian.activeobjects
+ activeobjects-plugin
+ 0.23.8
+ provided
+
+
+
+ org.eclipse.lyo.oslc4j.core
+ oslc4j-core
+ 2.1.0
+
+
+ org.eclipse.lyo.oslc4j.core
+ oslc4j-json4j-provider
+ 2.1.0
+
+
+ org.eclipse.lyo.oslc4j.core
+ oslc4j-jena-provider
+ 2.1.0
+
+
+ xml-apis
+ xml-apis
+
+
+
+
+ org.eclipse.lyo.core.query
+ oslc-query
+ [1.1,)
+
+
+ org.eclipse.lyo.server
+ oauth-core
+ 2.1.0
+
+
+ org.eclipse.lyo.server
+ oauth-consumer-store
+ 2.1.0
+
+
+
+
+
+
+
+
+
+ com.atlassian.maven.plugins
+ maven-jira-plugin
+ ${amps.version}
+ true
+
+ ${jira.version}
+ ${jira.version}
+
+
+
+
+
+ maven-compiler-plugin
+
+ 1.6
+ 1.6
+
+
+
+ ${buildDirectory}
+
+
+ ${project.basedir}/target
+ 6.3.6
+ 5.0.4
+ 1.2.0
+
+ 5.2.26
+
+
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/SyncConfigLoader.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/SyncConfigLoader.java
new file mode 100644
index 0000000..6d56784
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/SyncConfigLoader.java
@@ -0,0 +1,841 @@
+package com.ericsson.eif.leansync.mapping;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.xpath.jaxp.XPathFactoryImpl;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSOutput;
+import org.w3c.dom.ls.LSSerializer;
+
+import com.ericsson.eif.leansync.mapping.data.ActionType;
+import com.ericsson.eif.leansync.mapping.data.SyncConfiguration;
+import com.ericsson.eif.leansync.mapping.data.SyncField;
+import com.ericsson.eif.leansync.mapping.data.SyncGeneralField;
+import com.ericsson.eif.leansync.mapping.data.SyncMapping;
+import com.ericsson.eif.leansync.mapping.data.SyncTemplate;
+import com.ericsson.eif.leansync.mapping.data.SyncXmlFieldConfig;
+import com.ericsson.eif.leansync.mapping.exceptions.SyncConfigurationException;
+
+/**
+ * A class which servers for loading LeanSync configuration from xml
+ *
+ */
+public class SyncConfigLoader {
+ private final String CONFIG_NODE_CONNECTION = "connection";
+ private final String CONFIG_ATTR_NS = "ns";
+ private final String CONFIG_ATTR_NAME= "name";
+ private final String CONFIG_ATTR_ID = "id";
+ private final String CONFIG_ATTR_XPATH = "xpath";
+ private final String CONFIG_ATTR_FIELD_TYPE = "fieldType";
+ private final String CONFIG_ATTR_CONTENT_TYPE = "contentType";
+ private final String CONFIG_ATTR_MAP_TO = "mapTo";
+ private final String CONFIG_ATTR_NOTIFY_CHANGE = "notifyChange";
+ private final String CONFIG_ATTR_ACTION = "action";
+ private final String CONFIG_ATTR_KEEP_TAGS = "keepTags";
+ private final String CONFIG_ATTR_ENCODE_HTML = "encodeHtml";
+ private final String CONFIG_ATTR_TO_DATE_FORMAT = "toDateFormat";
+ private final String CONFIG_ATTR_FROM_DATE_FORMAT = "fromDateFormat";
+ private final String CONFIG_ATTR_USERNAME = "username";
+ private final String CONFIG_ATTR_PASSWORD = "password";
+ private final String CONFIG_NODE_CONFIG_FULL = "//configurations/configuration";
+ private final String CONFIG_NODE_PROJECT_REL = "projects/project";
+ private final String CONFIG_NODE_HEADER_REL = "headers/header";
+ private final String CONFIG_NODE_DOMAIN_REL = "domains/domain";
+ private final String CONFIG_NODE_RDFTYPE_REL = "rdfTypes/rdfType";
+ private final String CONFIG_ATTR_HEADER_VALUE = "value";
+ private final String CONFIG_NODE_TYPE_REL = "issueTypes/issueType";
+ private final String CONFIG_NODE_MAPPING_IN_REL = "mappings/mappingIn";
+ private final String CONFIG_NODE_MAPPING_OUT_REL = "mappings/mappingOut";
+ private final String CONFIG_NODE_XMLFIELDS = "xmlFields";
+ private final String CONFIG_NODE_FIELDS = "fields";
+ private final String CONFIG_NODE_FIELD = "field";
+ private final String CONFIG_NODE_TEMPLATE = "template";
+ private final String CONFIG_NODE_TEMPLATE_FIELD_REL = "templateFields/templateField";
+ private final String CONFIG_ATTR_ID_PREFIX = "idPrefix";
+ private final String CONFIG_ATTR_ID_SUFFIX = "idSuffix";
+ private final String CONFIG_ATTR_ALWAYS_SAVE = "alwaysSave";
+ private final String CONFIG_NODE_ERRORLOG = "errorLog";
+ private final String CONFIG_NODE_MAP_REL = "maps/map";
+ private final String CONFIG_ATTR_KEY = "key";
+ private final String CONFIG_ATTR_VALUE = "value";
+ private final String CONFIG_ATTR_DEFAULT= "default";
+
+ /**
+ * Load LeanSync configuration from xml
+ * @param inputConfiguration the xml containing LeanSync configuration
+ * @return LeanSync configuration for each project
+ * @throws Exception
+ */
+ public Map loadConfiguration(String inputConfiguration) throws Exception {
+ Map confMap = new HashMap();
+ // initialize dom and xpath factory
+ DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
+ domFactory.setNamespaceAware(true);
+ DocumentBuilder builder = domFactory.newDocumentBuilder();
+ ByteArrayInputStream is = new ByteArrayInputStream(inputConfiguration.getBytes());
+ Document doc = builder.parse(is);
+
+ XPathFactory xpathFactory = new XPathFactoryImpl();
+ XPath xpath = xpathFactory.newXPath();
+
+ // locate all property nodes:
+ NodeList configurationList = (NodeList) xpath.evaluate(CONFIG_NODE_CONFIG_FULL, doc, XPathConstants.NODESET);
+
+ if (configurationList != null) {
+ for (int i = 0; i < configurationList.getLength(); i++) {
+ Node configuration = configurationList.item(i);
+ SyncConfiguration fieldConfiguration = processConfiguration(configuration, xpath);
+ for (String project : fieldConfiguration.getProjects()) {
+ confMap.put(project, fieldConfiguration);
+ }
+ }
+ }
+ return confMap;
+ }
+
+ /**
+ * Load the configuration section from LeanSync configuration
+ * @param configurationNode the configuration section in xml
+ * @param xpath the path to the configuration section in xml
+ * @return the configuration for LeanSync
+ * @throws Exception
+ */
+ private SyncConfiguration processConfiguration(Node configurationNode, XPath xpath ) throws Exception {
+ SyncConfiguration configuration = new SyncConfiguration();
+
+ processErrorLog(configurationNode, configuration, xpath);
+ processProjects(configurationNode, configuration, xpath);
+ processIssueTypes(configurationNode, configuration, xpath);
+ processDomains(configurationNode, configuration, xpath);
+ processMappings(configurationNode, configuration, xpath);
+
+ return configuration;
+ }
+
+ /**
+ * It validates if the IDs in the templates are defined in the field configuration
+ * @param mapping the mapping configuration
+ * @param name information text for validation. It specifies the type of mapping
+ * @return the validation message, it's empty if the configuration is valid
+ */
+ private static String validateMapping(SyncMapping mapping, String name){
+ List templates = mapping.getTemplates();
+ StringBuilder validation = new StringBuilder();
+ if (templates != null) {
+ for (SyncTemplate template : templates) {
+ String validateTemplate = validateTemplate(mapping, template, name);
+ if (validateTemplate != null) {
+ validation.append(validateTemplate);
+ }
+ }
+ }
+ if(validation.length() == 0){
+ return null;
+ }else{
+ return validation.toString();
+ }
+ }
+
+ /**
+ * It validates if the IDs in the template are defined in the field configuration
+ * @param mapping the mapping configuration
+ * @param temlate the template containing IDs which are checked if there are defined in the field configuration
+ * @param name information text for validation. It specifies the type of mapping
+ * @return the validation message, it's empty if the configuration is valid
+ */
+ private static String validateTemplate(SyncMapping mapping, SyncTemplate template, String name){
+ if(template.getTemplate() == null || mapping.getFields() == null){
+ return null;
+ }
+ StringBuilder result = new StringBuilder();
+
+ List templateItem = new ArrayList();
+ Pattern p = Pattern.compile(template.getIdPrefix() + ".+?" + template.getIdSuffix());
+ Matcher m = p.matcher(template.getTemplate());
+
+ Set declaredFelds = new HashSet();
+ List fields = mapping.getFields();
+ for (SyncField leanSyncField : fields) {
+ declaredFelds.add(template.getIdPrefix() + leanSyncField.getId() + template.getIdSuffix());
+ }
+
+ List xmlFieldConfigsList = mapping.getXmlFieldConfigs();
+ for (SyncXmlFieldConfig xmlFieldConfig : xmlFieldConfigsList) {
+ List fieldList = xmlFieldConfig.getFields();
+ for (SyncField leanSyncField : fieldList) {
+ declaredFelds.add(template.getIdPrefix() + leanSyncField.getId() + template.getIdSuffix());
+ }
+ }
+
+ while(m.find()) {
+ templateItem.add(m.group());
+ }
+ for (String item : templateItem) {
+ if(!declaredFelds.contains(item)){
+ if(result.length() != 0){
+ result.append(", ");
+ }
+ result.append(item);
+ }
+ }
+
+ if(result.length() > 0){
+ return name + " mapping - following items do not contain field declaration: " + result.toString() +". ";
+ }
+
+ return null;
+ }
+
+ /**
+ * Load the error log section
+ * @param configurationNode the configuration section
+ * @param configuration the configuration data
+ * @param xpath the path to the error log section
+ * @throws XPathExpressionException
+ */
+ private void processErrorLog(Node configurationNode, SyncConfiguration configuration, XPath xpath) throws XPathExpressionException {
+ Node errorLogNode = (Node) xpath.evaluate(CONFIG_NODE_ERRORLOG, configurationNode, XPathConstants.NODE);
+
+ if (errorLogNode == null) {
+ return;
+ }
+
+ NamedNodeMap attributes = errorLogNode.getAttributes();
+ Node nameNode = attributes.getNamedItem(CONFIG_ATTR_NAME);
+
+ if (nameNode != null) {
+ configuration.setErrorLog(nameNode.getNodeValue());
+ }
+
+ }
+
+ /**
+ * Load the projects section with the list of the projects which the configuration is valid for
+ * @param configurationNode the configuration section
+ * @param configuration the configuration data
+ * @param xpath the path to projects section
+ * @throws XPathExpressionException
+ */
+ private void processProjects(Node configurationNode, SyncConfiguration configuration, XPath xpath) throws XPathExpressionException {
+ Set values = processStringNodeListAsSet(configurationNode, xpath, CONFIG_NODE_PROJECT_REL);
+ if(values != null){
+ configuration.setProjects(values);
+ }
+ }
+
+ /**
+ * Load the domains section with the list of the domains which the configuration is valid for
+ * @param configurationNode the configuration section
+ * @param configuration the configuration data
+ * @param xpath the xpath instance
+ * @throws XPathExpressionException
+ */
+ private void processDomains(Node configurationNode, SyncConfiguration configuration, XPath xpath) throws XPathExpressionException {
+ Set values = processStringNodeListAsSet(configurationNode, xpath, CONFIG_NODE_DOMAIN_REL);
+ if(values != null){
+ configuration.setDomains(values);
+ }
+ }
+
+ /**
+ * Put the values of the nodes to the set
+ * @param parentNode the parent node of the nodes with the values
+ * @param xpath the xpath instance
+ * @param path the path to the nodes with the values
+ * @return the loaded set of the values
+ * @throws XPathExpressionException
+ */
+ private Set processStringNodeListAsSet(Node parentNode, XPath xpath, String path) throws XPathExpressionException {
+ NodeList nodeList = (NodeList) xpath.evaluate(path, parentNode, XPathConstants.NODESET);
+ if(nodeList == null){
+ return null;
+ }
+
+ Set values = new HashSet();
+
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node != null) {
+ String value = node.getTextContent();
+ if (value != null && !value.isEmpty()) {
+ values.add(value);
+ }
+ }
+ }
+ return values;
+
+ }
+
+ /**
+ * Load the issue types section with the list of the issue types which the configuration is valid for
+ * @param configurationNode the configuration section
+ * @param configuration the configuration data
+ * @param xpath the xpath instance
+ * @throws XPathExpressionException
+ */
+ private void processIssueTypes(Node configurationNode, SyncConfiguration configuration, XPath xpath) throws XPathExpressionException {
+ Set values = processStringNodeListAsSet(configurationNode, xpath, CONFIG_NODE_TYPE_REL);
+ if(values != null){
+ configuration.setIssueTypes(values);
+ }
+ }
+
+ /**
+ * Load the mappings section with the list of the mapping configurations.
+ * The mapping configurations are added to the configuration data (configuration)
+ * @param configurationNode the configuration section
+ * @param configuration the configuration data
+ * @param xpath the xpath instance
+ * @throws XPathExpressionException
+ */
+ private void processMappings(Node configurationNode, SyncConfiguration configuration, XPath xpath) throws Exception {
+
+ String validationResult = null;
+ String validationInResult = processMappingList(CONFIG_NODE_MAPPING_IN_REL, configurationNode, configuration, xpath);
+ if (validationInResult != null) {
+ validationResult = (validationResult == null) ? new String(validationInResult) : validationResult + validationInResult;
+ }
+
+ String validationOutResult = processMappingList(CONFIG_NODE_MAPPING_OUT_REL, configurationNode, configuration, xpath);
+ if (validationOutResult != null) {
+ validationResult = (validationResult == null) ? new String(validationOutResult) : validationResult + validationOutResult;
+ }
+
+ if (validationResult != null) {
+ throw new SyncConfigurationException(validationResult);
+ }
+ }
+
+ private String processMappingList(String mappingExpression, Node configurationNode, SyncConfiguration configuration, XPath xpath) throws Exception {
+ NodeList mappingList = (NodeList) xpath.evaluate(mappingExpression, configurationNode, XPathConstants.NODESET);
+
+ String validationResult = null;
+
+ if (mappingList != null) {
+ for (int i = 0; i < mappingList.getLength(); i++) {
+ Node mappingNode = mappingList.item(i);
+ SyncMapping mapping = processMapping(mappingNode, configuration, xpath);
+ String result = null;
+ if (CONFIG_NODE_MAPPING_IN_REL.equals(mappingExpression)) {
+ configuration.addInMapping(mapping);
+ result = validateMapping(mapping, "Inbound");
+ } else if (CONFIG_NODE_MAPPING_OUT_REL.equals(mappingExpression)) {
+ configuration.addOutMapping(mapping);
+ result = validateMapping(mapping, "Outbound");
+ }
+ if (result != null) {
+ validationResult = (validationResult == null) ? new String(result) : validationResult + result;
+ }
+ }
+ }
+
+ return validationResult;
+ }
+
+ /**
+ * Load the mapping section.
+ * @param mappingNode the mapping section
+ * @param config the configuration data
+ * @param xpath the xpath instance
+ * @return the mapping data
+ * @throws XPathExpressionException
+ * @throws SyncConfigurationException
+ */
+ private SyncMapping processMapping(Node mappingNode, SyncConfiguration config, XPath xpath) throws XPathExpressionException, SyncConfigurationException {
+ SyncMapping leanSyncMapping = new SyncMapping();
+
+ processConnection(mappingNode, leanSyncMapping, xpath);
+ processFieldConfig(mappingNode, leanSyncMapping, xpath);
+
+ return leanSyncMapping;
+ }
+
+
+ /**
+ * Load the templates section. The templates are added to the mapping data (mapping)
+ * @param mappingNode the mapping section
+ * @param mapping the mapping data
+ * @param xpath the xpath instance
+ * @throws XPathExpressionException
+ * @throws SyncConfigurationException
+ */
+ private void processTemplates(Node mappingNode, SyncMapping mapping, XPath xpath) throws XPathExpressionException, SyncConfigurationException {
+ NodeList templateList = (NodeList) xpath.evaluate(CONFIG_NODE_TEMPLATE_FIELD_REL, mappingNode, XPathConstants.NODESET);
+ if(templateList == null){
+ return;
+ }
+
+ for (int i = 0; i < templateList.getLength(); i++) {
+ Node templateNode = templateList.item(i);
+ SyncTemplate template = processTemplate(templateNode, xpath);
+ if (template != null) {
+ mapping.addTemplate(template);
+ }
+ }
+ }
+
+ /**
+ * Load the template section.
+ * @param templateFieldNode the templates section
+ * @param xpath the xpath instance
+ * @return the template data
+ * @throws XPathExpressionException
+ * @throws SyncConfigurationException
+ */
+ private SyncTemplate processTemplate(Node templateFieldNode, XPath xpath) throws XPathExpressionException, SyncConfigurationException {
+ SyncTemplate template = new SyncTemplate();
+ processGeneralField(template, templateFieldNode, xpath);
+
+ NamedNodeMap attrs = templateFieldNode.getAttributes();
+ Node prefixNode = attrs.getNamedItem(CONFIG_ATTR_ID_PREFIX);
+ Node suffixNode = attrs.getNamedItem(CONFIG_ATTR_ID_SUFFIX);
+ Node alwaysSave = attrs.getNamedItem(CONFIG_ATTR_ALWAYS_SAVE);
+ Node templateNode = (Node) xpath.evaluate(CONFIG_NODE_TEMPLATE, templateFieldNode, XPathConstants.NODE);
+
+ if (prefixNode != null) {
+ String prefix = prefixNode.getNodeValue();
+ template.setIdPrefix(prefix);
+ }
+
+ if (suffixNode != null) {
+ String suffix = suffixNode.getNodeValue();
+ template.setIdSuffix(suffix);
+ }
+
+ if (alwaysSave != null) {
+ template.setAlwaysSave(alwaysSave.getNodeValue());
+ }
+
+ if(templateNode != null){
+ String templateText = loadXMLNodeValue(templateNode);
+ template.setTemplate(templateText);
+ }
+
+ return template;
+ }
+
+ /**
+ * Load the parameters which are common for all the types of the fields
+ * @param field the field configuration
+ * @param node the field section
+ * @param xpath xpath instance
+ * @throws XPathExpressionException
+ * @throws SyncConfigurationException
+ */
+ private void processGeneralField(SyncGeneralField field, Node node, XPath xpath) throws XPathExpressionException, SyncConfigurationException {
+ NamedNodeMap attrs = node.getAttributes();
+ Node nsNode = attrs.getNamedItem(CONFIG_ATTR_NS);
+ Node nameNode = attrs.getNamedItem(CONFIG_ATTR_NAME);
+ Node fieldTypeNode = attrs.getNamedItem(CONFIG_ATTR_FIELD_TYPE);
+ Node contentTypeNode = attrs.getNamedItem(CONFIG_ATTR_CONTENT_TYPE);
+ Node mapToNode = attrs.getNamedItem(CONFIG_ATTR_MAP_TO);
+ Node notifyChangeNode = attrs.getNamedItem(CONFIG_ATTR_NOTIFY_CHANGE);
+ Node actionNode = attrs.getNamedItem(CONFIG_ATTR_ACTION);
+
+ if (nsNode != null) {
+ field.setNs(nsNode.getNodeValue());
+ }
+ if (nameNode != null) {
+ field.setName(nameNode.getNodeValue());
+ }
+ if (fieldTypeNode != null) {
+ field.setFieldType(fieldTypeNode.getNodeValue());
+ }
+ if (contentTypeNode != null) {
+ field.setContentType(contentTypeNode.getNodeValue());
+ }
+ if (mapToNode != null) {
+ field.setMapTo(mapToNode.getNodeValue());
+ }
+ if (notifyChangeNode != null) {
+ String notifyChangeVal = notifyChangeNode.getNodeValue();
+ if(SyncConstants.BOOLEAN_FALSE.equalsIgnoreCase(notifyChangeVal)) {
+ field.setNotifyChange(false);
+ }
+ }
+ if (actionNode != null) {
+ String nodeValue = actionNode.getNodeValue();
+ ActionType actionType = ActionType.getActionType(nodeValue);
+ field.setAction(actionType);
+ }
+
+ Map valueMapping = processValueMapping(node, xpath);
+ field.setValueMapping(valueMapping);
+
+ String defaultValue = processDefaultValue(node, xpath);
+ field.setDefaultValue(defaultValue);
+ }
+
+
+ /**
+ * Load the fields section.
+ * @param fieldsNode the fields section
+ * @param xpath xpath instance
+ * @return loaded the list of the field configuration
+ * @throws XPathExpressionException
+ * @throws SyncConfigurationException
+ */
+ private List processFields(Node fieldsNode, XPath xpath) throws XPathExpressionException, SyncConfigurationException {
+ NodeList fieldNodeList = (NodeList) xpath.evaluate(CONFIG_NODE_FIELD, fieldsNode, XPathConstants.NODESET);
+ if(fieldNodeList == null){
+ return null;
+ }
+ List fieldList = new ArrayList();
+
+ for (int i = 0; i < fieldNodeList.getLength(); i++) {
+ Node fieldNode = fieldNodeList.item(i);
+ if (fieldNode != null) {
+ SyncField field = processField(fieldNode, xpath);
+ if(field != null){
+ fieldList.add(field);
+ }
+ }
+ }
+ return fieldList;
+ }
+
+ /**
+ * Load the field section.
+ * @param fieldNode the field section
+ * @param xpath xpath instance
+ * @return loaded the field configuration
+ * @throws XPathExpressionException
+ * @throws SyncConfigurationException
+ */
+ private SyncField processField(Node fieldNode, XPath xpath) throws XPathExpressionException, SyncConfigurationException {
+ SyncField field = new SyncField();
+ processGeneralField(field, fieldNode, xpath);
+
+ NamedNodeMap attributes = fieldNode.getAttributes();
+
+ Node idNode = attributes.getNamedItem(CONFIG_ATTR_ID);
+ Node xpathNode = attributes.getNamedItem(CONFIG_ATTR_XPATH);
+ Node keepTags = attributes.getNamedItem(CONFIG_ATTR_KEEP_TAGS);
+ Node encodeHTML = attributes.getNamedItem(CONFIG_ATTR_ENCODE_HTML);
+ Node toDateFormat = attributes.getNamedItem(CONFIG_ATTR_TO_DATE_FORMAT);
+ Node fromDateFormat = attributes.getNamedItem(CONFIG_ATTR_FROM_DATE_FORMAT);
+
+ if (idNode != null) {
+ field.setId(idNode.getNodeValue());
+ }
+ if (xpathNode != null) {
+ field.setXpath(xpathNode.getNodeValue());
+ }
+ if (keepTags != null) {
+ String keepTagsVal = keepTags.getNodeValue();
+ if(SyncConstants.BOOLEAN_TRUE.equalsIgnoreCase(keepTagsVal)) {
+ field.setKeepTags(true);
+ }
+ }
+ if (encodeHTML != null) {
+ String encodeHTMLVal = encodeHTML.getNodeValue();
+ if(SyncConstants.BOOLEAN_TRUE.equalsIgnoreCase(encodeHTMLVal)) {
+ field.setEncodeHtml(true);
+ }
+ }
+ if (toDateFormat != null) {
+ field.setToDateFormat(toDateFormat.getNodeValue());
+ }
+ if (fromDateFormat != null) {
+ field.setFromDateFormat(fromDateFormat.getNodeValue());
+ }
+
+ Map valueMapping = processValueMapping(fieldNode, xpath);
+ field.setValueMapping(valueMapping);
+
+ return field;
+ }
+
+ /**
+ * Load the connection section.
+ * The connection configuration is added to the mapping data
+ * @param mappingNode the mapping section
+ * @param xpath xpath instance
+ * @throws XPathExpressionException
+ * @throws SyncConfigurationException
+ */
+ private void processConnection(Node mappingNode, SyncMapping mapping, XPath xpath) throws XPathExpressionException {
+ NodeList connNodeList = (NodeList) xpath.evaluate(CONFIG_NODE_CONNECTION, mappingNode, XPathConstants.NODESET);
+
+ //username attribute
+ if (connNodeList != null && connNodeList.getLength() > 0) {
+ Node connNode = connNodeList.item(0);
+ NamedNodeMap connAttributes = connNode.getAttributes();
+ Node usernameNode = connAttributes.getNamedItem(CONFIG_ATTR_USERNAME);
+ if (usernameNode != null) {
+ String username = usernameNode.getNodeValue();
+ mapping.setUsername(username);
+ }
+
+ //password attribute
+ Node passwordNode = connAttributes.getNamedItem(CONFIG_ATTR_PASSWORD);
+ if (passwordNode != null) {
+ String password = passwordNode.getNodeValue();
+ mapping.setPassword(password);
+ }
+
+ //Headers
+ NodeList headderList = (NodeList) xpath.evaluate(CONFIG_NODE_HEADER_REL, connNode, XPathConstants.NODESET);
+ if (headderList != null) {
+ HashMap headers = new HashMap();
+
+ for (int i = 0; i < headderList.getLength(); i++) {
+ Node headerNode = headderList.item(i);
+ NamedNodeMap attributes = headerNode.getAttributes();
+ if (attributes != null) {
+ Node nameNode = attributes.getNamedItem(CONFIG_ATTR_NAME);
+ Node valueNode = attributes.getNamedItem(CONFIG_ATTR_HEADER_VALUE);
+ if (nameNode != null && nameNode.getNodeValue() != null && !nameNode.getNodeValue().isEmpty() && valueNode != null && valueNode.getNodeValue() != null) {
+ headers.put(nameNode.getNodeValue(), valueNode.getNodeValue());
+ }
+ }
+ }
+
+ if (!headers.isEmpty()) {
+ mapping.setHeaders(headers);
+ }
+ }
+
+ //RDF types
+ NodeList rdfTypeList = (NodeList) xpath.evaluate(CONFIG_NODE_RDFTYPE_REL, connNode, XPathConstants.NODESET);
+ if (rdfTypeList != null) {
+ Set rdfTypes = new HashSet();
+
+ for (int i = 0; i < rdfTypeList.getLength(); i++) {
+ Node rdfTypeNode = rdfTypeList.item(i);
+ NamedNodeMap attributes = rdfTypeNode.getAttributes();
+ if (attributes != null) {
+ Node valueNode = attributes.getNamedItem(CONFIG_ATTR_VALUE);
+ if (valueNode != null && valueNode.getNodeValue() != null && valueNode.getNodeValue() != null && !valueNode.getNodeValue().isEmpty() ) {
+ rdfTypes.add(valueNode.getNodeValue());
+ }
+ }
+ }
+
+ if (!rdfTypes.isEmpty()) {
+ mapping.setRdfTypes(rdfTypes);
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Load the field configurations section.
+ * The field configurations are added to the mapping data (mapping)
+ * @param mappingNode the section containing field mapping configuration
+ * @param mapping the mapping data
+ * @param xpath xpath instance
+ * @throws XPathExpressionException
+ * @throws SyncConfigurationException
+ */
+ private void processFieldConfig(Node mappingNode, SyncMapping mapping, XPath xpath) throws XPathExpressionException, SyncConfigurationException {
+ NodeList fieldsList = (NodeList) xpath.evaluate(CONFIG_NODE_FIELDS, mappingNode, XPathConstants.NODESET);
+
+ if (fieldsList != null) {
+ for (int i = 0; i < fieldsList.getLength(); i++) {
+ Node fieldsNode = fieldsList.item(i);
+ List fieldList = processFields(fieldsNode, xpath);
+ if(fieldList != null) {
+ mapping.setFields(fieldList);
+ }
+ }
+ }
+ processTemplates(mappingNode, mapping, xpath);
+ processXmlFieldConfigs(mappingNode, mapping, xpath);
+
+ }
+
+ /**
+ * Load the field configurations section in xml content of the field.
+ * The field configurations are added to the mapping data (mapping)
+ * @param mappingNode the section containing the field mapping configurations
+ * @param mapping the mapping data
+ * @param xpath xpath instance
+ * @throws XPathExpressionException
+ * @throws SyncConfigurationException
+ */
+ private void processXmlFieldConfigs(Node mappingNode, SyncMapping mapping, XPath xpath) throws XPathExpressionException, SyncConfigurationException {
+
+ NodeList xmlFieldsConfigList = (NodeList) xpath.evaluate(CONFIG_NODE_XMLFIELDS, mappingNode, XPathConstants.NODESET);
+ if(xmlFieldsConfigList == null){
+ return;
+ }
+
+ for (int i = 0; i < xmlFieldsConfigList.getLength(); i++) {
+ Node xmlFieldsConfigNode = xmlFieldsConfigList.item(i);
+ if (xmlFieldsConfigNode != null) {
+ SyncXmlFieldConfig xmlFieldConfig = processXmlFieldConfig(xmlFieldsConfigNode, xpath);
+ if(xmlFieldConfig != null){
+ mapping.addXmlFieldConfig(xmlFieldConfig);
+ }
+ }
+ }
+ }
+
+ /**
+ * Load the field configuration section in xml content of the field.
+ * @param xmlFieldsConfigNode the section containing the field mapping configurations
+ * @param xpath the xpath instance
+ * @return loaded the field configuration section
+ * @throws XPathExpressionException
+ * @throws SyncConfigurationException
+ */
+ private SyncXmlFieldConfig processXmlFieldConfig(Node xmlFieldsConfigNode, XPath xpath) throws XPathExpressionException, SyncConfigurationException {
+ SyncXmlFieldConfig xmlFieldConfig = new SyncXmlFieldConfig();
+
+ processXmlFieldsConfigProperties(xmlFieldsConfigNode, xmlFieldConfig);
+ List fields = processFields(xmlFieldsConfigNode, xpath);
+ if(fields != null){
+ xmlFieldConfig.setFields(fields);
+ }
+
+ return xmlFieldConfig;
+ }
+
+ /**
+ * Load the properties of the xml field configuration
+ * @param xmlFieldsNode the field configuration containing the properties for mapping
+ * @param xmlFieldsConfig field mapping configuration data
+ * @throws XPathExpressionException
+ */
+ private void processXmlFieldsConfigProperties(Node xmlFieldsNode, SyncXmlFieldConfig xmlFieldsConfig) throws XPathExpressionException {
+ NamedNodeMap attributes = xmlFieldsNode.getAttributes();
+
+ Node nsNode = attributes.getNamedItem(CONFIG_ATTR_NS);
+ Node nameNode = attributes.getNamedItem(CONFIG_ATTR_NAME);
+
+ if (nsNode != null) {
+ xmlFieldsConfig.setNs(nsNode.getNodeValue());
+ }
+ if (nameNode != null) {
+ xmlFieldsConfig.setName(nameNode.getNodeValue());
+ }
+ }
+
+ /**
+ * Load the section the value mapping
+ * @param node the value mapping section
+ * @param xpath xpath instance
+ * @return the value mapping table
+ * @throws XPathExpressionException
+ */
+ private Map processValueMapping(Node node, XPath xpath) throws XPathExpressionException {
+ NodeList mapList = (NodeList) xpath.evaluate(CONFIG_NODE_MAP_REL, node, XPathConstants.NODESET);
+
+ Map valueMapper = null;
+ if (mapList != null && mapList.getLength() > 0) {
+ valueMapper = new HashMap();
+ for (int i = 0; i < mapList.getLength(); i++) {
+ Node mapNode = mapList.item(i);
+
+ NamedNodeMap attrs = mapNode.getAttributes();
+ Node keyNode = attrs.getNamedItem(CONFIG_ATTR_KEY);
+ Node valueNode = attrs.getNamedItem(CONFIG_ATTR_VALUE);
+
+ if (keyNode != null && keyNode.getNodeValue() != null && valueNode != null && valueNode.getNodeValue() != null) {
+ valueMapper.put(keyNode.getNodeValue(), valueNode.getNodeValue());
+ }
+ }
+ }
+ return valueMapper;
+ }
+
+ /**
+ * Load default value for the value mapping
+ * @param node the section with default value configuration
+ * @param xpath xpath instance
+ * @return the default value for the value mapping
+ * @throws XPathExpressionException
+ */
+ private String processDefaultValue(Node node, XPath xpath) throws XPathExpressionException {
+ Node defaultNode = (Node) xpath.evaluate(CONFIG_ATTR_DEFAULT, node, XPathConstants.NODE);
+ if(defaultNode != null){
+ NamedNodeMap attrsDefault = defaultNode.getAttributes();
+ Node defaultValueNode = attrsDefault.getNamedItem(CONFIG_ATTR_VALUE);
+ if(defaultValueNode != null){
+ return defaultValueNode.getNodeValue();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Load xml value from the node
+ * @param node the node containing xml content
+ * @return xml value of the node
+ */
+ public static String loadXMLNodeValue(Node node){
+ if(node == null){
+ return null;
+ }
+
+ Document document = node.getOwnerDocument();
+ DOMImplementationLS domImplLS = (DOMImplementationLS) document
+ .getImplementation();
+ LSSerializer serializer = domImplLS.createLSSerializer();
+
+ LSOutput output=domImplLS.createLSOutput();
+ output.setEncoding(document.getInputEncoding());
+ StringWriter writer=new StringWriter();
+ output.setCharacterStream(writer);
+ serializer.getDomConfig().setParameter("xml-declaration", false);
+ serializer.write(node,output);
+ String value = writer.toString();
+ if(value != null){
+ value = value.replaceFirst("<"+node.getNodeName()+".*?>\n?", "");
+ int idx = value.indexOf(""+node.getNodeName()+">");
+ if(idx >= 0){
+ value = value.substring(0, idx);
+ }
+ }
+ return value;
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/SyncConstants.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/SyncConstants.java
new file mode 100644
index 0000000..aba0d82
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/SyncConstants.java
@@ -0,0 +1,45 @@
+package com.ericsson.eif.leansync.mapping;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The constants for LeanSync
+ *
+ */
+public interface SyncConstants {
+ public static final String CONFIG_CONTENT_TYPE_HTML = "html";
+ public static final String CONFIG_FIELD_TYPE_CUSTOM = "custom";
+ public static final String ID_PREFIX_DEFAULT_VALUE = "%";
+ public static final String ID_SUFFIX_DEFAULT_VALUE = "%";
+ public static final String BOOLEAN_FALSE = "false";
+ public static final String BOOLEAN_TRUE = "true";
+ public static final String INBOUND_SYNC_STATUS_MARK = "---Inbound sync---";
+ public static final String OUTBOUND_SYNC_STATUS_MARK = "---Outbound sync---";
+ public static final String END_OF_LINE = "\n";
+ public static final String HTML_END_OF_LINE = " ";
+}
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/SyncHelper.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/SyncHelper.java
new file mode 100644
index 0000000..0722cdd
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/SyncHelper.java
@@ -0,0 +1,201 @@
+package com.ericsson.eif.leansync.mapping;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringEscapeUtils;
+
+import com.ericsson.eif.leansync.mapping.data.SyncGeneralField;
+
+
+public class SyncHelper {
+
+ /**
+ * Encode all non alphanumeric chars are replaced with hex code
+ * and surrounded by "_" characters.
+ * @param name the name of tag
+ * @return encoded name
+ */
+ public static String encodeTagName(String name) {
+ if(name == null){
+ return null;
+ }
+
+ final int len = name.length();
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < len; i++) {
+ Character ch = name.charAt(i);
+ if (!Character.isDigit(ch) && !Character.isLetter(ch)) {
+ // Replace with _hex value of ascii_
+ sb.append('_');
+ sb.append(Integer.toHexString((int) ch));
+ sb.append('_');
+ } else {
+ sb.append(ch);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * It creates the error information for inbound and outbound connection.
+ * Inbound information are at first and then the outbound information is.
+ * The section for inbound and outbound connection are
+ * separated by constants INBOUND_SYNC_STATUS_MARK and OUTBOUND_SYNC_STATUS_MARK
+ * If inbound information is added the current outbound information must be kept and vice-versia
+ * Set status as null to clear information for defined direction (in/out)
+ * @param content the current value of status
+ * @param status the information whichewill be added
+ * @param isInbound choice if added information is for in/out connection
+ * @return sync status
+ */
+ public static String createSyncStatus(String content, String status,
+ boolean isInbound) {
+
+ if (content == null) {
+ content = "";
+ }
+ StringBuilder result = new StringBuilder();
+
+ int idxInboundMark = content.indexOf(SyncConstants.INBOUND_SYNC_STATUS_MARK);
+ int idxOutboundMark = content.indexOf(SyncConstants.OUTBOUND_SYNC_STATUS_MARK);
+
+ if (isInbound) {
+ // Set inbound status
+
+ if (status != null) {
+ result.append(SyncConstants.INBOUND_SYNC_STATUS_MARK);
+ result.append(SyncConstants.END_OF_LINE);
+ result.append(status.trim());
+ }
+ if (idxOutboundMark >= 0) {
+ result.append(SyncConstants.END_OF_LINE);
+ String outBoundText = content.substring(idxOutboundMark);
+ result.append(outBoundText.trim());
+ }
+ } else {
+ // Set Outbound status
+ if (idxInboundMark >= 0) {
+ String inboundText = null;
+ if (idxOutboundMark >= 0) {
+ inboundText = content.substring(idxInboundMark,
+ idxOutboundMark);
+ } else {
+ inboundText = content.substring(idxInboundMark);
+ }
+ result.append(inboundText.trim());
+ result.append(SyncConstants.END_OF_LINE);
+ }
+ if (status != null) {
+ result.append(SyncConstants.OUTBOUND_SYNC_STATUS_MARK);
+ result.append(SyncConstants.END_OF_LINE);
+ result.append(status.trim());
+ }
+ }
+ return result.toString().trim();
+ }
+
+ /**
+ * Add text to another text
+ * @param text current text
+ * @param textToAdd text which will be added to current text
+ * @return result text
+ */
+ public static String appendText(String text, String textToAdd ){
+ if(text == null){
+ text = textToAdd;
+ }else if(textToAdd == null){
+ text = textToAdd;
+ }else{
+ text+= textToAdd;
+ }
+ return text;
+ }
+
+ /**
+ * It maps the value to the defined value according to the configuration.
+ * When the value mapping doesn't match corresponding value the default value is set if it's defined
+ * If the mapping doesn't matches the value and default value is not defined the input value is returned
+ * @param value the value which will be mapped
+ * @param field the configuration of field which contains value mapping and defined default value
+ * @return mapped value
+ */
+ public static String mapToValue(String value, SyncGeneralField field){
+ Map valueMapping = field.getValueMapping();
+ String defaultValue = field.getDefaultValue();
+
+ if(valueMapping != null && valueMapping.containsKey(value)){
+ return valueMapping.get(value);
+ }else if(defaultValue != null){
+ return defaultValue;
+ }
+ return value;
+ }
+
+
+ /**
+ * Convert the list of the string to the one string where each items are separated by the end of the line
+ * @param list the list of the string which will be put to one string
+ * @return converted the list of string to the one string
+ */
+ public static String convertStringListToText(List list){
+ if(list == null){
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (String str : list) {
+ sb.append(str);
+ sb.append(SyncConstants.END_OF_LINE);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Replace \n for the
+ */
+ public static String convertToHTML(String text){
+ if(text == null){
+ return null;
+ }
+ return text.replaceAll(SyncConstants.END_OF_LINE, SyncConstants.HTML_END_OF_LINE);
+ }
+
+ /**
+ * Escape html text
+ */
+ public static String encodeHTML(String text){
+ if(text == null){
+ return null;
+ }
+ return StringEscapeUtils.escapeHtml(text);
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/ActionType.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/ActionType.java
new file mode 100644
index 0000000..3050682
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/ActionType.java
@@ -0,0 +1,48 @@
+package com.ericsson.eif.leansync.mapping.data;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The enumeration for action type.
+ * CREATE - a configuration is valid only when a resource in created
+ * UPDATE - the configuration is valid only when the resource is updated
+ * UNDEF - the configuration is valid for both action
+ *
+ */
+public enum ActionType {
+ CREATE, UPDATE, UNDEF;
+
+ public static ActionType getActionType(String type) {
+ if("CREATE".equalsIgnoreCase(type)){
+ return ActionType.CREATE;
+ }else if ("UPDATE".equalsIgnoreCase(type)){
+ return UPDATE;
+ }
+ return ActionType.UNDEF;
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncConfiguration.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncConfiguration.java
new file mode 100644
index 0000000..3d8dea9
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncConfiguration.java
@@ -0,0 +1,140 @@
+package com.ericsson.eif.leansync.mapping.data;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.xml.bind.annotation.XmlElement;
+
+/**
+ * Data class representing the configuration. There can be one or more configurations for different projects
+ *
+ */
+public class SyncConfiguration {
+
+ private Set projects;
+ private Set issueTypes;
+ private Set domains;
+ @XmlElement(name="inMappings")
+ private List inMappingList;
+ @XmlElement(name="outMappings")
+ private List outMappingList;
+ private String errorLog;
+
+ public SyncConfiguration(){
+ projects = new HashSet();
+ issueTypes = new HashSet();
+ domains = new HashSet();
+ inMappingList = new ArrayList();
+ outMappingList = new ArrayList();
+ }
+
+ public Set getProjects() {
+ return projects;
+ }
+
+ public void setProjects(Set projects) {
+ this.projects = projects;
+ }
+
+ public void addProject(String project) {
+ this.projects.add(project);
+ }
+
+ public Set getIssueTypes() {
+ return issueTypes;
+ }
+
+ public void setIssueTypes(Set issueTypes) {
+ this.issueTypes = issueTypes;
+ }
+
+ public void addIssueType(String issueType) {
+ this.issueTypes.add(issueType);
+ }
+
+ public boolean containsIssueType(String issueType) {
+ return (this.issueTypes != null && this.issueTypes.contains(issueType));
+ }
+
+ public List getInMappings() {
+ return inMappingList;
+ }
+
+ public void setInMapping(List inMappings) {
+ this.inMappingList = inMappings;
+ }
+
+ public List getOutMappings() {
+ return outMappingList;
+ }
+
+ public void setOutMappings(List outMappings) {
+ this.outMappingList = outMappings;
+ }
+
+ public void addOutMapping(SyncMapping mappings) {
+ this.outMappingList.add(mappings);
+ }
+ public void addInMapping(SyncMapping mappings) {
+ this.inMappingList.add(mappings);
+ }
+
+ public SyncMapping getFirstInMapping() {
+ if(inMappingList != null && inMappingList.size() > 0){
+ return inMappingList.get(0);
+ }
+ return null;
+ }
+
+ public SyncMapping getFirstOutMapping() {
+ if(outMappingList != null && outMappingList.size() > 0){
+ return outMappingList.get(0);
+ }
+ return null;
+ }
+
+ public String getErrorLog() {
+ return errorLog;
+ }
+
+ public void setErrorLog(String errorLog) {
+ this.errorLog = errorLog;
+ }
+
+ public Set getDomains() {
+ return domains;
+ }
+
+ public void setDomains(Set domains) {
+ this.domains = domains;
+ }
+
+}
\ No newline at end of file
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncField.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncField.java
new file mode 100644
index 0000000..7835c4c
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncField.java
@@ -0,0 +1,97 @@
+package com.ericsson.eif.leansync.mapping.data;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * Data class containing a configuration for the synchronization between fields
+ *
+ */
+public class SyncField extends SyncGeneralField{
+ //unique ID of field which is used e,g, in the template
+ private String id;
+ //location of the field in the xml. The path is defined by XPATH
+ private String xpath;
+ //true - the tags from the value of the filed will not be removed
+ private boolean keepTags;
+ //true - encode HTML character e.g. '<' -> '!lt;'
+ private boolean encodeHtml;
+ // input value of date will be converted to this format
+ private String toDateFormat;
+ //specifies the date format in input value of date
+ private String fromDateFormat;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getXpath() {
+ return xpath;
+ }
+
+ public void setXpath(String xpath) {
+ this.xpath = xpath;
+ }
+
+ public boolean isKeepTags() {
+ return keepTags;
+ }
+
+ public void setKeepTags(boolean keepTags) {
+ this.keepTags = keepTags;
+ }
+
+ public boolean isEncodeHtml() {
+ return encodeHtml;
+ }
+
+ public void setEncodeHtml(boolean encodeHtml) {
+ this.encodeHtml = encodeHtml;
+ }
+
+ public String getToDateFormat() {
+ return toDateFormat;
+ }
+
+ public void setToDateFormat(String toDateFormat) {
+ this.toDateFormat = toDateFormat;
+ }
+
+ public String getFromDateFormat() {
+ return fromDateFormat;
+ }
+
+ public void setFromDateFormat(String fromDateFormat) {
+ this.fromDateFormat = fromDateFormat;
+ }
+
+
+}
\ No newline at end of file
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncGeneralField.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncGeneralField.java
new file mode 100644
index 0000000..ec6a6da
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncGeneralField.java
@@ -0,0 +1,111 @@
+package com.ericsson.eif.leansync.mapping.data;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.Map;
+
+/**
+ * Data class contains the common parameters for the fields of configuration
+ *
+ */
+public abstract class SyncGeneralField {
+ //the namespace of the field which will be mapped
+ private String ns;
+ //the name of the field whihc will be mapped
+ private String name;
+ //the type of field e.g. custom -> specifies the the field is custom and not general field of the system
+ private String fieldType;
+ //the type of field e.g. html - the characters "\n" will be replaced by in outgoing data
+ private String contentType;
+ //The name of the field where the value will be put
+ private String mapTo;
+ //The type of action with resource - create or update
+ private ActionType action;
+ //the value can be mapped to another value e.g. for priority: A->1. B->2, C->3
+ private Map valueMapping;
+ //true - when the field is change then the change is propagated to a remote system
+ private boolean notifyChange = true;
+ //when the value mapping doesn't match the value then the default value is used
+ private String defaultValue;
+
+ public String getNs() {
+ return ns;
+ }
+ public void setNs(String ns) {
+ this.ns = ns;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getFieldType() {
+ return fieldType;
+ }
+ public void setFieldType(String fieldType) {
+ this.fieldType = fieldType;
+ }
+ public String getContentType() {
+ return contentType;
+ }
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+ public String getMapTo() {
+ return mapTo;
+ }
+ public void setMapTo(String mapTo) {
+ this.mapTo = mapTo;
+ }
+ public Map getValueMapping() {
+ return valueMapping;
+ }
+ public void setValueMapping(Map valueMapping) {
+ this.valueMapping = valueMapping;
+ }
+ public ActionType getAction() {
+ return action;
+ }
+ public void setAction(ActionType action) {
+ this.action = action;
+ }
+ public boolean isNotifyChange() {
+ return notifyChange;
+ }
+ public void setNotifyChange(boolean notifyChange) {
+ this.notifyChange = notifyChange;
+ }
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+ public void setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncMapping.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncMapping.java
new file mode 100644
index 0000000..439384d
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncMapping.java
@@ -0,0 +1,162 @@
+package com.ericsson.eif.leansync.mapping.data;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Data class for mapping between the fields. There can be two mapping for incoming and outgoing connections
+ *
+ */
+public class SyncMapping {
+ //the http headers which are put to the header of outgoing request
+ private Map headers;
+ //it specifies rdf types of the outgoing content
+ private Set rdfTypes;
+ //it's used for basic auth to the remote system
+ private String username;
+ //it's used for basic auth to the remote system
+ private String password;
+ //sync configuration for field mapping
+ private List fields;
+ //sync configuration for field mapping - the fields are saved in xml
+ private List xmlFieldConfigs;
+ //for mapping M:1
+ private List templates;
+
+ public SyncMapping(){
+ headers = new HashMap();
+ rdfTypes = new HashSet();
+ fields = new ArrayList();
+ xmlFieldConfigs = new ArrayList();
+ }
+
+ public Map getHeaders() {
+ return headers;
+ }
+
+ public void setHeaders(Map headers) {
+ this.headers = headers;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public void setTemplates(List templates) {
+ this.templates = templates;
+ }
+
+ public Set getRdfTypes() {
+ return rdfTypes;
+ }
+
+ public void setRdfTypes(Set rdfTypes) {
+ this.rdfTypes = rdfTypes;
+ }
+
+ public void addTemplate(SyncXmlFieldConfig config){
+ if(xmlFieldConfigs == null){
+ xmlFieldConfigs = new ArrayList();
+ }
+ xmlFieldConfigs.add(config);
+ }
+
+ public void addField(SyncField field){
+ if(fields == null){
+ fields = new ArrayList();
+ }
+ fields.add(field);
+ }
+
+ public void addTemplate(SyncTemplate template){
+ if(templates == null){
+ templates = new ArrayList();
+ }
+ templates.add(template);
+ }
+
+ public void addXmlFieldConfig(SyncXmlFieldConfig xmlFieldConfig){
+ if(xmlFieldConfigs == null){
+ xmlFieldConfigs = new ArrayList();
+ }
+ xmlFieldConfigs.add(xmlFieldConfig);
+ }
+
+ public void addHeader(String name, String value){
+ if(headers == null){
+ headers = new HashMap();
+ }
+ headers.put(name, value);
+ }
+
+ public void addRdfType(String value){
+ if(rdfTypes == null){
+ rdfTypes = new HashSet();
+ }
+ rdfTypes.add(value);
+ }
+
+ public List getFields() {
+ return fields;
+ }
+
+ public void setFields(List fields) {
+ this.fields = fields;
+ }
+
+ public List getXmlFieldConfigs() {
+ return xmlFieldConfigs;
+ }
+
+ public void setXmlFieldConfigs(List xmlFieldConfigs) {
+ this.xmlFieldConfigs = xmlFieldConfigs;
+ }
+
+ public List getTemplates() {
+ return templates;
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncSnapshot.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncSnapshot.java
new file mode 100644
index 0000000..814195e
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncSnapshot.java
@@ -0,0 +1,61 @@
+package com.ericsson.eif.leansync.mapping.data;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * Data class contains the value of template and configuration of template field
+ * It serves for saving a snapshot and for field mapping M:1
+ *
+ */
+public class SyncSnapshot {
+ //the configuration for template field
+ private SyncTemplate templateConfig;
+ //the value of the field which are mapped by the template
+ private String value;
+
+ public SyncSnapshot(SyncTemplate templateConfig) {
+ this.templateConfig = templateConfig;
+ }
+ public SyncSnapshot(SyncTemplate templateConfig, String value) {
+ this.templateConfig = templateConfig;
+ this.value = value;
+ }
+ public SyncTemplate getTemplateConfig() {
+ return templateConfig;
+ }
+ public void setTemplateConfig(SyncTemplate templateConfig) {
+ this.templateConfig = templateConfig;
+ }
+ public String getValue() {
+ return value;
+ }
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncTemplate.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncTemplate.java
new file mode 100644
index 0000000..e6a9180
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncTemplate.java
@@ -0,0 +1,78 @@
+package com.ericsson.eif.leansync.mapping.data;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import com.ericsson.eif.leansync.mapping.SyncConstants;
+
+/**
+ * Data class contains the template for the field mapping M:1 or the snapshot
+ *
+ */
+public class SyncTemplate extends SyncGeneralField{
+ //the template containing special marks for inserting the field values
+ private String template;
+ //prefix for id of the field in the template
+ private String idPrefix;
+ //suffix for id of the field in the template
+ private String idSuffix;
+ //true - the mapped value of the field will be save even when the error accurs
+ private String alwaysSave;
+
+ public SyncTemplate(){
+ idPrefix = SyncConstants.ID_PREFIX_DEFAULT_VALUE;
+ idSuffix = SyncConstants.ID_SUFFIX_DEFAULT_VALUE;
+ }
+
+ public String getTemplate() {
+ return template;
+ }
+ public void setTemplate(String template) {
+ this.template = template;
+ }
+ public String getIdPrefix() {
+ return idPrefix;
+ }
+ public void setIdPrefix(String idPrefix) {
+ this.idPrefix = idPrefix;
+ }
+ public String getIdSuffix() {
+ return idSuffix;
+ }
+ public void setIdSuffix(String idSuffix) {
+ this.idSuffix = idSuffix;
+ }
+
+ public String getAlwaysSave() {
+ return alwaysSave;
+ }
+
+ public void setAlwaysSave(String alwaysSave) {
+ this.alwaysSave = alwaysSave;
+ }
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncXmlFieldConfig.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncXmlFieldConfig.java
new file mode 100644
index 0000000..dec6862
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/data/SyncXmlFieldConfig.java
@@ -0,0 +1,62 @@
+package com.ericsson.eif.leansync.mapping.data;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.List;
+
+/**
+ * Data class containing a configuration for the synchronization between fields
+ * The configuration is for the fields which are in the xml content of the field
+ *
+ */
+public class SyncXmlFieldConfig {
+ private String ns;
+ private String name;
+ private List fields;
+
+ public String getNs() {
+ return ns;
+ }
+ public void setNs(String ns) {
+ this.ns = ns;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public List getFields() {
+ return fields;
+ }
+ public void setFields(List fields) {
+ this.fields = fields;
+ }
+
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/exceptions/SyncConfigurationException.java b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/exceptions/SyncConfigurationException.java
new file mode 100644
index 0000000..50816e6
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/eif/leansync/mapping/exceptions/SyncConfigurationException.java
@@ -0,0 +1,52 @@
+package com.ericsson.eif.leansync.mapping.exceptions;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The exception is thrown when the the Sync configuration is not valid.
+ *
+ */
+public class SyncConfigurationException extends Exception {
+
+ private static final long serialVersionUID = -9007611492783867749L;
+
+ public SyncConfigurationException(){
+ }
+
+ public SyncConfigurationException(String message){
+ super(message);
+ }
+
+ public SyncConfigurationException(Throwable cause){
+ super(cause);
+ }
+
+ public SyncConfigurationException(String message, Throwable cause){
+ super(message, cause);
+ }
+}
\ No newline at end of file
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/Constants.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/Constants.java
new file mode 100644
index 0000000..7895b59
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/Constants.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2012 IBM, 2013 Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Russell Boykin - initial API and implementation
+ * Alberto Giammaria - initial API and implementation
+ * Chris Peters - initial API and implementation
+ * Gianluca Bernardini - initial API and implementation
+ * Michael Fiedler - Bugzilla adpater implementations
+ *******************************************************************************/
+package com.ericsson.jira.oslc;
+
+import org.eclipse.lyo.oslc4j.core.model.OslcConstants;
+
+public interface Constants
+{
+ public static String CHANGE_MANAGEMENT_DOMAIN = "http://open-services.net/ns/cm#";
+ public static String CHANGE_MANAGEMENT_NAMESPACE = "http://open-services.net/ns/cm#";
+ public static String CHANGE_MANAGEMENT_NAMESPACE_PREFIX = "oslc_cm";
+ public static String FOAF_NAMESPACE = "http://xmlns.com/foaf/0.1/";
+ public static String FOAF_NAMESPACE_PREFIX = "foaf";
+ public static String QUALITY_MANAGEMENT_NAMESPACE = "http://open-services.net/ns/qm#";
+ public static String QUALITY_MANAGEMENT_PREFIX = "oslc_qm";
+ public static String REQUIREMENTS_MANAGEMENT_NAMESPACE = "http://open-services.net/ns/rm#";
+ public static String REQUIREMENTS_MANAGEMENT_PREFIX = "oslc_rm";
+ public static String SOFTWARE_CONFIGURATION_MANAGEMENT_NAMESPACE = "http://open-services.net/ns/scm#";
+ public static String SOFTWARE_CONFIGURATION_MANAGEMENT_PREFIX = "oslc_scm";
+ public static String JIRA_DOMAIN = "http://atlassian.com/ns/cm#";
+ public static String JIRA_NAMESPACE = "http://atlassian.com/ns/cm#";
+ public static String JIRA_NAMESPACE_PREFIX = "jira";
+
+ public static String CHANGE_REQUEST = "ChangeRequest";
+ public static String TYPE_CHANGE_REQUEST = CHANGE_MANAGEMENT_NAMESPACE + "ChangeRequest";
+ public static String TYPE_RELATED_CHANGE_REQUEST = CHANGE_MANAGEMENT_NAMESPACE + "relatedChangeRequest";
+ public static String TYPE_CHANGE_SET = SOFTWARE_CONFIGURATION_MANAGEMENT_NAMESPACE + "ChangeSet";
+ public static String TYPE_DISCUSSION = OslcConstants.OSLC_CORE_NAMESPACE + "Discussion";
+ public static String TYPE_PERSON = FOAF_NAMESPACE + "Person";
+ public static String TYPE_REQUIREMENT = REQUIREMENTS_MANAGEMENT_NAMESPACE + "Requirement";
+ public static String TYPE_TEST_CASE = QUALITY_MANAGEMENT_NAMESPACE + "TestCase";
+ public static String TYPE_TEST_EXECUTION_RECORD = QUALITY_MANAGEMENT_NAMESPACE + "TestExecutionRecord";
+ public static String TYPE_TEST_PLAN = QUALITY_MANAGEMENT_NAMESPACE + "TestPlan";
+ public static String TYPE_TEST_RESULT = QUALITY_MANAGEMENT_NAMESPACE + "TestResult";
+ public static String TYPE_TEST_SCRIPT = QUALITY_MANAGEMENT_NAMESPACE + "TestScript";
+
+ public static String PATH_CHANGE_REQUEST = "changeRequest";
+
+ public static String USAGE_LIST = CHANGE_MANAGEMENT_NAMESPACE + "list";
+
+ public static final String HDR_OSLC_VERSION = "OSLC-Core-Version";
+ public static final String OSLC_VERSION_V2 = "2.0";
+
+ public static final String NEXT_PAGE = "jira.NextPage";
+
+ //Jira issue - OSLC types
+ public static final String JIRA_TYPE_ASIGNEE = Constants.JIRA_NAMESPACE + "assignee";
+ public static final String JIRA_TYPE_REPORTER = Constants.JIRA_NAMESPACE + "reporter";
+ public static final String JIRA_TYPE_DESCRIPTION = OslcConstants.DCTERMS_NAMESPACE + "description";
+ public static final String JIRA_TYPE_ENVIRONMNET = Constants.JIRA_NAMESPACE + "environment";
+ public static final String JIRA_TYPE_PRIORITY = Constants.JIRA_NAMESPACE + "IssuePriority";
+ public static final String JIRA_TYPE_PROJECT_ID = Constants.JIRA_NAMESPACE + "projectId";
+ public static final String JIRA_TYPE_STATUS = Constants.JIRA_NAMESPACE + "IssueStatus";
+ public static final String JIRA_TYPE_ISSUE_TYPE = Constants.JIRA_NAMESPACE + "IssueType";
+ public static final String JIRA_TYPE_COMPONENT = Constants.JIRA_NAMESPACE + "component";
+ public static final String JIRA_TYPE_AFFECTS_VERSION = Constants.JIRA_NAMESPACE + "affectsVersion";
+ public static final String JIRA_TYPE_FIX_VERSION = Constants.JIRA_NAMESPACE + "fixVersion";
+ public static final String JIRA_TYPE_ORIGINAL_ESTIMATE = Constants.JIRA_NAMESPACE + "originalEstimate";
+ public static final String JIRA_TYPE_REMAINING_ESTIMATE = Constants.JIRA_NAMESPACE + "remainingEstimate";
+ public static final String JIRA_TYPE_TIME_SPENT = Constants.JIRA_NAMESPACE + "timeSpent";
+ public static final String JIRA_TYPE_RESOLUTION = Constants.JIRA_NAMESPACE + "IssueResolution";
+ public static final String JIRA_TYPE_LABEL = Constants.JIRA_NAMESPACE + "label";
+ public static final String JIRA_TYPE_VOTER = Constants.JIRA_NAMESPACE + "voter";
+ public static final String JIRA_TYPE_WATCHER = Constants.JIRA_NAMESPACE + "watcher";
+ public static final String JIRA_TYPE_CUSTOM_FIELD = Constants.JIRA_NAMESPACE + "customField";
+ public static final String JIRA_TYPE_HISTORY = Constants.JIRA_NAMESPACE + "IssueHistory";
+
+ //dcterms types
+ public static final String DCTERMS_TITLE = OslcConstants.DCTERMS_NAMESPACE + "title";
+ public static final String DCTERMS_DESCRIPTION = OslcConstants.DCTERMS_NAMESPACE + "description";
+ public static final String DCTERMS_IDENTIFIER = OslcConstants.DCTERMS_NAMESPACE + "identifier";
+ public static final String DCTERMS_DUEDATE = OslcConstants.DCTERMS_NAMESPACE + "dueDate";
+
+ //OSLC constant
+ public static final String OSLC_CM_STATUS = "http://open-services.net/ns/cm#" + "status";
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/Credentials.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/Credentials.java
new file mode 100644
index 0000000..d4320e4
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/Credentials.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2011 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package com.ericsson.jira.oslc;
+
+/**
+ * Encapsulates a Bugzilla username and password.
+ *
+ * @author Samuel Padgett
+ */
+public class Credentials {
+ private String username;
+ private String password;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
\ No newline at end of file
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/HTTP.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/HTTP.java
new file mode 100644
index 0000000..4779c80
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/HTTP.java
@@ -0,0 +1,455 @@
+package com.ericsson.jira.oslc;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import net.oauth.OAuth;
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthException;
+import net.oauth.OAuthMessage;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+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.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ericsson.jira.oslc.utils.LogUtils;
+
+
+/**
+ * A simple HTTP client with support for authentication.
+ */
+public class HTTP {
+ private static final String CURRENT_CLASS = "HTTP";
+
+ public static enum OAuthPhases {
+ /**
+ * OAuth dance phase 1 - getting request token
+ */
+ OAUTH_PHASE_1,
+ /**
+ * OAuth dance phase 2 - user authorization and getting access token
+ */
+ OAUTH_PHASE_2,
+ /**
+ * OAuth authorization (using access token)
+ */
+ OAUTH_PHASE_3
+ };
+
+ private static final Logger logger = LoggerFactory.getLogger(HTTP.class);
+
+ /**
+ * It wraps the DefaultHttpClient to avoid to use ssl connection.
+ * @param base default http client
+ * @return
+ */
+ private static DefaultHttpClient wrapClient(DefaultHttpClient base) {
+ String currentMethod = "wrapClient";
+ try {
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ X509TrustManager tm = new X509TrustManager() {
+
+ public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
+ }
+
+ public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return (X509Certificate[]) null;
+ }
+ };
+ ctx.init(null, new TrustManager[] { tm }, null);
+ SSLSocketFactory ssf = new SSLSocketFactory(ctx);
+ ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+ ClientConnectionManager ccm = base.getConnectionManager();
+ SchemeRegistry sr = ccm.getSchemeRegistry();
+ sr.register(new Scheme("https", ssf, 443));
+ return new DefaultHttpClient(ccm, base.getParams());
+ }
+ catch (Exception ex) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + ex.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Creates OAuth header which is different for individual oauth phases
+ * @param url the url where the request will be sent
+ * @param method http method - POST, PUT, GET ...
+ * @param accessor OAuth accessor
+ * @param verifier verification code
+ * @param phase oauth phase
+ * @return OAuth header
+ */
+ private static String getOAuthAuthorizationHeader(
+ String url, String method, OAuthAccessor accessor, String verifier, OAuthPhases phase) {
+ String currentMethod = "getOAuthAuthorization";
+
+ String oAuthHeader = null;
+
+ List params = new ArrayList();
+ switch (phase) {
+ case OAUTH_PHASE_1:
+ params.add(new OAuth.Parameter("oauth_callback", accessor.consumer.callbackURL));
+ break;
+ case OAUTH_PHASE_2:
+ params.add(new OAuth.Parameter("oauth_token", accessor.requestToken));
+ if (verifier != null) {
+ params.add(new OAuth.Parameter("oauth_verifier", verifier));
+ }
+ break;
+ case OAUTH_PHASE_3:
+ params.add(new OAuth.Parameter("oauth_token", accessor.accessToken));
+ break;
+ }
+
+ OAuthMessage request;
+ try {
+ request = accessor.newRequestMessage(method, url, params);
+ oAuthHeader = request.getAuthorizationHeader("JIRA");
+ }
+ catch (OAuthException e) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ }
+ catch (IOException e) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ }
+ catch (URISyntaxException e) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ }
+
+ return oAuthHeader;
+ }
+
+ /**
+ * Get resource on defined url
+ * @param url the location of the resource
+ * @param accept string with accept content definition (e.g. "text/html")
+ * @param user user login used in basic authorization
+ * @param password user password used in basic authorization
+ * @param headers the headers which will be added to the request
+ * @return http response from successful request, otherwise return null
+ * @throws ClientProtocolException
+ * @throws IOException
+ */
+ public static HttpResponse get(String url, String accept, String user, String password, Map headers)
+ throws ClientProtocolException, IOException {
+ return get(url, accept, null, user, password, null, null, null, headers);
+ }
+
+ /**
+ * Get resource on defined url
+ * @param url the location of the resource
+ * @param accept string with accept content definition (e.g. "text/html")
+ * @param content content-type - the type of the content in the body of the request (e.g. "text/html")
+ * @param user user login used in basic authorization
+ * @param password user password used in basic authorization
+ * @param headers the headers which will be added to the request
+ * @return http response from successful request, otherwise return null
+ * @throws ClientProtocolException
+ * @throws IOException
+ */
+ public static HttpResponse get(String url, String accept, String content, String user, String password, Map headers)
+ throws ClientProtocolException, IOException {
+ return get(url, accept, content, user, password, null, null, OAuthPhases.OAUTH_PHASE_3, headers);
+ }
+
+ /**
+ * Methods create and send http GET request to given url.
+ * @param url link, where request is sent
+ * @param accept string with accept content definition (e.g. "text/html")
+ * @param user user login used in basic authorization
+ * @param password user password used in basic authorization
+ * @param accessor used in OAuth authorization, contains consumer information and tokens
+ * @param phase OAuth authorization phase
+ * @param headers the headers which will be added to the request
+ * @return http response from successful request, otherwise return null
+ * @throws IOException
+ * @throws ClientProtocolException
+ */
+ public static HttpResponse get(String url, String accept, String content,
+ String user, String password, OAuthAccessor accessor, String verifier, OAuthPhases phase, Map headers)
+ throws ClientProtocolException, IOException {
+ String currentMethod = "get";
+
+ DefaultHttpClient client = new DefaultHttpClient();
+ client = wrapClient(client);
+
+ HttpGet httpget = new HttpGet(url);
+
+
+
+ httpget.addHeader("OSLC-Core-Version", "2.0");
+ if (accept != null)
+ httpget.addHeader("Accept", accept);
+ if (content != null)
+ httpget.addHeader("Content-Type", content);
+
+ if (accessor != null) {
+ httpget.addHeader(
+ "Authorization", getOAuthAuthorizationHeader(url, "GET", accessor, verifier, phase));
+ }
+ else if (user != null && password != null) {
+ StringBuilder builder = new StringBuilder();
+ httpget.addHeader("Authorization", "Basic " +
+ javax.xml.bind.DatatypeConverter.printBase64Binary(
+ builder.append(user).append(':').append(password).toString().getBytes()));
+ }
+
+ if(headers != null){
+ Set> entrySet = headers.entrySet();
+ for (Entry entry : entrySet) {
+ httpget.addHeader(entry.getKey(), entry.getValue());
+ }
+ }
+
+ try {
+ String logMessage =LogUtils.createLogMessage(url, null, httpget.getAllHeaders(),"GET");
+ logger.debug(logMessage);
+ HttpResponse response = client.execute(httpget);
+ logResponse(response, url, "GET");
+ return response;
+
+ }
+ catch (ClientProtocolException e) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ throw e;
+ }
+ catch (IOException e) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ throw e;
+ }
+ }
+
+ /**
+ * Methods creates and send http POST request to given url.
+ * @param url link, where request is sent
+ * @param body body of request, which will be sent
+ * @param accept accept string with accept content definition (e.g. "text/html")
+ * @param content content string with body format definition (e.g. "text/html")
+ * @param user user login used in basic authorization
+ * @param password user password used in basic authorization
+ * @return http response from successful request, otherwise returns null
+ * @throws IOException
+ * @throws ClientProtocolException
+ */
+ public static HttpResponse post(String url, String body, String accept, String content,
+ String user, String password) throws ClientProtocolException, IOException {
+ return post(url, body, accept, content, user, password, null, null, OAuthPhases.OAUTH_PHASE_3);
+ }
+
+ /**
+ * Methods creates and send http POST request to given url.
+ * @param url link, where request is sent
+ * @param body body of request, which will be sent
+ * @param accept accept string with accept content definition (e.g. "text/html")
+ * @param content content string with body format definition (e.g. "text/html")
+ * @param user user login used in basic authorization
+ * @param password user password used in basic authorization
+ * @param accessor used in OAuth authorization, contains consumer information and tokens
+ * @param phase OAuth authorization phase
+ * @return http response from successful request, otherwise returns null
+ * @throws IOException
+ * @throws ClientProtocolException
+ */
+ public static HttpResponse post(String url, String body, String accept, String content,
+ String user, String password, OAuthAccessor accessor, String verifier, OAuthPhases phase)
+ throws ClientProtocolException, IOException {
+ String currentMethod = "post";
+ DefaultHttpClient client = new DefaultHttpClient();
+ client = wrapClient(client);
+
+ HttpPost httpPost = new HttpPost(url);
+
+ httpPost.addHeader("OSLC-Core-Version", "2.0");
+ if (accept != null)
+ httpPost.addHeader("Accept", accept);
+ if (content != null)
+ httpPost.addHeader("Content-Type", content);
+
+ if (accessor != null) {
+ httpPost.addHeader(
+ "Authorization", getOAuthAuthorizationHeader(url, "POST", accessor, verifier, phase));
+ }
+ else if (user != null && password != null) {
+ StringBuilder builder = new StringBuilder();
+ httpPost.addHeader("Authorization", "Basic " +
+ javax.xml.bind.DatatypeConverter.printBase64Binary(
+ builder.append(user).append(':').append(password).toString().getBytes()));
+ }
+
+ try {
+ httpPost.setEntity(new StringEntity(body, "UTF-8"));
+
+ String logMessage =LogUtils.createLogMessage(url, null, httpPost.getAllHeaders(),"POST");
+ logger.debug(logMessage);
+ HttpResponse response = client.execute(httpPost);
+ logResponse(response, url, "POST");
+ return response;
+
+ }
+ catch (ClientProtocolException e) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ throw e;
+ }
+ catch (IOException e) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ throw e;
+ }
+ }
+
+ /**
+ * Methods creates and send http PUT request to given url.
+ * @param url link, where request is sent
+ * @param body body of request, which will be sent
+ * @param accept accept string with accept content definition (e.g. "text/html")
+ * @param content content string with body format definition (e.g. "text/html")
+ * @param user user login used in basic authorization
+ * @param password user password used in basic authorization
+ * @param accessor used in OAuth authorization, contains consumer information and tokens
+ * @param phase OAuth authorization phase
+ * @return http response from successful request, otherwise returns null
+ * @throws IOException
+ * @throws ClientProtocolException
+ */
+ public static HttpResponse put(String url, String body, String accept, String content,
+ String user, String password, OAuthAccessor accessor, String appendix, OAuthPhases phase)
+ throws ClientProtocolException, IOException {
+ return put(url, body, accept, content,
+ user, password, accessor, appendix, phase, null);
+}
+
+ /**
+ * Methods creates and send http PUT request to given url.
+ * @param url link, where request is sent
+ * @param body body of request, which will be sent
+ * @param accept accept string with accept content definition (e.g. "text/html")
+ * @param content content string with body format definition (e.g. "text/html")
+ * @param user user login used in basic authorization
+ * @param password user password used in basic authorization
+ * @param accessor used in OAuth authorization, contains consumer information and tokens
+ * @param phase OAuth authorization phase
+ * @param headers http headers
+ * @return http response from successful request, otherwise returns null
+ * @throws IOException
+ * @throws ClientProtocolException
+ */
+ public static HttpResponse put(String url, String body, String accept, String content,
+ String user, String password, OAuthAccessor accessor, String appendix, OAuthPhases phase, Map headers)
+ throws ClientProtocolException, IOException {
+ String currentMethod = "put";
+
+ DefaultHttpClient client = new DefaultHttpClient();
+ client = wrapClient(client);
+
+ String targetUrl = url;
+ if (appendix != null)
+ targetUrl += appendix;
+
+ HttpPut httpput = new HttpPut(targetUrl);
+
+ httpput.addHeader("OSLC-Core-Version", "2.0");
+ if (accept != null)
+ httpput.addHeader("Accept", accept);
+ if (content != null)
+ httpput.addHeader("Content-Type", content);
+
+
+ if(headers != null){
+ Set> entrySet = headers.entrySet();
+ for (Entry entry : entrySet) {
+ httpput.addHeader(entry.getKey(), entry.getValue());
+ }
+ }
+
+ if (accessor != null) {
+ httpput.addHeader(
+ "Authorization", "OAuth " + getOAuthAuthorizationHeader(targetUrl, "PUT", accessor, null, phase));
+ }
+ else if (user != null && password != null) {
+ StringBuilder builder = new StringBuilder();
+ httpput.addHeader("Authorization", "Basic " +
+ javax.xml.bind.DatatypeConverter.printBase64Binary(
+ builder.append(user).append(':').append(password).toString().getBytes()));
+ }
+
+ try {
+ httpput.setEntity(new StringEntity(body, "UTF-8"));
+ String logMessage =LogUtils.createLogMessage(url, body, httpput.getAllHeaders(),"PUT");
+ logger.debug(logMessage);
+
+ HttpResponse response = client.execute(httpput);
+ logResponse(response, url, "PUT");
+
+ return response;
+
+ }
+ catch (ClientProtocolException e) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ throw e;
+ }
+ catch (IOException e) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ throw e;
+ }
+ }
+
+ private static void logResponse(HttpResponse response, String url, String action){
+ if (response != null && response.getStatusLine() != null) {
+ logger.debug("Response of " + action + " request for uri = " + url + ", responseCode = " + response.getStatusLine().getStatusCode() + ", responsePhrase = " + response.getStatusLine().getReasonPhrase());
+ } else {
+ logger.error("Response of " + action + " request for uri = " + url + " is null");
+ }
+ }
+
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/PluginConfig.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/PluginConfig.java
new file mode 100644
index 0000000..2b00a4e
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/PluginConfig.java
@@ -0,0 +1,155 @@
+package com.ericsson.jira.oslc;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.ericsson.jira.oslc.resources.ao.AOManager;
+
+/**
+ *
+ * The configuration for the plugin
+ * It;s possible to set the list of projects which will be visible from outside (e.g. OSLC catalog contain only projects
+ * in the list). If no project is set then all projects
+ * will be visible from outside. The list contains the IDs of projects, not the names because the name can be changed
+ * It's also possible to set the list of issue types. The rule is the same as for the projects.
+ *
+ *
+ */
+public class PluginConfig {
+ private Set filteredProjects;
+ private Set filteredTypes;
+ private static PluginConfig instance;
+ public static final String FILTERED_PROJECTS = "filteredProjects";
+ public static final String FILTERED_TYPES = "filteredTypes";
+
+ public static PluginConfig getInstance() throws Exception {
+ if (instance == null) {
+ instance = new PluginConfig(true);
+ }
+ return instance;
+ }
+
+ public static PluginConfig getInstance(boolean loadConfig) throws Exception {
+ if (instance == null) {
+ instance = new PluginConfig(loadConfig);
+ }
+ return instance;
+ }
+
+ /**
+ * It creates empty configuration and if the argument loadConfig is set on 'true' then the configuration
+ * is loaded from DB
+ * @param loadConfig the sync configuration will be loaded from DB, false - it creates empty configuration
+ * @throws Exception
+ */
+ private PluginConfig(boolean loadConfig) throws Exception {
+ filteredProjects = new HashSet();
+ filteredTypes = new HashSet();
+ if(loadConfig){
+ loadConfiguration();
+ }
+ }
+
+ /**
+ * It loads plugin configuration from DB
+ * @throws Exception
+ */
+ private void loadConfiguration() throws Exception {
+
+ AOManager mngr = AOManager.getInstance();
+ Map configValues = mngr.getConfigValues();
+ if (configValues != null) {
+ loadFilter(filteredProjects, configValues.get(FILTERED_PROJECTS));
+ loadFilter(filteredTypes, configValues.get(FILTERED_TYPES));
+ }else{
+ filteredProjects.clear();
+ filteredTypes.clear();
+ }
+ }
+
+ /**
+ * It creates sync configuration from defined configuration which is stored in the argument inputConfiguration
+ * @param inputFilteredProjects the list of ids of projects separated by comma
+ * @param inputFilteredTypes the list of ids of issue types separated by comma
+ * @throws Exception
+ */
+ public void loadConfiguration(String inputFilteredProjects, String inputFilteredTypes) throws Exception {
+ if (inputFilteredProjects != null && !inputFilteredProjects.isEmpty()) {
+ loadFilter(filteredProjects, inputFilteredProjects);
+ }else{
+ filteredProjects.clear();
+ }
+
+ if (inputFilteredTypes != null && !inputFilteredTypes.isEmpty()) {
+ loadFilter(filteredTypes, inputFilteredTypes);
+ }else{
+ filteredTypes.clear();
+ }
+ }
+
+ /**
+ * Parses the value of argument input containing the list of projects/issue types separated by comma.
+ * The set of project/issue types saves to argument filter
+ * @param filter the set of projects/issue types extracted from input
+ * @param input the list of projects/issue types separated by comma
+ * @throws Exception
+ */
+ public void loadFilter(Setfilter, String input) throws Exception {
+ filter.clear();
+ if (input != null && !input.isEmpty()) {
+ String[] splited = input.trim().split(",");
+ for (String str : splited) {
+ str= str.trim();
+ if(!str.isEmpty()){
+ filter.add(new Long(str));
+ }
+ }
+ }
+ }
+
+ public Set getFilteredProjects() {
+ return filteredProjects;
+ }
+
+ public void setFilteredProjects(Set filteredProjects) {
+ this.filteredProjects = filteredProjects;
+ }
+
+ public Set getFilteredTypes() {
+ return filteredTypes;
+ }
+
+ public void setFilteredTypes(Set filteredTypes) {
+ this.filteredTypes = filteredTypes;
+ }
+
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/constants/JiraConstants.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/constants/JiraConstants.java
new file mode 100644
index 0000000..f982b54
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/constants/JiraConstants.java
@@ -0,0 +1,77 @@
+package com.ericsson.jira.oslc.constants;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+public interface JiraConstants {
+ public static final String REST_URL="/rest/jirarestresource/1.0/";
+ public static final String ACTIVATE_CONSUMER_URL="oauth/consumers/activate/";
+ public static final String REMOVE_CONSUMER_URL="oauth/consumers/remove/";
+ public static final String REMOVE_RS_LINK = "oauth/rootserviceslinks/removelink/";
+ public static final String REMOVE_SP_CATALOG_LINK = "oauth/serviceprovidercatalogslinks/removelink/";
+ public static final String REMOVE_SERVICEPROVIDER_LINK = "oauth/serviceproviderslinks/removelink/";
+ public static final String OAUTH_CONSUMER_PAGE_URL="/plugins/servlet/jiraservlet/OAuthConsumerServlet";
+ public static final String ROOTSEERVICES_MANAGEMENTT_PAGE = "/plugins/servlet/jiraservlet/RootServicesManagementServlet";
+ public static final String CATALOGS_MANAGEMENT_PAGE = "/plugins/servlet/jiraservlet/ServiceProviderCatalogsManagementServlet";
+ public static final String PROJECT_RELATIONSHIPS_PAGE = "/plugins/servlet/jiraservlet/ProjectRelationshipsServlet";
+ public static final String ISSUE_TYPE_PATH = "issueTypes/";
+ public static final String ISSUE_PRIORITY_PATH = "issuePriorities/";
+ public static final String ISSUE_STATUS_PATH = "issueStates/";
+ public static final String ISSUE_RESOLUTION_PATH = "issueResolutions/";
+ public static final String CREATION_DIALOG_WIDTH="900px";
+ public static final String CREATION_DIALOG_HEIGHT="600px";
+ public static final String SELECTION_DIALOG_WIDTH="900px";
+ public static final String SELECTION_DIALOG_HEIGHT="600px";
+ public static final String CM_CHANGE_REQUEST= "http://open-services.net/ns/cm#ChangeRequest";
+ public static final String CREATE_ISSUE = "/plugins/servlet/jiraservlet/createissue";
+ public static final String SELECT_ISSUE = "/plugins/servlet/jiraservlet/selectissue";
+ public static final String REMOVE_OSLC_LINK_FROM_REMOTE_APP = "oslc/links/removeFromRemoteApp/";
+ public static final String REMOVE_OSLC_LINK_FROM_JIRA = "oslc/links/removeFromJira/";
+ public static final String ADD_OSLC_LINK_DIALOG="/plugins/servlet/jiraservlet/addoslclinkdialog";
+ public static final String ADD_OSLC_LINK_TO_REMOTE_APP = "oslc/links/addToRemoteApp/";
+ public static final String ADD_OSLC_LINK_TO_JIRA = "oslc/links/addToJira/";
+ public static final String OSLC_CUSTOM_FIELD_NAME="External Links";
+ public static final String OSLC_CUSTOM_FIELD_LABEL="Label";
+ public static final String OSLC_CUSTOM_FIELD_URI="URI";
+ public static final String GET_OSLC_LINK_TYPES="oslc/links/types/";
+ public static final String OSLC_RESPONSE_TYPE_1 = "#oslc-windowName-1.0";
+ public static final String OSLC_RESPONSE_TYPE_2 = "#oslc-postMessage-1.0";
+ public static final String OSLC_RESPONSE_TYPE_3 = "#oslc-core-windowName-1.0";
+ public static final String OSLC_RESPONSE_TYPE_4 = "#oslc-core-postMessage-1.0";
+ public static final String SESSION_OAUTHACCESSOR = "oAuthAccessor";
+ public static final String SESSION_CURRENT_LINK = "currentOperationLink";
+ public static final String OAUTH_CALLBACK_SERVICE_URL = REST_URL + "oauth/authorizationcallback";
+ public static final String OAUTH_EXT_CALLBACK_SERVICE_URL = REST_URL + "oauth/authorizationexternalcallback";
+ public static final String RELATED_CHANGE_REQUEST_URL_APPENDIX = "?oslc.properties=oslc_cm%3ArelatedChangeRequest&oslc.prefix=oslc_cm%3D%3Chttp%3A%2F%2Fopen-services.net%2Fns%2Fcm%23%3E";
+ public static final String ISSUE_ICON = "/images/icons/favicon.png";
+ public static final String REALM_NAME = "JIRA";
+ public static final String SYNC_HEADER_NAME = "LeanSync";
+ public static final int SINGLE_TEXT_LIMIT = 255;
+ public static final String XSD_PATH = "/config/leanSyncConfig.xsd";
+
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/customfields/OSLCLink.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/customfields/OSLCLink.java
new file mode 100644
index 0000000..7eb2e56
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/customfields/OSLCLink.java
@@ -0,0 +1,120 @@
+package com.ericsson.jira.oslc.customfields;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.atlassian.jira.component.ComponentAccessor;
+import com.atlassian.jira.issue.CustomFieldManager;
+import com.atlassian.jira.issue.Issue;
+import com.atlassian.jira.issue.customfields.impl.GenericTextCFType;
+import com.atlassian.jira.issue.customfields.manager.GenericConfigManager;
+import com.atlassian.jira.issue.customfields.persistence.CustomFieldValuePersister;
+import com.atlassian.jira.issue.fields.CustomField;
+import com.atlassian.jira.issue.fields.layout.field.FieldLayoutItem;
+import com.atlassian.jira.security.Permissions;
+import com.atlassian.jira.user.ApplicationUser;
+import com.ericsson.jira.oslc.constants.JiraConstants;
+import com.ericsson.jira.oslc.exceptions.PermissionException;
+import com.ericsson.jira.oslc.managers.JiraManager;
+import com.ericsson.jira.oslc.managers.PermissionManager;
+import com.ericsson.jira.oslc.utils.AppLinksRepository;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * It represents the link to remote resource.
+ *
+ */
+public class OSLCLink extends GenericTextCFType {
+ private static final String CURRENT_CLASS = "OSLCLink";
+ private static final Logger logger = LoggerFactory.getLogger(OSLCLink.class);
+
+ protected OSLCLink(CustomFieldValuePersister customFieldValuePersister, GenericConfigManager genericConfigManager) {
+ super(customFieldValuePersister, genericConfigManager);
+ }
+
+ @Override
+ public Map getVelocityParameters(final Issue issue, final CustomField field, final FieldLayoutItem fieldLayoutItem) {
+ final Map map = super.getVelocityParameters(issue, field, fieldLayoutItem);
+
+ CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager();
+
+
+ if (issue == null || issue.getId() == null) {
+ return map;
+ }
+
+ CustomField customField = customFieldManager.getCustomFieldObjectByName(JiraConstants.OSLC_CUSTOM_FIELD_NAME);
+
+ if (customField == null) {
+ return map;
+ }
+
+ String ApplicationLinks = (String) customField.getValue(issue);
+
+ AppLinksRepository appLinkList = new AppLinksRepository();
+ try {
+ GsonBuilder gsonBuilder = new GsonBuilder();
+ Gson gson = gsonBuilder.create();
+ appLinkList = gson.fromJson(ApplicationLinks, AppLinksRepository.class);
+
+ } catch (com.google.gson.JsonSyntaxException e) {
+ logger.error(CURRENT_CLASS, e);
+ }
+
+ if (appLinkList == null) {
+ appLinkList = new AppLinksRepository();
+ }
+
+ ApplicationUser user = PermissionManager.getLoggedUser();
+ boolean editable = false;
+ if (user != null){
+ try {
+ PermissionManager.checkPermissionWithUser(user, issue, Permissions.EDIT_ISSUE);
+ editable = true;
+ } catch (PermissionException e) {
+ editable = false;
+ }
+ }
+ map.put("editable", editable);
+ map.put("restURL", JiraManager.getRestUrl());
+ map.put("baseURL", JiraManager.getBaseUrl());
+ map.put("link_addoslclinkdialog", JiraManager.getBaseUrl() + JiraConstants.ADD_OSLC_LINK_DIALOG + "?issuekey=" + issue.getKey());
+ map.put("removeOslcLinkURLFromRemoteApp", JiraConstants.REMOVE_OSLC_LINK_FROM_REMOTE_APP);
+ map.put("removeOslcLinkURLFromJira", JiraConstants.REMOVE_OSLC_LINK_FROM_JIRA);
+ map.put("issueID", issue.getId().toString());
+ map.put("appLinkList", appLinkList.GetAllAppLinks());
+ map.put("oauthcallback", JiraManager.getBaseUrl() + JiraConstants.OAUTH_CALLBACK_SERVICE_URL);
+
+ return map;
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/events/IssueEventType.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/events/IssueEventType.java
new file mode 100644
index 0000000..736cdd3
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/events/IssueEventType.java
@@ -0,0 +1,36 @@
+package com.ericsson.jira.oslc.events;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * Enum for type of events which are not fired automatically by JIRA and has to be fired manually.
+ *
+ */
+public enum IssueEventType {
+ ADD_EXT_LINK, REMOVE_EXT_LINK;
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/events/IssueEventsHandler.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/events/IssueEventsHandler.java
new file mode 100644
index 0000000..fe99446
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/events/IssueEventsHandler.java
@@ -0,0 +1,302 @@
+package com.ericsson.jira.oslc.events;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.ofbiz.core.entity.GenericEntityException;
+import org.ofbiz.core.entity.GenericValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+
+import com.atlassian.event.api.EventListener;
+import com.atlassian.event.api.EventPublisher;
+import com.atlassian.jira.event.issue.IssueEvent;
+import com.atlassian.jira.event.type.EventType;
+import com.atlassian.jira.issue.Issue;
+import com.atlassian.jira.project.Project;
+import com.ericsson.eif.leansync.mapping.data.SyncConfiguration;
+import com.ericsson.eif.leansync.mapping.data.SyncField;
+import com.ericsson.eif.leansync.mapping.data.SyncMapping;
+import com.ericsson.eif.leansync.mapping.data.SyncTemplate;
+import com.ericsson.eif.leansync.mapping.data.SyncXmlFieldConfig;
+import com.ericsson.jira.oslc.sync.JiraObjectMapping;
+import com.ericsson.jira.oslc.sync.OutboundSyncUtils;
+import com.ericsson.jira.oslc.sync.SyncConfig;
+import com.ericsson.jira.oslc.sync.SyncUtils;
+import com.ericsson.jira.oslc.utils.ErrorSyncHandler;
+/**
+ *
+ * A Listener which is called whenever events occur on JIRA issue
+ */
+public class IssueEventsHandler implements InitializingBean, DisposableBean {
+ private static Logger logger = LoggerFactory.getLogger(IssueEventsHandler.class);
+ private static final String CURRENT_CLASS = "JiraIssueEventsHandler";
+ private final EventPublisher eventPublisher;
+
+ /**
+ * Constructor.
+ *
+ * @param eventPublisher injected {@code EventPublisher} implementation.
+ */
+ public IssueEventsHandler(EventPublisher eventPublisher) {
+ this.eventPublisher = eventPublisher;
+ }
+
+ @Override
+ public void destroy() throws Exception {
+ this.eventPublisher.unregister(this);
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ this.eventPublisher.register(this);
+ }
+
+ /**
+ * Receives any {@code IssueEvent}s sent by JIRA.
+ *
+ * @param issueEvent the IssueEvent passed to us
+ */
+ @EventListener
+ public void onIssueEvent(IssueEvent issueEvent) {
+ Issue issue = issueEvent.getIssue();
+
+ if (issue == null) {
+ logger.debug(CURRENT_CLASS + ".onIssueEvent for issue null");
+ return;
+ }
+
+ logger.debug(CURRENT_CLASS + ".onIssueEvent: IssueEvent for issue "
+ + issue.getKey());
+
+
+ try {
+ Project project = issueEvent.getProject();
+
+ if(EventType.ISSUE_CREATED_ID.equals(issueEvent.getEventTypeId())){
+ issueEvent = null;
+ }
+ updateSnapshot(issue, project, issueEvent);
+ } catch (Exception e) {
+ logger.error("CURRENT_CLASS", e);
+ }
+ }
+
+ /**
+ * Receives any {@code IssueEvent}s sent by JIRA.
+ *
+ * @param issueEvent the RestIssueEvent passed to us. It's custom event which is fired from our code manually
+ */
+ @EventListener
+ public void onIssueEvent(RestIssueEvent event) {
+ Issue issue = event.getIssue();
+ if(issue == null){
+ logger.error(CURRENT_CLASS + ".onIssueEvent: RestIssueEvent for issue null");
+ return;
+ }else{
+ logger.debug(CURRENT_CLASS + ".onIssueEvent: RestIssueEvent for issue "
+ + issue.getKey());
+ }
+
+
+ Project project = issue.getProjectObject();
+ try {
+ updateSnapshot(issue, project, null);
+ } catch (Exception e) {
+ logger.error("CURRENT_CLASS", e);
+ }
+ }
+
+ /**
+ * It update the JIRA snapshot in a remote resource
+ * @param issue JIRA issue which fires a event
+ * @param project the project of JIRA issue
+ * @param issueEvent the event which was fired
+ * @throws Exception
+ */
+ private void updateSnapshot(Issue issue, Project project, IssueEvent issueEvent) throws Exception {
+
+ SyncConfiguration leanSyncConfig = getLeanSyncConfiguration(issue, project);
+ if (leanSyncConfig == null) {
+ return;
+ }
+
+ ErrorSyncHandler errorHandler = new ErrorSyncHandler();
+
+ try {
+ SyncMapping firstInMapping = leanSyncConfig.getFirstInMapping();
+ if (firstInMapping == null) {
+ logger.debug(CURRENT_CLASS + ".updateSnapshot: " + " In mapping is null");
+ return;
+ }
+
+ List templates = firstInMapping.getTemplates();
+ List fields = firstInMapping.getFields();
+ List xmlFieldConfigs = firstInMapping.getXmlFieldConfigs();
+ if (allowUpdate(issueEvent, templates, fields, xmlFieldConfigs, leanSyncConfig.getErrorLog())) {
+ OutboundSyncUtils.updateRemoteResource(issue, leanSyncConfig, errorHandler);
+ } else {
+ logger.debug(CURRENT_CLASS + ".updateSnapshot: " + " Update remote resource " + issue.getKey() + " was not allowed.");
+ }
+ } catch (Exception e) {
+ errorHandler.addMessage(e.getMessage());
+ logger.error("CURRENT_CLASS", e);
+ } finally {
+ //Save error log to error custom field if it's configured
+ String errorMessage = (errorHandler != null && !errorHandler.isLogEmpty())?errorHandler.getMessagesAsString():null;
+ SyncUtils.updateErrorLog(leanSyncConfig, issue, errorMessage, false);
+ }
+
+ }
+
+ /**
+ * It checks if the update of the remote resource is allowed. It verifies if the fields
+ * which have been changed are in the notification list.
+ * @param issueEvent the event which was fired
+ * @param templates the list of templates
+ * @param fields the list of configurations of fields
+ * @param xmlFieldConfigs the list of configurations of xml fields
+ * @param errorLogName the name of Error custom field
+ * @return true - the remote resource can be updated, otherwise false
+ * @throws GenericEntityException
+ */
+ private boolean allowUpdate(IssueEvent issueEvent, List templates, List fields, List xmlFieldConfigs, String errorLogName)
+ throws GenericEntityException {
+ if(issueEvent == null || issueEvent.getWorklog() != null){
+ return true;
+ }
+
+ Set notNotifiedFields = new HashSet();
+
+ if (errorLogName != null && !"".equals(errorLogName)) {
+ notNotifiedFields.add(errorLogName);
+ }
+
+ if (templates != null) {
+ for (SyncTemplate template : templates) {
+ String mapTo = template.getMapTo();
+ if (mapTo != null && !"".equals(mapTo) && !template.isNotifyChange()) {
+ notNotifiedFields.add(mapTo);
+ }
+ }
+ }
+
+ if (fields != null) {
+ for (SyncField field : fields) {
+ String mapTo = field.getMapTo();
+ if (mapTo != null && !"".equals(mapTo) && !field.isNotifyChange()) {
+ notNotifiedFields.add(mapTo);
+ }
+ }
+ }
+
+ if (xmlFieldConfigs != null) {
+ for (SyncXmlFieldConfig xmlFieldConfig : xmlFieldConfigs) {
+ for (SyncField field : xmlFieldConfig.getFields()) {
+ String mapTo = field.getMapTo();
+ if (mapTo != null && !"".equals(mapTo) && !field.isNotifyChange()) {
+ notNotifiedFields.add(mapTo);
+ }
+ }
+ }
+ }
+
+ if(issueEvent.getComment() != null && !notNotifiedFields.contains(JiraObjectMapping.COMMENTS.getName())){
+ return true;
+ }
+
+ GenericValue changeLog = issueEvent.getChangeLog();
+ if (changeLog != null) {
+ List changeItemList = changeLog.getRelated("ChildChangeItem");
+ if (changeItemList != null) {
+ if (notNotifiedFields.size() < 1 && changeItemList.size() > 0) {
+ return true;
+ }
+ for (GenericValue changeItem : changeItemList) {
+ String name = changeItem.get("field").toString();
+ String fieldType = changeItem.get("fieldtype").toString();
+ if (fieldType != null && "jira".equals(fieldType)) {
+ name = JiraObjectMapping.getFieldIdsToLabels().get(name);
+ }
+ if (!notNotifiedFields.contains(name)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Fetch LeanSync Configuration based on project and issue type
+ * @param issue JIRA issue which has been changed
+ * @param project the project of JIRA issue
+ * @return LeanSync configuration fro the issue
+ * @throws Exception
+ */
+ public SyncConfiguration getLeanSyncConfiguration(Issue issue, Project project) throws Exception {
+ Long projectId = null;
+ if (project != null && project.getId() != null ) {
+ projectId = project.getId();
+ }else{
+ logger.debug(CURRENT_CLASS + ".onIssueEvent: event " + " for issue " + issue.getKey() + " - project is null");
+ return null;
+ }
+
+ String issueTypeId = null;
+ if (issue.getIssueTypeObject() != null && issue.getIssueTypeObject().getId() != null) {
+ issueTypeId = issue.getIssueTypeObject().getId();
+ } else {
+ logger.debug(CURRENT_CLASS + ".updateSnapshot: " + " for issue " + issue.getKey() + " - issue type is null");
+ return null;
+ }
+
+ SyncConfig config = SyncConfig.getInstance();
+ Map fieldConfMap = config.getConfigurationMap();
+
+ logger.debug(CURRENT_CLASS + ".onIssueEvent: event " + " for issue " + issue.getKey() + " - project is " + projectId + " - issueType is " + issueTypeId);
+ if (projectId != null && fieldConfMap != null) {
+ SyncConfiguration leanSyncConfig = fieldConfMap.get(projectId.toString());
+
+ if (leanSyncConfig != null && issueTypeId != null && leanSyncConfig.containsIssueType(issueTypeId)) {
+ logger.debug(CURRENT_CLASS + ".getLeanSyncConfiguration: " + " Configuration found");
+ return leanSyncConfig;
+ }
+ }else{
+ logger.debug(CURRENT_CLASS + ".getLeanSyncConfiguration: " + " Configuration not found for project " + projectId + " and issueType " + issueTypeId);
+ }
+
+ return null;
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/events/RestIssueEvent.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/events/RestIssueEvent.java
new file mode 100644
index 0000000..f4678e4
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/events/RestIssueEvent.java
@@ -0,0 +1,76 @@
+package com.ericsson.jira.oslc.events;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.Map;
+
+import com.atlassian.crowd.embedded.api.User;
+import com.atlassian.jira.event.AbstractEvent;
+import com.atlassian.jira.issue.Issue;
+
+/**
+ * It represents custom event which are fired manually from the code e.g. when
+ * external link is added.
+ *
+ */
+public class RestIssueEvent extends AbstractEvent {
+ private Issue issue;
+ private User user;
+ private IssueEventType type;
+
+ public RestIssueEvent(final Issue issue, final User user, IssueEventType type) {
+ super();
+ this.issue = issue;
+ this.user = user;
+ this.type = type;
+ }
+
+ public RestIssueEvent(final Issue issue, final User user, Map parameters) {
+ super(parameters);
+ this.issue = issue;
+ this.user = user;
+ }
+
+ public Issue getIssue() {
+ return issue;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public IssueEventType getType() {
+ return type;
+ }
+
+ public void setType(IssueEventType type) {
+ this.type = type;
+ }
+
+
+}
\ No newline at end of file
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/GetIssueException.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/GetIssueException.java
new file mode 100644
index 0000000..a0d8fc5
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/GetIssueException.java
@@ -0,0 +1,53 @@
+package com.ericsson.jira.oslc.exceptions;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The exception is thrown when a issue is not available
+ *
+ */
+public class GetIssueException extends Exception {
+
+ private static final long serialVersionUID = -1945771994194731300L;
+
+ public GetIssueException() {
+ }
+
+ public GetIssueException(String message) {
+ super(message);
+ }
+
+ public GetIssueException(Throwable cause) {
+ super(cause);
+ }
+
+ public GetIssueException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
\ No newline at end of file
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/IssueTransitionException.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/IssueTransitionException.java
new file mode 100644
index 0000000..cc39679
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/IssueTransitionException.java
@@ -0,0 +1,51 @@
+package com.ericsson.jira.oslc.exceptions;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The exception is thrown when transition of issue failed
+ *
+ */
+public class IssueTransitionException extends Exception{
+ private static final long serialVersionUID = 8985395102999065444L;
+
+ public IssueTransitionException(){
+ }
+
+ public IssueTransitionException(String message){
+ super(message);
+ }
+
+ public IssueTransitionException(Throwable cause){
+ super(cause);
+ }
+
+ public IssueTransitionException(String message, Throwable cause){
+ super(message, cause);
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/IssueValidationException.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/IssueValidationException.java
new file mode 100644
index 0000000..e9973ac
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/IssueValidationException.java
@@ -0,0 +1,52 @@
+package com.ericsson.jira.oslc.exceptions;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The exception is thrown when error occurs during a validation
+ *
+ */
+public class IssueValidationException extends Exception{
+ private static final long serialVersionUID = -8581963580698927791L;
+
+ public IssueValidationException(){
+ }
+
+ public IssueValidationException(String message){
+ super(message);
+ }
+
+ public IssueValidationException(Throwable cause){
+ super(cause);
+ }
+
+ public IssueValidationException(String message, Throwable cause){
+ super(message, cause);
+ }
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/NoResourceException.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/NoResourceException.java
new file mode 100644
index 0000000..063359d
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/NoResourceException.java
@@ -0,0 +1,52 @@
+package com.ericsson.jira.oslc.exceptions;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The exception is thrown when a resource is not available
+ *
+ */
+public class NoResourceException extends Exception {
+
+ private static final long serialVersionUID = 1583709001536069481L;
+
+ public NoResourceException(){
+ }
+
+ public NoResourceException(String message){
+ super(message);
+ }
+
+ public NoResourceException(Throwable cause){
+ super(cause);
+ }
+
+ public NoResourceException(String message, Throwable cause){
+ super(message, cause);
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/PermissionException.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/PermissionException.java
new file mode 100644
index 0000000..314cd66
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/PermissionException.java
@@ -0,0 +1,51 @@
+package com.ericsson.jira.oslc.exceptions;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The exception is thrown when permission problem occurs
+ *
+ */
+public class PermissionException extends Exception {
+ private static final long serialVersionUID = 2207461020664304332L;
+
+ public PermissionException(){
+ }
+
+ public PermissionException(String message){
+ super(message);
+ }
+
+ public PermissionException(Throwable cause){
+ super(cause);
+ }
+
+ public PermissionException(String message, Throwable cause){
+ super(message, cause);
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/PreconditionException.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/PreconditionException.java
new file mode 100644
index 0000000..7a85441
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/PreconditionException.java
@@ -0,0 +1,52 @@
+package com.ericsson.jira.oslc.exceptions;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The exception is thrown when a precondition failed
+ *
+ */
+public class PreconditionException extends Exception {
+
+ private static final long serialVersionUID = 4468614532312055881L;
+
+ public PreconditionException(){
+ }
+
+ public PreconditionException(String message){
+ super(message);
+ }
+
+ public PreconditionException(Throwable cause){
+ super(cause);
+ }
+
+ public PreconditionException(String message, Throwable cause){
+ super(message, cause);
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/StatusException.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/StatusException.java
new file mode 100644
index 0000000..b142282
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/exceptions/StatusException.java
@@ -0,0 +1,53 @@
+package com.ericsson.jira.oslc.exceptions;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The exception is thrown when the validations or the operations with the issue failed in LeanSync.
+ *
+ */
+public class StatusException extends Exception {
+ private static final long serialVersionUID = 9139099220017790017L;
+
+
+ public StatusException(){
+ }
+
+ public StatusException(String message){
+ super(message);
+ }
+
+ public StatusException(Throwable cause){
+ super(cause);
+ }
+
+ public StatusException(String message, Throwable cause){
+ super(message, cause);
+ }
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/handlers/OAuthHandler.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/handlers/OAuthHandler.java
new file mode 100644
index 0000000..929bc4f
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/handlers/OAuthHandler.java
@@ -0,0 +1,297 @@
+package com.ericsson.jira.oslc.handlers;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpSession;
+
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthConsumer;
+import net.oauth.OAuthServiceProvider;
+
+import org.eclipse.lyo.server.oauth.core.OAuthConfiguration;
+import org.eclipse.lyo.server.oauth.core.consumer.ConsumerStore;
+import org.eclipse.lyo.server.oauth.core.consumer.ConsumerStoreException;
+import org.eclipse.lyo.server.oauth.core.consumer.LyoOAuthConsumer;
+import org.eclipse.lyo.server.oauth.core.token.LRUCache;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ericsson.jira.oslc.constants.JiraConstants;
+import com.ericsson.jira.oslc.oslcclient.Client;
+import com.ericsson.jira.oslc.resources.RootServices;
+import com.ericsson.jira.oslc.resources.ao.AOConsumerStore;
+import com.ericsson.jira.oslc.resources.ao.AOManager;
+import com.ericsson.jira.oslc.resources.ao.RootServicesEntity;
+import com.ericsson.jira.oslc.utils.OSLCUtils;
+
+/**
+ * A class contains the methods for work with OAuth consumer store, Consumer, Accessor and other OAuth stuff.
+ *
+ */
+public class OAuthHandler {
+ private static Logger logger = LoggerFactory.getLogger(OAuthHandler.class);
+ private static final String CURRENT_CLASS = "OAuthHandler";
+ private static LRUCache rootServicesCache = new LRUCache (10);
+
+ /**
+ * It loads the consumer store and saves it to OAuth configuration
+ * @param config OAuth configuration where the cosnumer store will be put to
+ * @throws ConsumerStoreException
+ * @throws ClassNotFoundException
+ * @throws SQLException
+ */
+ public static void loadOAuthConsumers(OAuthConfiguration config) throws ConsumerStoreException, ClassNotFoundException, SQLException {
+ config.setConsumerStore(loadConsumerStore());
+ logger.info("OAuth consumers added");
+ }
+
+ /**
+ * Loads the consumer store from DB
+ * @return the consumer store
+ * @throws ConsumerStoreException
+ * @throws ClassNotFoundException
+ * @throws SQLException
+ */
+ private static ConsumerStore loadConsumerStore() throws ConsumerStoreException, ClassNotFoundException, SQLException{
+ AOConsumerStore aoConsumerStore = new AOConsumerStore();
+ return aoConsumerStore;
+ }
+
+ /**
+ * Adds a consumer to the consumer store
+ * @param consumer the consumer containing its name, if it's provisional
+ * @return consumer store containing all the consumers including added consumer
+ * @throws ConsumerStoreException
+ * @throws ClassNotFoundException
+ * @throws SQLException
+ */
+ public static ConsumerStore addConsumer(LyoOAuthConsumer consumer) throws ConsumerStoreException, ClassNotFoundException, SQLException{
+ String currentMethod = "addConsumer";
+ if(consumer == null){
+ logger.debug(CURRENT_CLASS + "." + currentMethod + " Consumer is null");
+ return loadConsumerStore();
+ }
+
+ ConsumerStore consumerStore = loadConsumerStore();
+ consumerStore.addConsumer(consumer);
+
+ logger.debug(CURRENT_CLASS + "." + currentMethod + " consumer added");
+ return consumerStore;
+ }
+
+ /**
+ * Removes a consumer defined by consumerKey.
+ * @param consumerKey the key of consumer
+ * @return consumer store containing all the consumers without removed consumer
+ * @throws ClassNotFoundException
+ * @throws SQLException
+ * @throws ConsumerStoreException
+ */
+ public static ConsumerStore removeConsumer(String consumerKey) throws ClassNotFoundException, SQLException, ConsumerStoreException {
+ String currentMethod = "removeConsumer";
+ logger.debug(CURRENT_CLASS + "." + currentMethod + " Consumer key: " + consumerKey);
+
+ ConsumerStore consumerStore = loadConsumerStore();
+ if (consumerKey != null) {
+ consumerStore.removeConsumer(consumerKey);
+ }
+
+ logger.debug(CURRENT_CLASS + "." + currentMethod + " consumer removed");
+ return consumerStore;
+
+ }
+
+ /**
+ * Updates a consumer defined by consumerKey. It's needed to specify if the consumer is provisional or not
+ * @param consumerKey the key of consumer
+ * @param provisional specifies if the consumer is provisional or
+ * @return consumer store containing all the consumers including updated the consumer
+ * @throws ClassNotFoundException
+ * @throws SQLException
+ * @throws ConsumerStoreException
+ */
+ public static ConsumerStore updateConsumer(String consumerKey, boolean provisional) throws ClassNotFoundException, SQLException, ConsumerStoreException {
+ String currentMethod = "updateConsumer";
+ logger.debug(CURRENT_CLASS + "." + currentMethod + " Consumer key: " + consumerKey + ", provisional = " + provisional);
+
+ ConsumerStore consumerStore = loadConsumerStore();
+ if (consumerKey != null) {
+ LyoOAuthConsumer consumer = consumerStore.getConsumer(consumerKey);
+ consumer.setProvisional(provisional);
+ consumerStore.updateConsumer(consumer);
+ }
+
+ logger.debug(CURRENT_CLASS + "." + currentMethod + " consumer updated");
+ return consumerStore;
+ }
+
+ /**
+ * Returns the consumer for entered resource. It goes through all registered
+ * rootservices and tries to match oAuthDomain of rootservices with URI of
+ * entered resource
+ *
+ * @param resourceURI uri of Resource
+ * @return consumer for entered resource
+ */
+ public static OAuthConsumer getConsumer(String resourceURI) {
+ AOManager mngr = AOManager.getInstance();
+ List rootServicesList = mngr.all(RootServicesEntity.class);
+ List addToCache = new ArrayList();
+
+ for (RootServicesEntity rootServicesEntity : rootServicesList) {
+ RootServices rootServices = rootServicesCache.get(rootServicesEntity.getRootServicesURI());
+ if (rootServices != null) {
+ if (matchAuthDomain(resourceURI, rootServices.getOAuthDomain())) {
+ return getConsumer(rootServices, rootServicesEntity);
+ }
+ } else {
+ addToCache.add(rootServicesEntity);
+ }
+ }
+
+ for (RootServicesEntity rootServicesEntity : addToCache) {
+
+ Client client = new Client();
+
+ //If entity is root services, then call to Client.getRootServicesDetails.
+ //If entity is catalog, 1st try get values from entity. If values are not set in catalog,
+ //then new Client function will be called, which fetch OAuth values from catalog.
+ RootServices rootServices = null;
+ if (rootServicesEntity.isRootServices() == true) {
+ rootServices = client.getRootServicesDetails(rootServicesEntity.getRootServicesURI());
+ }
+ else {
+ if (OSLCUtils.isNullOrEmpty(rootServicesEntity.getConsumerKey()) == false &&
+ OSLCUtils.isNullOrEmpty(rootServicesEntity.getConsumerSecret()) == false &&
+ OSLCUtils.isNullOrEmpty(rootServicesEntity.getOAuthDomain()) == false &&
+ OSLCUtils.isNullOrEmpty(rootServicesEntity.getRequestTokenURI()) == false &&
+ OSLCUtils.isNullOrEmpty(rootServicesEntity.getAccessTokenURI()) == false &&
+ OSLCUtils.isNullOrEmpty(rootServicesEntity.getUserAuthURI()) == false) {
+
+ rootServices = new RootServices();
+ rootServices.setOAuthAccessTokenURL(rootServicesEntity.getAccessTokenURI());
+ rootServices.setOAuthDomain(rootServicesEntity.getOAuthDomain());
+ rootServices.setOAuthRequestTokenURL(rootServicesEntity.getRequestTokenURI());
+ rootServices.setOAuthUserAuthorizationURL(rootServicesEntity.getUserAuthURI());
+ }
+ else {
+ rootServices = client.getRootServicesDetailsFromCatalog(rootServicesEntity.getRootServicesURI());
+ }
+ }
+
+ if (rootServices != null) {
+ rootServicesCache.put(rootServicesEntity.getRootServicesURI(), rootServices);
+ if (matchAuthDomain(resourceURI, rootServices.getOAuthDomain())) {
+ return getConsumer(rootServices, rootServicesEntity);
+ }
+ }
+ }
+
+ return null;
+
+ }
+
+ /**
+ * Matches entered URI with oAuth domain. If OAuth domain is a part of URI then return true, else false
+ * @param uri uri of resource
+ * @param oAuthDomain OAthDomain of rootservices
+ * @return true if OAuth domain is part of URI, else false
+ */
+ public static boolean matchAuthDomain(String uri, String oAuthDomain) {
+ return (uri != null && !uri.isEmpty() && oAuthDomain != null && !oAuthDomain.isEmpty() && uri.startsWith(oAuthDomain));
+ }
+
+ /**
+ * Creates and returns the consumer based on input parameters
+ * @param rootServices - rootservices which has been fetched from friend's side
+ * @param rootServicesEntity - rootservices of registered friend
+ * @return consumer based on input parameters
+ */
+ public static OAuthConsumer getConsumer(RootServices rootServices, RootServicesEntity rootServicesEntity){
+ OAuthServiceProvider provider = new OAuthServiceProvider(rootServices.getOAuthRequestTokenURL(), rootServices.getOAuthUserAuthorizationURL(), rootServices.getOAuthAccessTokenURL());
+ OAuthConsumer oauthConsumer = new OAuthConsumer("", rootServicesEntity.getConsumerKey(), rootServicesEntity.getConsumerSecret(), provider);
+ return oauthConsumer;
+ }
+
+ /**
+ * Returns a consumer according to defined a consumer key
+ * @param consumerKey a key of consumer which is returned
+ * @return a consumer according to defined a consumer key
+ * @throws ClassNotFoundException
+ * @throws SQLException
+ * @throws ConsumerStoreException
+ */
+ public static LyoOAuthConsumer getConsumerByKey(String consumerKey) throws ClassNotFoundException, SQLException, ConsumerStoreException {
+ String currentMethod = "getConsumerByKey";
+ logger.debug(CURRENT_CLASS + "." + currentMethod + " Consumer key: " + consumerKey);
+
+ ConsumerStore consumerStore = loadConsumerStore();
+ if (consumerKey != null) {
+ return consumerStore.getConsumer(consumerKey);
+ }
+ return null;
+ }
+
+ public static OAuthAccessor getAccessorFromSession(HttpSession session, String consumerKey, String url) {
+ OAuthAccessor ac = null;
+ try {
+ String oAuthAccessorSession = JiraConstants.SESSION_OAUTHACCESSOR + consumerKey;
+ ac = (OAuthAccessor) session.getAttribute(oAuthAccessorSession);
+ }
+ catch (ClassCastException e) {
+ logger.debug(CURRENT_CLASS + ".getAccessorFormSession Exception: " + e.getMessage());
+ e.printStackTrace();
+ }
+ return ac;
+ }
+
+ /**
+ * It saves an accessor to the session. The accessor are saved to the session according to the consumer key
+ * @param session the session which contains the accessors
+ * @param ac accessor to save
+ * @param consumerKey a key consumers
+ */
+ public static void saveAccessorToSession(HttpSession session, OAuthAccessor ac, String consumerKey) {
+ String oAuthAccessorSession = JiraConstants.SESSION_OAUTHACCESSOR + consumerKey;
+ session.setAttribute(oAuthAccessorSession, ac);
+ }
+
+ /**
+ * Clear the cache of rootservices.
+ */
+ public static void clearRootServicesCache(){
+ if(rootServicesCache != null){
+ rootServicesCache.clear();
+ }
+
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/managers/FieldManager.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/managers/FieldManager.java
new file mode 100644
index 0000000..7523b97
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/managers/FieldManager.java
@@ -0,0 +1,428 @@
+package com.ericsson.jira.oslc.managers;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import com.atlassian.crowd.embedded.api.User;
+import com.atlassian.jira.bc.project.component.ProjectComponent;
+import com.atlassian.jira.component.ComponentAccessor;
+import com.atlassian.jira.config.PriorityManager;
+import com.atlassian.jira.config.ResolutionManager;
+import com.atlassian.jira.issue.Issue;
+import com.atlassian.jira.issue.IssueManager;
+import com.atlassian.jira.issue.MutableIssue;
+import com.atlassian.jira.issue.changehistory.ChangeHistoryManager;
+import com.atlassian.jira.issue.issuetype.IssueType;
+import com.atlassian.jira.issue.label.LabelManager;
+import com.atlassian.jira.issue.priority.Priority;
+import com.atlassian.jira.issue.resolution.Resolution;
+import com.atlassian.jira.issue.status.Status;
+import com.atlassian.jira.issue.vote.VoteManager;
+import com.atlassian.jira.issue.watchers.WatcherManager;
+import com.atlassian.jira.project.Project;
+import com.atlassian.jira.project.version.Version;
+import com.atlassian.jira.security.Permissions;
+import com.atlassian.jira.user.ApplicationUser;
+import com.atlassian.jira.user.util.UserManager;
+import com.atlassian.jira.workflow.JiraWorkflow;
+import com.atlassian.jira.workflow.WorkflowManager;
+import com.ericsson.jira.oslc.PluginConfig;
+import com.ericsson.jira.oslc.exceptions.NoResourceException;
+import com.ericsson.jira.oslc.exceptions.PermissionException;
+import com.ericsson.jira.oslc.resources.JiraChangeRequest;
+import com.ericsson.jira.oslc.resources.JiraHistoryRequest;
+
+/**
+ * A class offering the methods for operations with JIRA field
+ *
+ */
+public class FieldManager {
+
+ /**
+ * It returns the name of priority according to ID
+ * @param id the ID od priority
+ * @return the name of priority according to ID
+ */
+ public static String getPriorityName(String id) {
+ PriorityManager prMngr = ComponentAccessor.getComponent(PriorityManager.class);
+ List priorities = prMngr.getPriorities();
+ Iterator pit = priorities.iterator();
+ while (pit.hasNext()) {
+ Priority pr = pit.next();
+ if (pr != null) {
+ if (pr.getId().compareToIgnoreCase(id) == 0) {
+ return pr.getName();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * It returns the status of the issue according to defined ID of status
+ * @param issueId the ID of issue
+ * @param issueStatusId the ID of status
+ * @return the status of the issue according to defined ID of status
+ */
+ public static String getStatusNameForIssue(String issueId, String issueStatusId) {
+ final IssueManager issueManager = ComponentAccessor.getIssueManager();
+ final MutableIssue issue = issueManager.getIssueObject(issueId);
+
+ if (issue != null) {
+ WorkflowManager wMngr = ComponentAccessor.getWorkflowManager();
+ JiraWorkflow workflow = wMngr.getWorkflow(issue);
+ List statuses = workflow.getLinkedStatusObjects();
+ for (Status st : statuses) {
+ if (st.getId().compareToIgnoreCase(issueStatusId) == 0) {
+ return st.getName();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * It returns the resolution name issue according to ID of resolution
+ * @param resolutionId ID of resolution
+ * @return resolution name
+ */
+ public static String getResolutionName(String resolutionId) {
+ ResolutionManager resMngr = ComponentAccessor.getComponent(ResolutionManager.class);
+ List resolutions = resMngr.getResolutions();
+ Iterator rit = resolutions.iterator();
+ while (rit.hasNext()) {
+ Resolution res = rit.next();
+ if (res != null) {
+ if (res.getId().compareToIgnoreCase(resolutionId) == 0) {
+ return res.getName();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all issue types
+ * @param request
+ * @return all issue types
+ * @throws IOException
+ */
+ public static String[] getIssueTypes() throws IOException {
+ List types = ComponentAccessor.getConstantsManager().getAllIssueTypeIds();
+ return types.toArray(new String[] {});
+ }
+
+ /**
+ * Get a list of issue types which are filtered according to Plugin configuration
+ * @param request
+ * @return list of filtered issue types
+ * @throws Exception
+ */
+ public static List getFilteredIssueTypes() throws Exception {
+ List issueTypes = null;
+
+ PluginConfig config = PluginConfig.getInstance();
+ Set filteredTypes = config.getFilteredTypes();
+
+ if(filteredTypes == null || filteredTypes.isEmpty()){
+ issueTypes = ComponentAccessor.getConstantsManager().getAllIssueTypeIds();
+ }else{
+ issueTypes = new ArrayList();
+ for (Long id : filteredTypes) {
+ IssueType issueType = ComponentAccessor.getConstantsManager().getIssueTypeObject(id.toString());
+ if(issueType != null){
+ issueTypes.add(issueType.getId());
+ }
+ }
+ }
+
+ return issueTypes;
+ }
+
+
+
+
+
+ /**
+ * Get the name of issue type according to ID
+ * @param id ID of issue type
+ * @return the name of issue type
+ * @throws IOException
+ */
+ public static String getIssueType(String id) throws IOException {
+ Collection types = ComponentAccessor.getConstantsManager().getAllIssueTypeObjects();
+ for (IssueType t : types)
+ if (t.getId().compareTo(id) == 0) return t.getName();
+
+ return null;
+ }
+
+ /**
+ * Get History of Issue as Resource which will be sent to the client
+ * @param request HttpServletRequest
+ * @param issueKey the key of Issue
+ * @return History of Issue as Resource which will be sent to the client
+ * @throws PermissionException
+ * @throws NoResourceException
+ */
+ public static JiraHistoryRequest getHistoryOfIssue(final HttpServletRequest request, String issueKey) throws PermissionException, NoResourceException {
+ final IssueManager issueManager = ComponentAccessor.getIssueManager();
+ final MutableIssue issue = issueManager.getIssueObject(issueKey);
+
+ if (issue == null) {
+ throw new NoResourceException("The issue " + issueKey + " doesn't exist.");
+ }
+
+ PermissionManager.checkPermission(request, issue, Permissions.BROWSE);
+
+ ChangeHistoryManager hMngr = ComponentAccessor.getChangeHistoryManager();
+ JiraHistoryRequest history = new JiraHistoryRequest(hMngr.getChangeHistories(issue));
+
+ return history;
+ }
+
+ /**
+ * Function prepares list of components to set to issue. Requested list is compared against
+ * allowed project components. Components which are not allowed are ignored.
+ * @param issue JIRA issue
+ * @param cList the list of project component names
+ * @return the list of project components
+ */
+ public static Collection componentsToSet(final Issue issue, List cList) {
+ Project prj = issue.getProjectObject();
+ Collection pcc = prj.getProjectComponents();
+ Collection toSet = new ArrayList();
+ for (String c : cList) {
+ for (ProjectComponent pc : pcc) {
+ if (pc.getName().compareTo(c) == 0) {
+ toSet.add(pc);
+ break;
+ }
+ }
+ }
+ return toSet;
+ }
+
+ /**
+ * Function prepares list of component ids to set to issue. Requested list is compared against
+ * allowed component. The component which is not allowed is ignored.
+ * @param issue JIRA issue
+ * @param name the version names
+ * @return the list of versions
+ */
+ public static Long[] componentNamesToIds(final Project prj, String name) {
+ if(name == null){
+ return null;
+ }
+
+ ArrayList list = new ArrayList();
+ list.add(name);
+ return componentNamesToIds(prj, list);
+ }
+
+ /**
+ * Function prepares list of component ids to set to issue. Requested list is compared against
+ * allowed components. The components which are not allowed are ignored.
+ * @param issue JIRA issue
+ * @param list the list of component names
+ * @return the list of components
+ */
+ public static Long[] componentNamesToIds(final Project prj, List list) {
+ if(prj == null || list == null){
+ return null;
+ }
+
+ Collection col = prj.getProjectComponents();
+ Collection ids = new ArrayList();
+ for (String s : list) {
+ for (ProjectComponent elem : col) {
+ if (elem.getName().compareTo(s) == 0) {
+ ids.add(elem.getId());
+ break;
+ }
+ }
+ }
+ return ids.toArray(new Long[ids.size()]);
+ }
+
+ /**
+ * Function prepares list of versions to set to issue. Requested list is compared against
+ * allowed versions. Versions which are not allowed are ignored.
+ * @param issue JIRA issue
+ * @param list the list of version names
+ * @return the list of versions
+ */
+ public static Collection versionsToSet(final Issue issue, List list) {
+ Project prj = issue.getProjectObject();
+ Collection col = prj.getVersions();
+ Collection toSet = new ArrayList();
+ for (String s : list) {
+ for (Version elem : col) {
+ if (elem.getName().compareTo(s) == 0) {
+ toSet.add(elem);
+ break;
+ }
+ }
+ }
+ return toSet;
+ }
+
+ /**
+ * Function prepares list of version ids to set to issue. Requested list is compared against
+ * allowed version. The version which is not allowed are ignored.
+ * @param issue JIRA issue
+ * @param name version name
+ * @return the list of version ids
+ */
+ public static Long[] versionNamesToIds(final Project prj, String name) {
+ if(name == null){
+ return null;
+ }
+
+ ArrayList list = new ArrayList();
+ list.add(name);
+ return versionNamesToIds(prj, list);
+ }
+
+
+
+ /**
+ * Function prepares list of version ids to set to issue. Requested list is compared against
+ * allowed version ids. The versions which are not allowed are ignored.
+ * @param issue JIRA issue
+ * @param list the list of version names
+ * @return the list of versions
+ */
+ public static Long[] versionNamesToIds(final Project prj, List list) {
+ if(prj == null || list == null){
+ return null;
+ }
+
+ Collection col = prj.getVersions();
+ Collection ids = new ArrayList();
+ for (String s : list) {
+ for (Version elem : col) {
+ if (elem.getName().compareTo(s) == 0) {
+ ids.add(elem.getId());
+ break;
+ }
+ }
+ }
+
+ return ids.toArray(new Long[ids.size()]);
+ }
+
+ /**
+ * It updates labels from JiraChangeRequest
+ * @param issue the issue which will be updated
+ * @param jcr JIRA Change request
+ * @param user the updated will be done by the user
+ */
+ public static void updateLabels(MutableIssue issue, JiraChangeRequest jcr, User user) {
+ Set names = jcr.getLabels();
+ LabelManager lMngr = ComponentAccessor.getComponent(LabelManager.class);
+ lMngr.setLabels(user, issue.getId(), names, false, false);
+ }
+
+ /**
+ * It updates voters from JiraChangeRequest
+ * @param issue the issue which will be updated
+ * @param jcr JIRA Change request
+ * @param user the updated will be done by the user
+ */
+ public static void updateVoters(MutableIssue issue, JiraChangeRequest jcr, User user) {
+ List names = jcr.getVoters();
+
+ List newVoters = new ArrayList();
+ UserManager um = ComponentAccessor.getUserManager();
+ for (String name : names) {
+ ApplicationUser u = um.getUserByName(name);
+ if (u != null) {
+ newVoters.add(u);
+ }
+ }
+
+ VoteManager vMngr = ComponentAccessor.getVoteManager();
+ List voters = vMngr.getVotersFor(issue, ComponentAccessor.getJiraAuthenticationContext().getLocale());
+
+ //remove existing voters, who aren't in incoming list
+ for (ApplicationUser voter : voters) {
+ if (newVoters.contains(voter) == false) {
+ vMngr.removeVote(voter, issue);
+ }
+ }
+
+ //add voters from incoming list
+ for (ApplicationUser voter : newVoters) {
+ vMngr.addVote(voter, issue);
+ }
+ }
+
+ /**
+ * It updates watchers from JiraChangeRequest
+ * @param issue the issue which will be updated
+ * @param jcr JIRA Change request
+ * @param user the updated will be done by the user
+ */
+ public static void updateWatchers(MutableIssue issue, JiraChangeRequest jcr, User user) {
+ List names = jcr.getWatchers();
+
+ List newWatchers = new ArrayList();
+ UserManager um = ComponentAccessor.getUserManager();
+ for (String name : names) {
+ ApplicationUser u = um.getUserByName(name);
+ if (u != null) {
+ newWatchers.add(u);
+ }
+ }
+
+ WatcherManager watcherMngr = ComponentAccessor.getWatcherManager();
+ List wList = watcherMngr.getWatchers(issue, ComponentAccessor.getJiraAuthenticationContext().getLocale());
+
+ //remove existing watchers, who aren't in incoming list
+ for (ApplicationUser u : wList) {
+ if (newWatchers.contains(u) == false) {
+ watcherMngr.stopWatching(u, issue);
+ }
+ }
+
+ //add watchers from incoming list
+ for (ApplicationUser u : newWatchers) {
+ watcherMngr.startWatching(u, issue);
+ }
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/managers/JiraManager.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/managers/JiraManager.java
new file mode 100644
index 0000000..c08ec09
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/managers/JiraManager.java
@@ -0,0 +1,814 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2013 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Sam Padgett - initial API and implementation
+ * Michael Fiedler - adapted for OSLC4J
+ *******************************************************************************/
+
+package com.ericsson.jira.oslc.managers;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.WebApplicationException;
+
+import org.ofbiz.core.entity.GenericEntityException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.atlassian.crowd.embedded.api.User;
+import com.atlassian.jira.bc.issue.IssueService;
+import com.atlassian.jira.bc.issue.IssueService.CreateValidationResult;
+import com.atlassian.jira.bc.issue.IssueService.IssueResult;
+import com.atlassian.jira.bc.issue.IssueService.IssueValidationResult;
+import com.atlassian.jira.bc.issue.IssueService.TransitionValidationResult;
+import com.atlassian.jira.bc.issue.IssueService.UpdateValidationResult;
+import com.atlassian.jira.bc.project.component.ProjectComponent;
+import com.atlassian.jira.component.ComponentAccessor;
+import com.atlassian.jira.config.properties.APKeys;
+import com.atlassian.jira.issue.Issue;
+import com.atlassian.jira.issue.IssueInputParameters;
+import com.atlassian.jira.issue.IssueInputParametersImpl;
+import com.atlassian.jira.issue.IssueManager;
+import com.atlassian.jira.issue.MutableIssue;
+import com.atlassian.jira.issue.fields.CustomField;
+import com.atlassian.jira.issue.managers.DefaultCustomFieldManager;
+import com.atlassian.jira.issue.managers.DefaultIssueManager;
+import com.atlassian.jira.issue.status.Status;
+import com.atlassian.jira.project.Project;
+import com.atlassian.jira.project.ProjectManager;
+import com.atlassian.jira.project.version.Version;
+import com.atlassian.jira.security.JiraAuthenticationContext;
+import com.atlassian.jira.security.Permissions;
+import com.atlassian.jira.user.ApplicationUser;
+import com.atlassian.jira.user.ApplicationUsers;
+import com.atlassian.jira.user.util.UserManager;
+import com.atlassian.jira.util.ErrorCollection;
+import com.atlassian.jira.workflow.JiraWorkflow;
+import com.atlassian.jira.workflow.WorkflowManager;
+import com.ericsson.eif.leansync.mapping.data.SyncConfiguration;
+import com.ericsson.jira.oslc.Constants;
+import com.ericsson.jira.oslc.PluginConfig;
+import com.ericsson.jira.oslc.constants.JiraConstants;
+import com.ericsson.jira.oslc.events.IssueEventType;
+import com.ericsson.jira.oslc.exceptions.GetIssueException;
+import com.ericsson.jira.oslc.exceptions.IssueTransitionException;
+import com.ericsson.jira.oslc.exceptions.IssueValidationException;
+import com.ericsson.jira.oslc.exceptions.NoResourceException;
+import com.ericsson.jira.oslc.exceptions.PermissionException;
+import com.ericsson.jira.oslc.exceptions.PreconditionException;
+import com.ericsson.jira.oslc.exceptions.StatusException;
+import com.ericsson.jira.oslc.resources.JiraChangeRequest;
+import com.ericsson.jira.oslc.services.ServiceHelper;
+import com.ericsson.jira.oslc.servlet.ServiceProviderCatalogSingleton;
+import com.ericsson.jira.oslc.sync.SyncUtils;
+import com.ericsson.jira.oslc.utils.AppLinksRepository;
+import com.ericsson.jira.oslc.utils.JiraIssueInputParameters;
+import com.ericsson.jira.oslc.utils.OSLCUtils;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.opensymphony.workflow.loader.ActionDescriptor;
+import com.opensymphony.workflow.loader.StepDescriptor;
+
+/**
+ *
+ * It contains the methods for operations with JIRA issue
+ *
+ */
+public class JiraManager {
+ private static final String CURRENT_CLASS = "JiraManager";
+ private static Logger logger = LoggerFactory.getLogger(JiraManager.class);
+
+
+ /**
+ * Get a list of Issues for a project ID using paging
+ *
+ * @param httpServletRequest HttpServletRequest
+ * @param projectKeyString the key of project as String
+ * @return The list of change requests
+ * @throws IOException
+ * @throws ServletException
+ * @throws URISyntaxException
+ * @throws PermissionException
+ */
+ public static List getIssuesByProject(final HttpServletRequest httpServletRequest,
+ final String projectKeyString) throws IOException, ServletException,
+ URISyntaxException, PermissionException {
+ String currentMethod = "getIssuesByProject";
+ String userName = PermissionManager.getUserName(httpServletRequest);
+
+ UserManager um = ComponentAccessor.getComponent(UserManager.class);
+ User user = ApplicationUsers.toDirectoryUser(um.getUserByName(userName));
+
+ //get issue for project
+ ProjectManager projectManager = ComponentAccessor.getProjectManager();
+ long prjid = Long.parseLong(projectKeyString);
+ Project prj = projectManager.getProjectObj(prjid);
+
+ ApplicationUser appUser = PermissionManager.getAppUserFromRequest(httpServletRequest);
+ PermissionManager.checkPermission(appUser, prj, Permissions.BROWSE);
+
+ IssueManager issueManager = ComponentAccessor.getIssueManager();
+ IssueService issueService = ComponentAccessor.getIssueService();
+ Collection ids;
+ try {
+ ids = issueManager.getIssueIdsForProject(prj.getId());
+ }
+ catch (GenericEntityException e1) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e1.getMessage());
+ return null;
+ }
+
+ List results = new ArrayList();
+ Iterator it = ids.iterator();
+ while (it.hasNext())
+ {
+ final IssueService.IssueResult issueResult = issueService.getIssue(user, it.next());
+ final MutableIssue issue = issueResult.getIssue();
+
+ if(!PermissionManager.hasPermission(appUser, issue, Permissions.BROWSE)){
+ continue;
+ }
+
+ JiraChangeRequest jcr = JiraChangeRequest.fromJiraIssue(issue);
+ jcr.setServiceProvider(ServiceProviderCatalogSingleton.getServiceProvider(httpServletRequest, projectKeyString).getAbout());
+
+ URI about;
+ try {
+ about = new URI(ServiceHelper.getOslcBaseUri(httpServletRequest) + "/" + projectKeyString + "/changeRequests/" + jcr.getIdentifier());
+ }
+ catch (URISyntaxException e) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ throw new WebApplicationException(e);
+ }
+
+ jcr.setAbout(about);
+ results.add(jcr);
+ }
+
+ return results;
+ }
+
+
+ /**
+ * Get a Jira Issue by id
+ *
+ * @param request
+ * @param IssueId
+ * @return Issue
+ * @throws IOException
+ * @throws ServletException
+ */
+ public static JiraChangeRequest getIssueById(final HttpServletRequest request, final Long issueId)
+ throws IOException, ServletException, URISyntaxException {
+
+ final IssueManager issueManager = ComponentAccessor.getIssueManager();
+ final MutableIssue issue = issueManager.getIssueObject(issueId);
+
+ JiraChangeRequest jcr = JiraChangeRequest.fromJiraIssue(issue);
+ return jcr;
+ }
+
+ /**
+ * Get a Jira Issue by id
+ *
+ * @param request
+ * @param IssueIdString
+ * @return Issue
+ * @throws IOException
+ * @throws ServletException
+ * @throws PermissionException
+ * @throws NoResourceException
+ */
+ public static JiraChangeRequest getIssueById(final HttpServletRequest request, final String issueId)
+ throws IOException, ServletException, URISyntaxException, PermissionException, NoResourceException {
+
+ final IssueManager issueManager = ComponentAccessor.getIssueManager();
+ final MutableIssue issue = issueManager.getIssueObject(issueId);
+
+ if(issue == null){
+ throw new NoResourceException("The issue " + issueId + " doesn't exist.");
+ }
+
+ PermissionManager.checkPermission(request, issue, Permissions.BROWSE);
+
+ JiraChangeRequest jcr = JiraChangeRequest.fromJiraIssue(issue);
+ return jcr;
+ }
+
+ /**
+ * Create a new JIRA issue from a JIRAChangeRequest
+ *
+ * @param httpServletRequest
+ * @param jcr JIRAChangeRequest
+ * @param productIdString the id of product
+ * @param syncType the type of synchronization
+ * @return id of the new JIRA issue
+ * @throws IOException
+ * @throws ServletException
+ */
+ public static Long createIssue(HttpServletRequest httpServletRequest, final JiraChangeRequest jcr, final String projectIdString, String syncType) throws Exception {
+ logger.debug("JiraManager - createIssue");
+
+ String userName = PermissionManager.getUserName(httpServletRequest);
+ UserManager um = ComponentAccessor.getComponent(UserManager.class);
+ ApplicationUser appUser = um.getUserByName(userName);
+ User user = ApplicationUsers.toDirectoryUser(um.getUserByName(userName));
+ JiraAuthenticationContext authenticationContext = ComponentAccessor.getJiraAuthenticationContext();
+ authenticationContext.setLoggedInUser(appUser);
+
+
+ ProjectManager projectManager = ComponentAccessor.getComponent(ProjectManager.class);
+ Project p = projectManager.getProjectObj(new Long(projectIdString)) ;
+ if (p == null) {
+ logger.warn("Project does not exist!");
+ throw new PreconditionException("Project with id " + projectIdString + " does not exist!");
+ }
+
+ PermissionManager.checkPermission(appUser, p, Permissions.CREATE_ISSUE);
+
+ jcr.setProjectId(p.getId());
+ jcr.setProject(p.getKey());
+
+ if(jcr.getReporter() == null || jcr.getReporter().isEmpty()){
+ jcr.setReporter(user.getName());
+ }
+
+ SyncConfiguration leanSyncConfiguration = SyncUtils.getLeanSyncConfiguration(jcr, null);
+
+ JiraIssueInputParameters jiraIssueInputParams = new JiraIssueInputParameters();
+ boolean generateStatusLog = false;
+ try {
+ jiraIssueInputParams = JiraChangeRequest.toIssueParameters(jcr, null, null, syncType, leanSyncConfiguration, p);
+ IssueInputParameters issueInputParams = jiraIssueInputParams.getIssueInputParameters();
+
+ String errorLogName = null;
+ if (leanSyncConfiguration != null) {
+ errorLogName = leanSyncConfiguration.getErrorLog();
+ }
+
+ // handle errors only for LeanSync
+ if (syncType != null) {
+
+ if (!jiraIssueInputParams.getErrorSyncHandler().isLogEmpty()) {
+ generateStatusLog = true;
+ logger.warn("Error during parsing issue");
+ JiraIssueInputParameters newJiraIssueInputParameters = new JiraIssueInputParameters();
+
+ SyncUtils.putValueToErrorField(null, newJiraIssueInputParameters, errorLogName, jiraIssueInputParams.getErrorSyncHandler());
+ SyncUtils.handleErrorState(null, newJiraIssueInputParameters, jiraIssueInputParams, leanSyncConfiguration, false);
+
+ issueInputParams = newJiraIssueInputParameters.getIssueInputParameters();
+ }else if (!jiraIssueInputParams.getWarnSyncHandler().isLogEmpty()) {
+ SyncUtils.putValueToErrorField(null, jiraIssueInputParams, errorLogName, jiraIssueInputParams.getWarnSyncHandler());
+ } else {
+ // only clear Error custom filed
+ SyncUtils.putValueToErrorField(null, jiraIssueInputParams, errorLogName, null);
+
+ }
+ }
+
+ IssueService issueService = ComponentAccessor.getComponent(IssueService.class);
+ IssueValidationResult validationResult = issueService.validateCreate(user, issueInputParams);
+
+ if (!validationResult.isValid()) {
+ logger.warn("Create issue - parameters are not valid.");
+ ErrorCollection errorCollection = validationResult.getErrorCollection();
+ throwErrorException(errorCollection);
+ }
+
+ IssueResult issueResult = issueService.create(user, (CreateValidationResult)validationResult);
+ if (!issueResult.isValid()) {
+ logger.warn("Error during creating issue");
+ ErrorCollection errorCollection = issueResult.getErrorCollection();
+ throwErrorException(errorCollection);
+ }
+
+ MutableIssue createdIssue = issueResult.getIssue();
+ if (createdIssue != null && syncType == null) {
+ List cList = jcr.getComponents();
+ Collection toSet1 = FieldManager.componentsToSet(createdIssue, cList);
+ createdIssue.setComponentObjects(toSet1);
+
+ List avList = jcr.getAffectsVersions();
+ Collection toSet2 = FieldManager.versionsToSet(createdIssue, avList);
+ createdIssue.setAffectedVersions(toSet2);
+
+ List fvList = jcr.getFixVersions();
+ Collection toSet3 = FieldManager.versionsToSet(createdIssue, fvList);
+ createdIssue.setFixVersions(toSet3);
+ }
+
+ return issueResult.getIssue().getId();
+
+ }
+ catch (Exception e) {
+ logger.error("Error", e);
+ if (syncType == null) {
+ throw e;
+ }
+ generateStatusLog = true;
+ String exceptionMessage = "CreateIssue error: Occurs error during parsing and validating input values.";
+
+ if (jiraIssueInputParams != null) {
+ jiraIssueInputParams.getErrorSyncHandler().addMessage(e.getMessage());
+ exceptionMessage += ": Message:" + jiraIssueInputParams.getErrorSyncHandler().getMessagesAsString();
+ }
+ logger.warn(exceptionMessage);
+ }finally {
+ if (generateStatusLog && syncType != null) {
+ String statusMessage = "Error";
+ final String errorValues = jiraIssueInputParams.getErrorSyncHandler().getMessagesAsString();
+
+ if (errorValues != null) {
+ statusMessage += ": " + errorValues;
+ }
+ logger.warn(statusMessage);
+ throw new StatusException(statusMessage);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the list of projects where the users has Browse permission. The list is filtered by defined filter
+ * in the plugin configuration
+ * @param httpServletRequest HttpServletRequest
+ * @return the list of projects where the users has Browse permission
+ * @throws Exception
+ */
+ public static Project[] getProjects(HttpServletRequest httpServletRequest) throws Exception {
+ ProjectManager projectManager = ComponentAccessor.getComponent(ProjectManager.class);
+ if (projectManager == null){
+ logger.debug("Project manager is null!");
+ return null;
+ }
+
+ List filteredProjects = new ArrayList();
+ ApplicationUser appUser = PermissionManager.getAppUserFromRequest(httpServletRequest);
+
+ PluginConfig config = PluginConfig.getInstance();
+ Set filteredProjectIDs = config.getFilteredProjects();
+
+ if(filteredProjectIDs == null || filteredProjectIDs.isEmpty()){
+ List allProjects = projectManager.getProjectObjects();
+ for (Project project : allProjects) {
+ if(PermissionManager.hasPermission(appUser,project, Permissions.BROWSE)){
+ filteredProjects.add(project);
+ }
+ }
+ }else{
+ for (Long projId : filteredProjectIDs) {
+ Project project = projectManager.getProjectObj(projId);
+ if(project != null && PermissionManager.hasPermission(appUser,project, Permissions.BROWSE)){
+ filteredProjects.add(project);
+ }
+ }
+ }
+
+ if (filteredProjects.size() <= 0) {
+ logger.debug("There are no projects!");
+ return null;
+ }
+
+ return filteredProjects.toArray(new Project[]{});
+ }
+
+ /**
+ * Returns the URL where the REST API is
+ * @return the URL where the REST API is
+ */
+ public static String getRestUrl() {
+ return ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL) + JiraConstants.REST_URL;
+ }
+
+ /**
+ * Returns the base URL
+ * @return the base URL
+ */
+ public static String getBaseUrl() {
+ return ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL);
+ }
+
+ /**
+ * Add External link
+ * @param issueId the ID of issue where the links will be added to
+ * @param linksList the list of the links which will be added to the issue
+ * @return true - if the links will be added to the issue successfully, otherwise false
+ * @throws IOException
+ * @throws ServletException
+ * @throws PermissionException
+ */
+ public static Boolean addOSLCLink(final Long issueId, final ArrayList linksList) throws IOException, ServletException, PermissionException {
+ logger.debug("JiraManager - addOSLCLink");
+ DefaultIssueManager issueManager = ComponentAccessor.getComponent(DefaultIssueManager.class);
+ MutableIssue issue = issueManager.getIssueObject(issueId);
+
+ PermissionManager.checkPermission(null, issue, Permissions.EDIT_ISSUE);
+
+ DefaultCustomFieldManager cfManager = (DefaultCustomFieldManager) ComponentAccessor.getCustomFieldManager();
+ CustomField cf = cfManager.getCustomFieldObjectByName(JiraConstants.OSLC_CUSTOM_FIELD_NAME);
+
+ if (cf == null){
+ return false;
+ }
+
+ // prepare GSON
+ GsonBuilder gsonBuilder = new GsonBuilder();
+ Gson gson = gsonBuilder.create();
+
+ String uri;
+ String label;
+
+ for (int iLink = 0; iLink < linksList.size(); iLink++) {
+ ArrayList onelink = (ArrayList)linksList.get(iLink);
+ if (onelink.size() > 1){
+ label = (String) onelink.get(0);
+ uri = (String) onelink.get(1);
+ }
+ else {
+ continue;
+ }
+ // get all application links which are already saved in custom
+ // field
+ String links = (String) cf.getValue(issue);
+ AppLinksRepository appLinkList = new AppLinksRepository();
+
+ if (links != "") {
+ try {
+ appLinkList = gson.fromJson(links, AppLinksRepository.class);
+ } catch (com.google.gson.JsonSyntaxException e) {
+ logger.debug("JiraManager - addOSLCLink - " + e.getMessage());
+ }
+
+ if (appLinkList == null){
+ appLinkList = new AppLinksRepository();
+ }
+
+ appLinkList.addAppLink(label, uri, true);
+ }
+
+ String updatedLinks = "";
+ updatedLinks = gson.toJson(appLinkList);
+
+ if (updatedLinks != null && updatedLinks != "") {
+ cf.createValue(issue, updatedLinks);
+ }
+
+ } // for
+
+ ApplicationUser user = PermissionManager.getLoggedUser();
+ OSLCUtils.fireRestIssueEvent(issue, user, IssueEventType.ADD_EXT_LINK);
+
+ return true;
+ }
+
+
+ /**
+ * It removes the external link from the issue
+ * @param issueId the ID of issue
+ * @param URItoRemove URI of the link which will be removed from the issue
+ * @return true - if the links will be removed to the issue successfully, otherwise false
+ * @throws GetIssueException
+ * @throws PermissionException
+ */
+ public static Boolean removeOSLCLink(final String issueId, final String URItoRemove) throws GetIssueException, PermissionException{
+ String currentMethod = "removeOSLCLink";
+ logger.debug(CURRENT_CLASS + "." + currentMethod);
+
+
+ DefaultIssueManager issueManager = ComponentAccessor.getComponent(DefaultIssueManager.class);
+ Long issueIdLong = (long) -1;
+
+ try {
+ issueIdLong = Long.valueOf(issueId).longValue();
+ } catch (Exception ex) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + "Exception: " + ex.getMessage());
+ throw new GetIssueException("Issue not available");
+ }
+
+ MutableIssue issue = issueManager.getIssueObject(issueIdLong);
+ ApplicationUser user = PermissionManager.getLoggedUser();
+ PermissionManager.checkPermissionWithUser(user, issue, Permissions.EDIT_ISSUE);
+
+ if(issue == null) throw new GetIssueException("Issue not available");
+
+ DefaultCustomFieldManager cfManager = (DefaultCustomFieldManager) ComponentAccessor.getCustomFieldManager();
+ CustomField cf = cfManager.getCustomFieldObjectByName(JiraConstants.OSLC_CUSTOM_FIELD_NAME);
+
+ if (cf == null){
+ throw new GetIssueException("Custom field ("+ JiraConstants.OSLC_CUSTOM_FIELD_NAME + ") not available");
+ }
+
+ // prepare GSON
+ GsonBuilder gsonBuilder = new GsonBuilder();
+ Gson gson = gsonBuilder.create();
+
+
+ // get all application links which are already saved in custom field
+ String appLinks = (String) cf.getValue(issue);
+ AppLinksRepository appLinkList = new AppLinksRepository();
+
+ if (appLinks == null){
+ return false;
+ }else if (appLinks == ""){
+ return true;
+ }
+
+ try{
+ appLinkList = gson.fromJson(appLinks, AppLinksRepository.class);
+ } catch (com.google.gson.JsonSyntaxException e) {
+ logger.error(CURRENT_CLASS + "." + currentMethod + "Exception: " + e.getMessage());
+ }
+
+ if (appLinkList == null || URItoRemove == null){
+ return false;
+ }
+
+ if (appLinkList.removeAppLink(URItoRemove)){
+ String updatedAppLinks = "";
+ if(appLinkList.GetAllAppLinks().size() == 0){
+ updatedAppLinks = "";
+ }
+ else {
+ updatedAppLinks = gson.toJson(appLinkList);
+ }
+
+ if (updatedAppLinks != null) {
+ cf.createValue(issue, updatedAppLinks);
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * It changes the status of the issue
+ * @param issue
+ * @param targetStatus the target status
+ * @throws Exception
+ */
+ public static void changeIssueState(Issue issue, String targetStatus) throws Exception {
+ Status current_status = issue.getStatusObject();
+
+ if (current_status.getId() == targetStatus) {
+ //same states - do nothing
+ return;
+ }
+
+ WorkflowManager wMngr = ComponentAccessor.getWorkflowManager();
+ JiraWorkflow workflow = wMngr.getWorkflow(issue);
+
+ List statuses = workflow.getLinkedStatusObjects();
+ Status next_status = null;
+ for (Status st : statuses) {
+ if (st.getId().compareToIgnoreCase(targetStatus) == 0) {
+ next_status = st;
+ break;
+ }
+ }
+
+ if (next_status == null) {
+ throw new IssueTransitionException("Requested status (" + targetStatus + ") does not exist!");
+ }
+
+ if (current_status.getId() == next_status.getId()) {
+ //same states - do nothing
+ return;
+ }
+
+ StepDescriptor step1 = workflow.getLinkedStep(current_status);
+ StepDescriptor step2 = workflow.getLinkedStep(next_status);
+
+ List actions1 = step1.getActions();
+ Collection actions2 = workflow.getActionsWithResult(step2);
+
+ int actionId = -1;
+ for (ActionDescriptor d1 : actions1) {
+ for (ActionDescriptor d2 : actions2) {
+ if (d1.getId() == d2.getId()) {
+ actionId = d1.getId();
+ break;
+ }
+ }
+ }
+
+ if (actionId == -1) {
+ throw new IssueTransitionException("Transition from " + current_status.getName()
+ + " to " + next_status.getName() + " is not allowed!");
+ }
+
+ IssueService issueService = ComponentAccessor.getComponent(IssueService.class);
+ IssueService.IssueResult transResult;
+
+ IssueInputParameters issueInputParameters = new IssueInputParametersImpl();
+
+ ApplicationUser aUser = ComponentAccessor.getJiraAuthenticationContext().getUser();
+ User user = ApplicationUsers.toDirectoryUser(aUser);
+
+ TransitionValidationResult validationResult = issueService.validateTransition(
+ user, issue.getId(), actionId, issueInputParameters);
+ if (validationResult.isValid() == false) {
+ logger.warn("Change issue state - parameters are not valid.");
+ ErrorCollection errorCollection = validationResult.getErrorCollection();
+ throwErrorException(errorCollection);
+ }
+
+ transResult = issueService.transition(user, validationResult);
+ if (transResult.isValid() == false) {
+ logger.warn("Error during issue state change!");
+ ErrorCollection errorCollection = transResult.getErrorCollection();
+ throwErrorException(errorCollection);
+ }
+ }
+
+ /**
+ * Update existing JIRA issue from a JIRAChangeRequest
+ *
+ * @param httpServletRequest
+ * @param jcr JIRAChangeRequest
+ * @param issueId the id of updated issue
+ * @param selectedProperties OSLC slected properties
+ * @param syncType the type of synchronization
+ * @return id of the new JIRA issue
+ * @throws IOException
+ * @throws ServletException
+ */
+ public static void updateIssue(HttpServletRequest httpServletRequest, final JiraChangeRequest jcr, final String issueId, Map selectedProperties, String syncType) throws Exception {
+ logger.debug("JiraManager - updateIssue");
+
+ final IssueManager issueManager = ComponentAccessor.getIssueManager();
+ final MutableIssue issue = issueManager.getIssueObject(issueId);
+
+ if (issue == null) {
+ logger.warn("Issue does not exist!");
+ throw new NoResourceException("Issue with id " + issueId + " does not exist!");
+ }
+
+ PermissionManager.checkPermission(httpServletRequest, issue, Permissions.EDIT_ISSUE);
+
+ String userName = PermissionManager.getUserName(httpServletRequest);
+ UserManager um = ComponentAccessor.getComponent(UserManager.class);
+ ApplicationUser appUser = um.getUserByName(userName);
+ JiraAuthenticationContext authenticationContext = ComponentAccessor.getJiraAuthenticationContext();
+ authenticationContext.setLoggedInUser(appUser);
+ User user = ApplicationUsers.toDirectoryUser(um.getUserByName(userName));
+ IssueService issueService = ComponentAccessor.getIssueService();
+
+ if (selectedProperties != null && selectedProperties.isEmpty()) {
+ selectedProperties = null;
+ }
+
+ // If user has the permission to edit issue (checked above), there still could be restrictions
+ // to edit certain field (e.g. user can't change reporter). So following statement checks
+ // also for some of sub-permissions.
+ if (selectedProperties != null && syncType == null) {
+ PermissionManager.checkUpdatePermissions(appUser, issue, jcr, selectedProperties);
+ }
+
+ // check configuration. If occurs error => not possible to set to ErrorSnapshot. Unknown "projectId"
+ SyncConfiguration leanSyncConfiguration = SyncUtils.getLeanSyncConfiguration(jcr, issue);
+ if (syncType != null && (leanSyncConfiguration == null || leanSyncConfiguration.getFirstInMapping() == null)) {
+ logger.debug("Issue with id " + issueId + " has wrong lean sync setting (projectId or issueTypeId!");
+ throw new Exception("Issue with id " + issueId + " has wrong lean sync setting (projectId or issueTypeId!");
+ }
+
+ String errorLogName = null;
+ JiraIssueInputParameters jiraIssueInputParams = new JiraIssueInputParameters();
+ if (leanSyncConfiguration != null) {
+ errorLogName = leanSyncConfiguration.getErrorLog();
+ }
+
+ // flag if occurs some error during parsing. true-will be updated status(error) log and thrown exception to client
+ boolean generateStatusLog = false;
+ try {
+ jiraIssueInputParams = JiraChangeRequest.toIssueParameters(jcr, issue, selectedProperties, syncType, leanSyncConfiguration, issue.getProjectObject());
+ IssueInputParameters issueInputParams = jiraIssueInputParams.getIssueInputParameters();
+
+ // handle errors only for LeanSync
+ if (syncType != null) {
+ if (!jiraIssueInputParams.getErrorSyncHandler().isLogEmpty()) {
+ generateStatusLog = true;
+ logger.warn("Error during parsing issue");
+ JiraIssueInputParameters newJiraIssueInputParameters = new JiraIssueInputParameters();
+
+ SyncUtils.putValueToErrorField(issue, newJiraIssueInputParameters, errorLogName, jiraIssueInputParams.getErrorSyncHandler());
+ SyncUtils.handleErrorState(issue, newJiraIssueInputParameters, jiraIssueInputParams, leanSyncConfiguration, false);
+
+ issueInputParams = newJiraIssueInputParameters.getIssueInputParameters();
+ }else if (!jiraIssueInputParams.getWarnSyncHandler().isLogEmpty()) {
+ SyncUtils.putValueToErrorField(issue, jiraIssueInputParams, errorLogName, jiraIssueInputParams.getWarnSyncHandler());
+ } else {
+ // only clear Error custom filed
+ SyncUtils.putValueToErrorField(issue, jiraIssueInputParams, errorLogName, null);
+
+ }
+ }
+
+ IssueValidationResult validationResult = issueService.validateUpdate(user, issue.getId(), issueInputParams);
+ if (!validationResult.isValid()) {
+ logger.warn("Update issue - parameters are not valid.");
+ ErrorCollection errorCollection = validationResult.getErrorCollection();
+ throwErrorException(errorCollection);
+ }
+
+ IssueService.IssueResult issueResult = issueService.update(user, (UpdateValidationResult)validationResult);
+ if (!issueResult.isValid()) {
+ logger.warn("Error during updating issue");
+ ErrorCollection errorCollection = issueResult.getErrorCollection();
+ throwErrorException(errorCollection);
+ }
+
+ //after successful update, check if there is also request for change state
+ MutableIssue updatedIssue = issueResult.getIssue();
+ if (updatedIssue != null && syncType == null) {
+ String statusId = issueInputParams.getStatusId();
+ if (statusId != null) {
+ changeIssueState(issueResult.getIssue(), statusId);
+ }
+
+ if (OSLCUtils.allowUpdate(selectedProperties, Constants.JIRA_TYPE_LABEL)) {
+ FieldManager.updateLabels(updatedIssue, jcr, user);
+ }
+
+ if (OSLCUtils.allowUpdate(selectedProperties, Constants.JIRA_TYPE_VOTER)) {
+ FieldManager.updateVoters(updatedIssue, jcr, user);
+ }
+
+ if (OSLCUtils.allowUpdate(selectedProperties, Constants.JIRA_TYPE_WATCHER)) {
+ FieldManager.updateWatchers(updatedIssue, jcr, user);
+ }
+ }
+ } catch (Exception e) {
+ logger.error("Error", e);
+ if (syncType == null) {
+ throw e;
+ }
+ //continue for LeanSync
+ generateStatusLog = true; // occurs error => generate snapshot and status information
+ jiraIssueInputParams.getErrorSyncHandler().addMessage(e.getMessage());
+
+ logger.warn(jiraIssueInputParams.getErrorSyncHandler().getMessagesAsString());
+
+ SyncUtils.saveErrorStatus(issue, jiraIssueInputParams.getErrorSyncHandler(), errorLogName);
+ SyncUtils.handleErrorState(issue, null, jiraIssueInputParams, leanSyncConfiguration, true);
+ } finally {
+ if (generateStatusLog && syncType != null) {
+ String statusMessage = "Error";
+ final String errorValues = jiraIssueInputParams.getErrorSyncHandler().getMessagesAsString();
+
+ if (errorValues != null) {
+ statusMessage += ": " + errorValues;
+ }
+ logger.warn("Error", statusMessage);
+ throw new StatusException(statusMessage);
+ }
+ }
+
+ }
+
+ /**
+ * It prepares error message from Error Collection and then throws IssueValidationException
+ * @param errorCollection collection of errors
+ * @throws IssueValidationException if the errors is not empty then throws IssueValidationException
+ */
+ private static void throwErrorException(ErrorCollection errorCollection) throws IssueValidationException{
+ StringBuilder sb = new StringBuilder();
+
+ Map errors = errorCollection.getErrors();
+ Collection errorMessages = errorCollection.getErrorMessages();
+
+ if (errors != null && !errors.isEmpty()) {
+ sb.append(errors.toString());
+ }
+ if (errorMessages != null && !errorMessages.isEmpty()) {
+ sb.append(errorMessages.toString());
+ }
+ String message = sb.toString();
+ logger.warn("Validation result: " + message);
+ throw new IssueValidationException(message);
+ }
+
+
+}
\ No newline at end of file
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/managers/PermissionManager.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/managers/PermissionManager.java
new file mode 100644
index 0000000..b17382a
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/managers/PermissionManager.java
@@ -0,0 +1,276 @@
+package com.ericsson.jira.oslc.managers;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.WebApplicationException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.atlassian.jira.component.ComponentAccessor;
+import com.atlassian.jira.issue.Issue;
+import com.atlassian.jira.permission.GlobalPermissionKey;
+import com.atlassian.jira.project.Project;
+import com.atlassian.jira.security.Permissions;
+import com.atlassian.jira.user.ApplicationUser;
+import com.atlassian.jira.user.util.UserManager;
+import com.ericsson.jira.oslc.Constants;
+import com.ericsson.jira.oslc.exceptions.PermissionException;
+import com.ericsson.jira.oslc.resources.JiraChangeRequest;
+import com.ericsson.jira.oslc.resources.JiraIssueStatus;
+import com.ericsson.jira.oslc.servlet.CredentialsFilter;
+import com.ericsson.jira.oslc.utils.OSLCUtils;
+
+
+/**
+ * A class for operations with the users and theirs permissions
+ *
+ */
+public class PermissionManager {
+ private static Logger logger = LoggerFactory.getLogger(PermissionManager.class);
+
+ /**
+ * Checks a permission for logged user and the issue.
+ * @param request HttpServletRequest
+ * @param issue JIRA issue
+ * @param permission the permission which will be checked
+ * @throws PermissionException if the user doesn't have the permission the exception will be thrown
+ */
+ public static void checkPermission(final HttpServletRequest request, final Issue issue, int permission) throws PermissionException {
+ ApplicationUser appUser = null;
+ if (request != null) {
+ appUser = getAppUserFromRequest(request);
+ }
+ if (appUser == null) {
+ appUser = getLoggedUser();
+ if (appUser == null) {
+ throw new PermissionException("User is not defined.");
+ }
+ }
+
+ boolean allowed = hasPermission(appUser, issue, permission);
+ if (!allowed) {
+ throw new PermissionException();
+ }
+ }
+
+ /**
+ * Checks a permission for defined user and the issue.
+ * @param appUser the user which will be checked
+ * @param issue JIRA issue
+ * @param permission the permission which will be checked
+ * @throws PermissionException if the user doesn't have the permission the exception will be thrown
+ */
+ public static void checkPermissionWithUser(ApplicationUser appUser, final Issue issue, int permission) throws PermissionException {
+ boolean allowed = ComponentAccessor.getPermissionManager().hasPermission(permission, issue, appUser);
+ if (!allowed) {
+ throw new PermissionException();
+ }
+ }
+
+ /**
+ * Checks a permission for defined user and the issue.
+ * If the user doesn't have the permission the exception will be thrown with defined message {additionalMesage}
+ * @param appUser the user which will be checked
+ * @param issue JIRA issue
+ * @param permission the permission which will be checked
+ * @param additionalMesage if the user doesn't have the permission the exception will be thrown with this message
+ * @throws PermissionException if the user doesn't have the permission the exception will be thrown
+ */
+ public static void checkPermissionWithUser(ApplicationUser appUser, final Issue issue, int permission, String additionalMesage) throws PermissionException {
+ boolean allowed = ComponentAccessor.getPermissionManager().hasPermission(permission, issue, appUser);
+ if (!allowed) {
+ throw new PermissionException(additionalMesage);
+ }
+ }
+
+ /**
+ * Checks a permission for defined user and the issue.
+ * @param appUser the user that will be checked
+ * @param issue JIRA issue
+ * @param permission the permission which will be checked
+ * @return true - the user has a permission, otherwise false
+ */
+ public static boolean hasPermission(ApplicationUser appUser, final Issue issue, int permission) {
+ return ComponentAccessor.getPermissionManager().hasPermission(permission, issue, appUser);
+ }
+
+ /**
+ * Checks a permission for defined user and the project.
+ * @param appUser the user that will be checked
+ * @param project the project that will be checked
+ * @param permission the permission which will be checked
+ * @return true - the user has a permission on the project, otherwise false
+ */
+ public static boolean hasPermission(ApplicationUser appUser, final Project project, int permission) {
+ return ComponentAccessor.getPermissionManager().hasPermission(permission, project, appUser);
+ }
+
+ /**
+ * Checks a permission for defined user and the project.
+ * @param appUser the user that will be checked
+ * @param project the project that will be checked
+ * @param permission the permission which will be checked
+ * @return false - the user hasn't a permission on the project
+ * @throws PermissionException if the user doesn't have the permission the exception will be thrown
+ */
+ public static boolean checkPermission(ApplicationUser appUser, final Project project, int permission) throws PermissionException {
+ boolean allowed = ComponentAccessor.getPermissionManager().hasPermission(permission, project, appUser);
+ if (!allowed) {
+ throw new PermissionException();
+ }
+ return allowed;
+ }
+
+ /**
+ * Check if the user has update permission on the JIRA issue
+ * @param appUser the user that will be checked
+ * @param issue JIRA issue
+ * @param jcr JiraChangeRequest
+ * @param selectedProperties OSLC properties
+ * @return the user has a permission on the issue, otherwise false
+ * @throws PermissionException if the user doesn't have the permission the exception will be thrown
+ */
+ public static boolean checkUpdatePermissions(ApplicationUser appUser, final Issue issue, final JiraChangeRequest jcr, final Map selectedProperties) throws PermissionException {
+
+ if (OSLCUtils.allowUpdate(selectedProperties, Constants.JIRA_TYPE_WATCHER)) {
+ checkPermissionWithUser(appUser, issue, Permissions.MANAGE_WATCHER_LIST, "User " + appUser.getName() + " has not permission to manage watchers!");
+ }
+
+ if (OSLCUtils.allowUpdate(selectedProperties, Constants.JIRA_TYPE_REPORTER)) {
+ checkPermissionWithUser(appUser, issue, Permissions.MODIFY_REPORTER, "User " + appUser.getName() + " has not permission to change reporter!");
+ }
+
+ if (OSLCUtils.allowUpdate(selectedProperties, Constants.DCTERMS_DUEDATE) || OSLCUtils.allowUpdate(selectedProperties, Constants.JIRA_TYPE_ORIGINAL_ESTIMATE) || OSLCUtils.allowUpdate(selectedProperties, Constants.JIRA_TYPE_REMAINING_ESTIMATE)) {
+ checkPermissionWithUser(appUser, issue, Permissions.SCHEDULE_ISSUE, "User " + appUser.getName() + " has not permission to schedule issue!");
+ }
+
+ if (OSLCUtils.allowUpdate(selectedProperties, Constants.JIRA_TYPE_STATUS)) {
+ checkPermissionWithUser(appUser, issue, Permissions.TRANSITION_ISSUE, "User " + appUser.getName() + " has not permission to change status!");
+
+ // user can change status, but could be restricted from closing issue
+ JiraIssueStatus status = jcr.getIssueStatus();
+ if (status != null) {
+ String statusId = OSLCUtils.getValueFromAbout(status.getAbout());
+ if (statusId.compareTo("6") == 0) { // 6 - Closed status
+ checkPermissionWithUser(appUser, issue, Permissions.CLOSE_ISSUE, "User " + appUser.getName() + " has not permission to close issue!");
+ }
+ }
+ // NOTE:
+ // Previous code will work only for standard Jira workflow, which contains
+ // Closed status.
+ // If there is different workflow for certain issue type, which has
+ // different end state,
+ // it won't work.
+ }
+
+ if (OSLCUtils.allowUpdate(selectedProperties, Constants.JIRA_TYPE_ASIGNEE)) {
+ checkPermissionWithUser(appUser, issue, Permissions.ASSIGN_ISSUE, "User " + appUser.getName() + " has not permission to change asignee!");
+
+ // user can assign someone to issue, but that person could be restricted
+ // from beeing assigned
+ UserManager um = ComponentAccessor.getUserManager();
+ ApplicationUser asignee = um.getUserByName(jcr.getAssignee());
+ // If incoming assignee is not defined (null), we assume that 'Unassigned'
+ // should be set.
+ // In other cases do permission checking.
+ if (asignee != null) {
+ checkPermissionWithUser(asignee, issue, Permissions.ASSIGNABLE_USER, "User " + jcr.getAssignee() + " can't be assigned to issue " + issue.getKey() + "!");
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Return true if the user has specified permission
+ * @param user Application use
+ * @param permission specific permission
+ * @return true if the user has specified permission else false
+ */
+ public static boolean hasPermission(ApplicationUser user, GlobalPermissionKey permission) {
+ return ComponentAccessor.getGlobalPermissionManager().hasPermission(permission, user);
+ }
+
+ /**
+ * Check if the user is admin
+ * @param userManager User manager
+ * @return true the user is a admin, otherwise false
+ */
+ public static boolean isSystemAdmin(com.atlassian.sal.api.user.UserManager userManager){
+ ApplicationUser loggedUser = getLoggedUser();
+ if(loggedUser != null){
+ String username = getLoggedUser().getUsername();
+ return userManager.isSystemAdmin(username);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a logged user
+ * @param request HttpServletRequest
+ * @return logged user
+ */
+ public static ApplicationUser getAppUserFromRequest(final HttpServletRequest request){
+ String userName = getUserName(request);
+
+ if(userName != null && !userName.trim().isEmpty()){
+ UserManager um = ComponentAccessor.getComponent(UserManager.class);
+ ApplicationUser appUser = um.getUserByName(userName);
+ return appUser;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns logged user
+ * @return logged user
+ */
+ public static ApplicationUser getLoggedUser() {
+ return ComponentAccessor.getJiraAuthenticationContext().getUser();
+ }
+
+ /**
+ * Returns a user name of logged a user
+ * @param request HttpServletRequest
+ * @return the user name of logged the user
+ */
+ public static String getUserName(HttpServletRequest request) {
+ Object attribute = request.getAttribute(CredentialsFilter.USERNAME_ATTRIBUTE);
+ if (attribute == null) {
+ logger.error("Session does not contain any credentials");
+ throw new WebApplicationException(401);
+ }
+ return (String) attribute;
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/oslcclient/Client.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/oslcclient/Client.java
new file mode 100644
index 0000000..54bcd4b
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/oslcclient/Client.java
@@ -0,0 +1,897 @@
+package com.ericsson.jira.oslc.oslcclient;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Map;
+
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthConsumer;
+import net.oauth.OAuthServiceProvider;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.wink.json4j.JSON;
+import org.apache.wink.json4j.JSONException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.atlassian.jira.component.ComponentAccessor;
+import com.atlassian.jira.config.properties.APKeys;
+import com.ericsson.jira.oslc.HTTP;
+import com.ericsson.jira.oslc.HTTP.OAuthPhases;
+import com.ericsson.jira.oslc.constants.JiraConstants;
+import com.ericsson.jira.oslc.resources.RootServices;
+import com.ericsson.jira.oslc.resources.ServiceProvider;
+import com.ericsson.jira.oslc.resources.ServiceProviderDialog;
+import com.ericsson.jira.oslc.utils.LogUtils;
+import com.ericsson.jira.oslc.utils.OSLCUtils;
+import com.hp.hpl.jena.rdf.model.Literal;
+import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.rdf.model.ModelFactory;
+import com.hp.hpl.jena.rdf.model.NodeIterator;
+import com.hp.hpl.jena.rdf.model.Property;
+import com.hp.hpl.jena.rdf.model.RDFNode;
+import com.hp.hpl.jena.rdf.model.Resource;
+import com.hp.hpl.jena.rdf.model.Statement;
+import com.hp.hpl.jena.rdf.model.StmtIterator;
+
+/**
+ * A class for operations with HTTP requests/reponse
+ *
+ */
+public class Client {
+ private static final String CURRENT_CLASS = "Client";
+ private static final Logger log = LoggerFactory.getLogger(Client.class);
+
+ //last status code from http response
+ private int responseCode;
+ //last phrase from http response
+ private String responsePhrase;
+
+ public int getLastResponseCode() {
+ return responseCode;
+ }
+
+ public String getLastResponsePhrase() {
+ return responsePhrase;
+ }
+
+ /**
+ * Method extracts body from http response to String instance
+ *
+ * @param is input stream (should be accessible from http response)
+ * @return string instance with response body
+ */
+ private String getResponseBody(InputStream is) {
+ String currentMethod = "getResponseBody";
+ InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+ String line = "";
+ StringBuilder sb = new StringBuilder();
+ try {
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ }
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Method gets oAuth details from rootservices url.
+ * @param rootServicesURI
+ * @return instance of class with detailed data about rootservices
+ */
+ public RootServices getRootServicesDetails(String rootServicesURI) {
+ String currentMethod = "getRootServicesDetails";
+
+ try {
+ //no authentication needed
+ HttpResponse resp = HTTP.get(rootServicesURI, "application/rdf+xml", null, null, null);
+
+ if (resp != null) {
+ responseCode = resp.getStatusLine().getStatusCode();
+ responsePhrase = resp.getStatusLine().getReasonPhrase();
+
+ if (responseCode == 200) {
+ try {
+ InputStream is = resp.getEntity().getContent();
+ Model rdfModel = null;
+ rdfModel = ModelFactory.createDefaultModel();
+ rdfModel.read(is, null);
+
+ RootServices rs = new RootServices();
+ final String ns = "http://jazz.net/xmlns/prod/jazz/jfs/1.0/";
+ rs.setOAuthAccessTokenURL(OSLCUtils.getProperty(rdfModel, ns, "oauthAccessTokenUrl"));
+ rs.setOAuthDomain(OSLCUtils.getProperty(rdfModel, ns, "oauthDomain"));
+ rs.setOAuthRequestConsumerKeyURL(OSLCUtils.getProperty(rdfModel, ns, "oauthRequestConsumerKeyUrl"));
+ rs.setOAuthRequestTokenURL(OSLCUtils.getProperty(rdfModel, ns, "oauthRequestTokenUrl"));
+ rs.setOAuthUserAuthorizationURL(OSLCUtils.getProperty(rdfModel, ns, "oauthUserAuthorizationUrl"));
+
+ return rs;
+ }
+ catch (Exception ex) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + ex.getMessage());
+ ex.printStackTrace();
+ responseCode = 500;
+ responsePhrase = "Error in parsing response data!\\nSource isn't root services or malformed!";
+ }
+ }
+ }
+ }
+ catch (ClientProtocolException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets in-bound consumer key from remote application.
+ * @param url consumer key request url
+ * @param consumerName consumer name
+ * @param secret consumer secret
+ * @param userID user id
+ * @return in-bound consumer key
+ */
+ public String getConsumerKey(String url, String consumerName, String secret, String userID) {
+ String key = null;
+ try {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ sb.append("\"name\": \"" + consumerName + "\",");
+ sb.append("\"secretType\": \"string\",");
+ sb.append("\"secret\": \"" + secret + "\",");
+ sb.append("\"trusted\": false,");
+ sb.append("\"userId\": null");
+ sb.append("}");
+
+ HttpResponse resp = null;
+
+ //no authentication needed
+ resp = HTTP.post(url, sb.toString(), null, "application/json", null, null);
+ if (resp != null) {
+ responseCode = resp.getStatusLine().getStatusCode();
+ responsePhrase = resp.getStatusLine().getReasonPhrase();
+
+ //no authentication needed
+ if (responseCode != 200) {
+ resp = HTTP.post(url, sb.toString(), null, "text/json", null, null);
+ if (resp != null) {
+ responseCode = resp.getStatusLine().getStatusCode();
+ responsePhrase = resp.getStatusLine().getReasonPhrase();
+ }
+ }
+
+ if (responseCode == 200) {
+ InputStream is = resp.getEntity().getContent();
+ String body = getResponseBody(is);
+
+ org.apache.wink.json4j.JSONObject j;
+ try {
+ j = (org.apache.wink.json4j.JSONObject) JSON.parse(body);
+ key = j.getString("key");
+ }
+ catch (NullPointerException e) {
+ log.error(CURRENT_CLASS + ".getConsumerKey Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = "Error in parsing response data!\\nSource isn't consumer key data or malformed json data!";
+ }
+ catch (JSONException e) {
+ log.error(CURRENT_CLASS + ".getConsumerKey Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = "Error in parsing response data!\\nSource isn't consumer key data or malformed json data!";
+ }
+ }
+ }
+ }
+ catch (ClientProtocolException e) {
+ log.error(CURRENT_CLASS + ".getConsumerKey Exception: " + e.getMessage());
+ e.printStackTrace();
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + ".getConsumerKey Exception: " + e.getMessage());
+ e.printStackTrace();
+ }
+
+ return key;
+ }
+
+ /**
+ * Method extracts service provider catalog uri from root services.
+ * @param rootServicesURI link to root services
+ * @return uri to service provider catalog
+ */
+ public String getServicesProviderCatalogURI(String rootServicesURI) {
+ String currentMethod = "getServicesProviderCatalogURI";
+ String result = null;
+
+ try {
+ //no authentication needed
+ HttpResponse resp = HTTP.get(rootServicesURI, "application/rdf+xml", null, null, null);
+
+ if (resp != null) {
+ responseCode = resp.getStatusLine().getStatusCode();
+ responsePhrase = resp.getStatusLine().getReasonPhrase();
+
+ if (responseCode == 200) {
+ try {
+ InputStream is = resp.getEntity().getContent();
+ Model rdfModel = null;
+ rdfModel = ModelFactory.createDefaultModel();
+ rdfModel.read(is, null);
+
+ result = OSLCUtils.getProperty(rdfModel,
+ "http://open-services.net/xmlns/cm/1.0/", "cmServiceProviders");
+ }
+ catch (Exception ex) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + ex.getMessage());
+ ex.printStackTrace();
+ responseCode = 500;
+ responsePhrase = "Error in parsing response data!\\nSource isn't root services or malformed!";
+ }
+ }
+ }
+ }
+ catch (ClientProtocolException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+
+ return result;
+ }
+
+ public RootServices getRootServicesDetailsFromCatalog(String catalogURI) {
+ return null;
+ }
+
+ /**
+ * Method gets service providers from catalog.
+ * @param catalogURI
+ * @return collection of string objects, which represents service providers in format:
+ * "title1","link1","title2","link2"..."titleN","linkN"
+ */
+ public ArrayList getServiceProviders(String catalogURI, OAuthAccessor accessor) {
+ String currentMethod = "getServiceProviders";
+ ArrayList serviceProviders = new ArrayList();
+
+ try {
+ //OAuth needed
+ HttpResponse resp = HTTP.get(
+ catalogURI, "application/rdf+xml", null, null, null,
+ accessor, null, OAuthPhases.OAUTH_PHASE_3, null);
+
+ if (resp != null) {
+ responseCode = resp.getStatusLine().getStatusCode();
+ responsePhrase = resp.getStatusLine().getReasonPhrase();
+
+ if (responseCode == 200) {
+ InputStream is = resp.getEntity().getContent();
+ Model rdfModel = null;
+ rdfModel = ModelFactory.createDefaultModel();
+ rdfModel.read(is, null);
+
+ Property prop = rdfModel.getProperty("http://open-services.net/ns/core#" + "serviceProvider");
+ StmtIterator statements = rdfModel.listStatements((Resource)null, prop, (RDFNode)null);
+
+ while (statements.hasNext()) {
+ Statement st = statements.next();
+ String providerURI = st.getObject().toString();
+
+ Property titleProp = rdfModel.createProperty("http://purl.org/dc/terms/title");
+ Statement statement = rdfModel.getProperty((Resource)st.getObject(), titleProp);
+ String title = "";
+ if (statement != null && statement.getObject() != null)
+ title = statement.getObject().toString();
+
+ int idx = title.indexOf("^^");
+ if (idx > -1)
+ title = title.substring(0, idx);
+
+ serviceProviders.add(title);
+ serviceProviders.add(providerURI);
+ }
+ }
+ }
+ }
+ catch (ClientProtocolException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+
+ return serviceProviders;
+ }
+
+ public void extractDialogs(Model rdfModel, ServiceProvider sp, boolean isCreation) {
+ String dlgType = "";
+ if (isCreation == true)
+ dlgType = "creationDialog";
+ else
+ dlgType = "selectionDialog";
+
+ Property prop = rdfModel.createProperty("http://open-services.net/ns/core#", dlgType);
+
+ NodeIterator nodes = rdfModel.listObjectsOfProperty(prop);
+ while (nodes.hasNext()) {
+ ServiceProviderDialog dlg = new ServiceProviderDialog();
+
+ Resource res = (Resource)nodes.next();
+
+ Property titleProp = rdfModel.createProperty("http://purl.org/dc/terms/title");
+ String title = OSLCUtils.getResourceLiteralValue(res, titleProp);
+ dlg.setType(title);
+
+ Property createDialogURLProperty = rdfModel.getProperty("http://open-services.net/ns/core#" + "dialog");
+ String uri = res.getPropertyResourceValue(createDialogURLProperty).getURI();
+ dlg.setLink(uri);
+
+ Property cDlgWProp = rdfModel.getProperty("http://open-services.net/ns/core#" + "hintWidth");
+ Property cDlgHProp = rdfModel.getProperty("http://open-services.net/ns/core#" + "hintHeight");
+
+ Statement st1 = res.getProperty(cDlgWProp);
+ if (st1 != null && st1.getObject() != null)
+ dlg.setWidth(st1.getObject().toString());
+
+ Statement st2 = res.getProperty(cDlgHProp);
+ if (st2 != null && st2.getObject() != null)
+ dlg.setHeight(st2.getObject().toString());
+
+ sp.addDialog(dlg, isCreation);
+ }
+ }
+
+ /**
+ * Method loads details for given service provider:
+ * - creation dialogs (type, link, width, height)
+ * - selection dialogs (type, link, width, height)
+ * @param spURL link to service provider
+ * @return service provider detailed data
+ */
+ public ServiceProvider getServiceProviderDetails(String spURL, OAuthAccessor accessor) {
+ String currentMethod = "getServiceProviderDetails";
+ ServiceProvider sp = null;
+
+ try {
+ //OAuth needed
+ HttpResponse resp = HTTP.get(
+ spURL, "application/rdf+xml", null, null, null,
+ accessor, null, OAuthPhases.OAUTH_PHASE_3, null);
+
+ if (resp != null) {
+ responseCode = resp.getStatusLine().getStatusCode();
+ responsePhrase = resp.getStatusLine().getReasonPhrase();
+
+ if (responseCode == 200) {
+ try {
+ InputStream is = resp.getEntity().getContent();
+ Model rdfModel = null;
+ rdfModel = ModelFactory.createDefaultModel();
+ rdfModel.read(is, null);
+
+ sp = new ServiceProvider();
+
+ extractDialogs(rdfModel, sp, true); //get creation dialogs
+ extractDialogs(rdfModel, sp, false); //get selection dialogs
+ }
+ catch (Exception ex) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + ex.getMessage());
+ ex.printStackTrace();
+ responseCode = 500;
+ responsePhrase = "Error in parsing response data!\\nSource isn't service provider or malformed!";
+ }
+ }
+ }
+ }
+ catch (ClientProtocolException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+
+ return sp;
+ }
+
+ /**
+ * Add link to Jira issue to remote application.
+ *
+ * @param issueKey issue key in format PROJECT-#
+ * @param projectId id of project
+ * @param url link to remote application, where issue link will be sent to
+ */
+ public void addLinkToRemoteURL(String issueKey, Long projectId, String projectName, String url, OAuthAccessor accessor) {
+ String currentMethod = "addLinkToRemoteURL";
+
+ // Construct link to issue. The very same link should be also in object in remote
+ // application.
+ String issueLinkToAdd =
+ ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL) +
+ "/rest/jirarestresource/1.0/" + projectId.toString() + "/changeRequests/" + issueKey;
+
+ Model rdfModel = null;
+ boolean found = false;
+
+ try {
+ // First get object from remote application. Response is expected to be in rdf+xml format
+ // and should contains list of jira issue links (relateChangeRequest).
+
+ //OAuth needed
+ HttpResponse resp = HTTP.get(
+ url, "application/rdf+xml", null, null, null,
+ accessor, null, OAuthPhases.OAUTH_PHASE_3, null);
+
+ if (resp != null) {
+ responseCode = resp.getStatusLine().getStatusCode();
+ responsePhrase = resp.getStatusLine().getReasonPhrase();
+
+ if (responseCode == 200) {
+ try {
+ //get response body
+ InputStream is = resp.getEntity().getContent();
+ String body = getResponseBody(is);
+
+ //initiate rdf model from response body
+ rdfModel = ModelFactory.createDefaultModel();
+ rdfModel.read(new ByteArrayInputStream(body.getBytes()), null);
+
+ //find all occurences of "relatedChangeRequest" witin the response, resp. rdf model
+ Property prop = rdfModel.createProperty("http://open-services.net/ns/cm#", "relatedChangeRequest");
+ NodeIterator it = rdfModel.listObjectsOfProperty(prop);
+
+ //go through the list
+ while (it.hasNext()) {
+ RDFNode node = it.next();
+ Resource res = (Resource)node;
+ String s = res.toString();
+ // If there is a issue link in response, which corresponds to given issue link,
+ // skip adding current link (found == true).
+ if (s.compareTo(issueLinkToAdd) == 0) {
+ found = true;
+ break;
+ }
+ }
+ }
+ catch (Exception ex) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + ex.getMessage());
+ ex.printStackTrace();
+ responseCode = 500;
+ responsePhrase = "Error in parsin object from remote application!";
+ return;
+ }
+ }
+ else {
+ //getting remote app object failed
+ return;
+ }
+ }
+ }
+ catch (ClientProtocolException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+
+ if (found == false && rdfModel != null) {
+ //given link was NOT found, so add it to model
+ try {
+ //add link to model - single property to existing main resource
+ Resource res = rdfModel.getResource(url);
+ Property addProp = rdfModel.createProperty("http://open-services.net/ns/cm#", "relatedChangeRequest");
+ Resource link = rdfModel.createResource(issueLinkToAdd);
+
+ Statement statement = rdfModel.createStatement(res, addProp, link);
+ rdfModel.add(statement);
+
+
+ if (projectName != null) {
+ Property extProjectProp = rdfModel.createProperty("http://open-services.net#", "extProject");
+ res.addProperty(extProjectProp, projectName);
+ }
+
+ //add link to model - details
+ Resource res2 = rdfModel.createResource();
+
+ Property subject = rdfModel.createProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "subject");
+ Resource subjectRes = rdfModel.createResource(url);
+
+ Property predicate = rdfModel.createProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "predicate");
+ Resource predicateRes = rdfModel.createResource("http://open-services.net/ns/cm#relatedChangeRequest");
+
+ Property object = rdfModel.createProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "object");
+ Resource objectRes = rdfModel.createResource(issueLinkToAdd);
+
+ Property type = rdfModel.createProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "type");
+ Resource typeRes = rdfModel.createResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement");
+
+ Property title = rdfModel.createProperty("http://purl.org/dc/terms/title");
+ Literal titleLit = rdfModel.createLiteral(issueKey);
+
+ res2.addProperty(subject, subjectRes);
+ res2.addProperty(predicate, predicateRes);
+ res2.addProperty(object, objectRes);
+ res2.addProperty(type, typeRes);
+ res2.addProperty(title, titleLit);
+
+ //write model back
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ rdfModel.write(os);
+ os.flush();
+
+ //send updated model back to remote application (same link)
+ HttpResponse resp = HTTP.put(url, os.toString(), "application/rdf+xml", "application/rdf+xml",
+ null, null, accessor, JiraConstants.RELATED_CHANGE_REQUEST_URL_APPENDIX,
+ OAuthPhases.OAUTH_PHASE_3);
+
+ if (resp != null) {
+ responseCode = resp.getStatusLine().getStatusCode();
+ responsePhrase = resp.getStatusLine().getReasonPhrase();
+ }
+ }
+ catch (ClientProtocolException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+ }
+ else {
+ //given link was found, so it is (probably) already linked in remote application
+ responseCode = 404;
+ responsePhrase = "Link to add already exists in remote application!";
+ }
+ }
+
+ /**
+ * Method removes link to Jira issue from remote application.
+ *
+ * @param issueKey issue key (project-id), which represent issue
+ * @param projectId project id, to which issue belongs
+ * @param url link to object in remote application, where issue link should be stored
+ */
+ public void removeLinkFromRemoteURL(String issueKey, Long projectId, String url, OAuthAccessor accessor) {
+ String currentMethod = "removeLinkFromRemoteURL";
+
+ // Construct link to issue. The very same link should be also in object in remote
+ // application.
+ String issueLinkToRemove =
+ ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL) +
+ "/rest/jirarestresource/1.0/" + projectId.toString() + "/changeRequests/" + issueKey;
+
+ Model rdfModel = null;
+ boolean found = false;
+
+ try {
+ // First get object from remote application. Response is expected to be in rdf+xml format
+ // and should contains list of jira issue links (relateChangeRequest).
+
+ //OAuth needed
+ HttpResponse resp = HTTP.get(
+ url, "application/rdf+xml", null, null, null,
+ accessor, null, OAuthPhases.OAUTH_PHASE_3, null);
+
+ responseCode = resp.getStatusLine().getStatusCode();
+ responsePhrase = resp.getStatusLine().getReasonPhrase();
+
+ if (responseCode == 200) {
+ try {
+ //get response body
+ InputStream is = resp.getEntity().getContent();
+ String body = getResponseBody(is);
+
+ //initiate rdf model from response body
+ rdfModel = ModelFactory.createDefaultModel();
+ rdfModel.read(new ByteArrayInputStream(body.getBytes()), null);
+
+ //find all occurences of "relatedChangeRequest" witin the response, resp. rdf model
+ Property prop = rdfModel.createProperty("http://open-services.net/ns/cm#", "relatedChangeRequest");
+ NodeIterator it = rdfModel.listObjectsOfProperty(prop);
+
+ //go through the list
+ while (it.hasNext()) {
+ RDFNode node = it.next();
+ Resource res = (Resource)node;
+ String s = res.toString();
+ // If there is a issue link in response, which corresponds to given issue link,
+ // it is removed from model.
+ if (s.compareTo(issueLinkToRemove) == 0) {
+ rdfModel.removeAll(null, prop, node);
+ found = true;
+ break;
+ }
+ }
+ }
+ catch (Exception ex) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + ex.getMessage());
+ ex.printStackTrace();
+ responseCode = 500;
+ responsePhrase = "Error in parsing object from remote application!";
+ return;
+ }
+ }
+ else
+ return; //http failed
+ }
+ catch (ClientProtocolException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ responsePhrase = e.getMessage();
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+ responseCode = 500;
+ //responsePhrase = "Connection to remote application failed!";
+ responsePhrase = e.getMessage();
+ return;
+ }
+
+ if (found == true && rdfModel != null) {
+ //given link was found and removed from model
+ try {
+ //write model back
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ rdfModel.write(os);
+ os.flush();
+
+ //send updated model back to remote application (same link)
+ HttpResponse resp = HTTP.put(url, os.toString(), "application/rdf+xml", "application/rdf+xml",
+ null, null, accessor, JiraConstants.RELATED_CHANGE_REQUEST_URL_APPENDIX,
+ OAuthPhases.OAUTH_PHASE_3);
+
+ responseCode = resp.getStatusLine().getStatusCode();
+ responsePhrase = resp.getStatusLine().getReasonPhrase();
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + "." + currentMethod + " Exception: " + e.getMessage());
+ e.printStackTrace();
+
+ responseCode = 500;
+ responsePhrase = "Connection to remote application failed!";
+ return;
+ }
+ }
+ else {
+ //given link wasn't found, so it (probably) doesn't exist in object in remote application
+ responseCode = 404;
+ responsePhrase = "Link to remove was not found!";
+ }
+ }
+
+
+ /**
+ * Get a OAuth request token from remote server defined by requestTokenURL
+ * @param requestTokenURL the URL for getting OAuth request token
+ * @param accessor OAuth accessor
+ * @return OAuth request token
+ */
+ private String [] oAuthGetRequestToken(String requestTokenURL, OAuthAccessor accessor) {
+
+ try {
+ HttpResponse response = HTTP.post(
+ requestTokenURL, "", null, null, null, null, accessor, null, OAuthPhases.OAUTH_PHASE_1);
+
+ if (response != null) {
+ responseCode = response.getStatusLine().getStatusCode();
+ responsePhrase = response.getStatusLine().getReasonPhrase();
+
+ if (responseCode == 200) {
+ String body = getResponseBody(response.getEntity().getContent());
+
+ String [] result = new String[2];
+ String [] elements = body.split("&");
+ int idx = elements[0].indexOf("=");
+ result[0] = elements[0].substring(idx+1, elements[0].length());
+ idx = elements[1].indexOf("=");
+ result[1] = elements[1].substring(idx+1, elements[1].length());
+ return result;
+ }
+ }
+ }
+ catch (ClientProtocolException e) {
+ log.error(CURRENT_CLASS + ".oAuthGetRequestToken Exception: " + e.getMessage());
+ e.printStackTrace();
+ return null;
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + ".oAuthGetRequestToken Exception: " + e.getMessage());
+ e.printStackTrace();
+ return null;
+ }
+
+ return null;
+ }
+
+ /**
+ * Get a OAuth access token from remote server defined by accessTokenURL
+ * @param accessTokenURL the URL for getting OAuth access token
+ * @param accessor OAuth accessor
+ * @param verifier verification code
+ * @return OAuth access token
+ */
+ private String [] oAuthGetAccessToken(String accessTokenURL, OAuthAccessor accessor, String verifier) {
+ try {
+ HttpResponse response = HTTP.get(
+ accessTokenURL, null, "text/xml", null, null, accessor, verifier, OAuthPhases.OAUTH_PHASE_2, null);
+
+ if (response != null) {
+ responseCode = response.getStatusLine().getStatusCode();
+ responsePhrase = response.getStatusLine().getReasonPhrase();
+
+ if (responseCode == 200) {
+ String body = getResponseBody(response.getEntity().getContent());
+
+ String [] result = new String[2];
+ String [] elements = body.split("&");
+ int idx = elements[0].indexOf("=");
+ result[0] = elements[0].substring(idx+1, elements[0].length());
+ idx = elements[1].indexOf("=");
+ result[1] = elements[1].substring(idx+1, elements[1].length());
+ return result;
+ }
+ }
+ }
+ catch (ClientProtocolException e) {
+ log.error(CURRENT_CLASS + ".oAuthGetAccessToken Exception: " + e.getMessage());
+ e.printStackTrace();
+ }
+ catch (IOException e) {
+ log.error(CURRENT_CLASS + ".oAuthGetAccessToken Exception: " + e.getMessage());
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * Methods starts 1st phase of OAuth dance - getting request token from provider.
+ * Note: After method gets request token back in response, OAuth dance continue on callback url
+ * (@see com.ericsson.jira.oslc.services.OAuthServices#authorizationcallbackDialog).
+ * @param consumer object with consumer information (consumer key, consumer secret, ...)
+ * @param callbackURL link, where provider responses after request token is sent
+ * @return accessor object with request token (used in next phase)
+ */
+ public OAuthAccessor oAuthDancePhase1(OAuthConsumer consumer, String callbackURL) {
+ OAuthServiceProvider provider = new OAuthServiceProvider(null, null, null);
+ OAuthConsumer c = new OAuthConsumer(callbackURL, consumer.consumerKey, consumer.consumerSecret, provider);
+ OAuthAccessor a = new OAuthAccessor(c);
+
+ String [] request = this.oAuthGetRequestToken(consumer.serviceProvider.requestTokenURL, a);
+
+ if (request != null) {
+ a.requestToken = request[0];
+ a.tokenSecret = request[1];
+ }
+
+ return a;
+ }
+
+ /**
+ * Methods performs 2nd phase of OAuth dance - getting access token.
+ * Access token is stored to accessor.
+ *
+ * @param consumer consumer object with consumer information (consumer key, consumer secret, ...)
+ * @param accessor accessor object with request token (from previous phase)
+ */
+ public void oAuthDancePhase2(OAuthConsumer consumer, OAuthAccessor accessor, String verifier) {
+ String [] access = this.oAuthGetAccessToken(
+ consumer.serviceProvider.accessTokenURL, accessor, verifier);
+
+ if (access != null) {
+ //put access key to accessor
+
+ accessor.accessToken = access[0];
+ accessor.tokenSecret = access[1];
+ }
+ }
+
+ /**
+ * Updates the remote resource
+ * @param uri the URI of remote resource
+ * @param body the body of PUT request.
+ * @param username the user name of the user who will update the remote resource
+ * @param password the password of the user who will update the remote resource
+ * @param headers HTTP header of PUT request
+ * @return the response of the request
+ * @throws ClientProtocolException
+ * @throws IOException
+ */
+ public HttpResponse updateRemoteResource(String uri, String body, String username, String password, Map headers) throws ClientProtocolException, IOException {
+ HttpResponse resp = null;
+ log.debug("Client.updateRemoteResource");
+
+ resp = HTTP.put(uri, body, "application/rdf+xml", "application/rdf+xml", username, password, null, null, null, headers);
+
+ return resp;
+ }
+
+ public HttpResponse getRemoteResource(String uri, String username, String password, Map headers ) throws ClientProtocolException, IOException {
+ HttpResponse resp = null;
+ log.debug("Client.getRemoteResource for uri: " + uri);
+ resp = HTTP.get(uri, "application/rdf+xml", username, password, headers);
+
+ return resp;
+ }
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcRdfJsonArrayProvider.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcRdfJsonArrayProvider.java
new file mode 100644
index 0000000..2c3b254
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcRdfJsonArrayProvider.java
@@ -0,0 +1,150 @@
+package com.ericsson.jira.oslc.provider;
+
+/*******************************************************************************
+ * Copyright (c) 2012 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Russell Boykin - initial API and implementation
+ * Alberto Giammaria - initial API and implementation
+ * Chris Peters - initial API and implementation
+ * Gianluca Bernardini - initial API and implementation
+ *******************************************************************************/
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.eclipse.lyo.oslc4j.core.model.OslcMediaType;
+import org.eclipse.lyo.oslc4j.provider.json4j.AbstractOslcRdfJsonProvider;
+
+@Provider
+@Produces(OslcMediaType.APPLICATION_JSON)
+@Consumes(OslcMediaType.APPLICATION_JSON)
+public class OslcRdfJsonArrayProvider
+ extends AbstractOslcRdfJsonProvider
+ implements MessageBodyReader,
+ MessageBodyWriter>
+{
+ public OslcRdfJsonArrayProvider()
+ {
+ super();
+ }
+
+ @Override
+ public long getSize(final ResponseArrayWrapper> objects,
+ final Class> type,
+ final Type genericType,
+ final Annotation[] annotation,
+ final MediaType mediaType)
+ {
+ return -1;
+ }
+
+ @Override
+ public boolean isWriteable(final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType)
+ {
+ if(ResponseArrayWrapper.class.isAssignableFrom(type)){
+ return
+ (isWriteable(
+ annotations,
+ OslcMediaType.APPLICATION_JSON_TYPE,
+ mediaType));
+ }
+ return false;
+ }
+
+ protected static boolean isWriteable(final Annotation[] annotations, final MediaType requiredMediaType, final MediaType actualMediaType) {
+
+ // When handling "recursive" writing of an OSLC Error object, we get a
+ // zero-length array of annotations
+ if ((annotations != null) && ((annotations.length > 0))) {
+ for (final Annotation annotation : annotations) {
+ if (annotation instanceof Produces) {
+ final Produces producesAnnotation = (Produces) annotation;
+
+ for (final String value : producesAnnotation.value()) {
+ if (requiredMediaType.isCompatible(MediaType.valueOf(value))) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // We do not have annotations when running from the non-web client.
+ return requiredMediaType.isCompatible(actualMediaType);
+
+ }
+
+ @Override
+ public void writeTo(final ResponseArrayWrapper> objects,
+ final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType,
+ final MultivaluedMap map,
+ final OutputStream outputStream)
+ throws IOException,
+ WebApplicationException
+ {
+
+ Object[] resources =objects.getResource();
+ writeTo(true,
+ resources,
+ mediaType,
+ map,
+ outputStream);
+ }
+ @Override
+ public boolean isReadable(final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType)
+ {
+ return (type.isArray()) &&
+ (isReadable(type.getComponentType(),
+ OslcMediaType.APPLICATION_JSON_TYPE,
+ mediaType));
+ }
+
+ @Override
+ public Object[] readFrom(final Class type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType,
+ final MultivaluedMap map,
+ final InputStream inputStream)
+ throws IOException,
+ WebApplicationException
+ {
+ return readFrom(type.getComponentType(),
+ mediaType,
+ map,
+ inputStream);
+ }
+}
\ No newline at end of file
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcRdfJsonErrorProvider.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcRdfJsonErrorProvider.java
new file mode 100644
index 0000000..45d4220
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcRdfJsonErrorProvider.java
@@ -0,0 +1,70 @@
+package com.ericsson.jira.oslc.provider;
+
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 IBM Corporation. All rights reserved. This program
+ * and the accompanying materials are made available under the terms of the
+ * Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 which
+ * accompanies this distribution. The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution
+ * License is available at http://www.eclipse.org/org/documents/edl-v10.php.
+ * Contributors: Russell Boykin - initial API and implementation Alberto
+ * Giammaria - initial API and implementation Chris Peters - initial API and
+ * implementation Gianluca Bernardini - initial API and implementation Steve
+ * Pitschke - Add support for FilteredResource and ResponseInfo
+ *******************************************************************************/
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.eclipse.lyo.oslc4j.core.model.Error;
+import org.eclipse.lyo.oslc4j.core.model.OslcMediaType;
+import org.eclipse.lyo.oslc4j.core.model.ResponseInfo;
+import org.eclipse.lyo.oslc4j.provider.json4j.AbstractOslcRdfJsonProvider;
+
+@Provider
+@Produces(OslcMediaType.APPLICATION_JSON)
+@Consumes(OslcMediaType.APPLICATION_JSON)
+public final class OslcRdfJsonErrorProvider extends AbstractOslcRdfJsonProvider implements MessageBodyWriter {
+ public OslcRdfJsonErrorProvider() {
+ super();
+ }
+
+ @Override
+ public long getSize(final Error object, final Class> type, final Type genericType, final Annotation[] annotation, final MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ public boolean isWriteable(final Class> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
+ if (Error.class.isAssignableFrom(type)) {
+ return isWriteable(type, annotations, OslcMediaType.APPLICATION_JSON_TYPE, mediaType);
+ }
+
+ return false;
+ }
+
+ @Override
+ public void writeTo(final Error object, final Class> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, final MultivaluedMap map, final OutputStream outputStream) throws IOException, WebApplicationException {
+ Object[] objects;
+ Map properties = null;
+ String descriptionURI = null;
+ String responseInfoURI = null;
+ ResponseInfo> responseInfo = null;
+
+ objects = new Object[] { object };
+
+ writeTo(objects, mediaType, map, outputStream, properties, descriptionURI, responseInfoURI, responseInfo);
+ }
+
+}
\ No newline at end of file
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcRdfJsonProvider.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcRdfJsonProvider.java
new file mode 100644
index 0000000..04dc430
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcRdfJsonProvider.java
@@ -0,0 +1,289 @@
+package com.ericsson.jira.oslc.provider;
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Russell Boykin - initial API and implementation
+ * Alberto Giammaria - initial API and implementation
+ * Chris Peters - initial API and implementation
+ * Gianluca Bernardini - initial API and implementation
+ * Steve Pitschke - Add support for FilteredResource and
+ * ResponseInfo
+ *******************************************************************************/
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.eclipse.lyo.oslc4j.core.OSLC4JConstants;
+import org.eclipse.lyo.oslc4j.core.OSLC4JUtils;
+import org.eclipse.lyo.oslc4j.core.model.AbstractResource;
+import org.eclipse.lyo.oslc4j.core.model.FilteredResource;
+import org.eclipse.lyo.oslc4j.core.model.OslcMediaType;
+import org.eclipse.lyo.oslc4j.core.model.ResponseInfo;
+import org.eclipse.lyo.oslc4j.core.model.ResponseInfoArray;
+import org.eclipse.lyo.oslc4j.core.model.ResponseInfoCollection;
+import org.eclipse.lyo.oslc4j.provider.json4j.AbstractOslcRdfJsonProvider;
+
+@Provider
+@Produces(OslcMediaType.APPLICATION_JSON)
+@Consumes(OslcMediaType.APPLICATION_JSON)
+public final class OslcRdfJsonProvider
+ extends AbstractOslcRdfJsonProvider
+ implements MessageBodyReader,
+ MessageBodyWriter
+{
+ public OslcRdfJsonProvider()
+ {
+ super();
+ }
+
+ @Override
+ public long getSize(final AbstractResource object,
+ final Class> type,
+ final Type genericType,
+ final Annotation[] annotation,
+ final MediaType mediaType)
+ {
+ return -1;
+ }
+
+ @Override
+ public boolean isWriteable(final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType)
+ {
+ Class> actualType;
+
+ if (FilteredResource.class.isAssignableFrom(type) &&
+ (genericType instanceof ParameterizedType))
+ {
+ ParameterizedType parameterizedType = (ParameterizedType)genericType;
+ Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+
+ if (actualTypeArguments.length != 1)
+ {
+ return false;
+ }
+
+ if (actualTypeArguments[0] instanceof Class>)
+ {
+ actualType = (Class>)actualTypeArguments[0];
+ }
+ else if (actualTypeArguments[0] instanceof ParameterizedType)
+ {
+ parameterizedType = (ParameterizedType)actualTypeArguments[0];
+ actualTypeArguments = parameterizedType.getActualTypeArguments();
+
+ if (actualTypeArguments.length != 1 ||
+ ! (actualTypeArguments[0] instanceof Class>))
+ {
+ return false;
+ }
+
+ actualType = (Class>)actualTypeArguments[0];
+ }
+ else if (actualTypeArguments[0] instanceof GenericArrayType)
+ {
+ GenericArrayType genericArrayType =
+ (GenericArrayType)actualTypeArguments[0];
+ Type componentType = genericArrayType.getGenericComponentType();
+
+ if (! (componentType instanceof Class>))
+ {
+ return false;
+ }
+
+ actualType = (Class>)componentType;
+ }
+ else
+ {
+ return false;
+ }
+
+ Type rawType = parameterizedType.getRawType();
+ if (URI.class.equals(actualType)
+ && (ResponseInfoCollection.class.equals(rawType) || ResponseInfoArray.class.equals(rawType)))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ actualType = type;
+ }
+
+ if (AbstractResource.class.isAssignableFrom(type)) {
+ return isWriteable(actualType, annotations, OslcMediaType.APPLICATION_JSON_TYPE, mediaType);
+ }
+
+ return false;
+ }
+
+ @Override
+ public void writeTo(final AbstractResource object,
+ final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType,
+ final MultivaluedMap map,
+ final OutputStream outputStream)
+ throws IOException,
+ WebApplicationException
+ {
+ Object[] objects;
+ Map properties = null;
+ String descriptionURI = null;
+ String responseInfoURI = null;
+ ResponseInfo> responseInfo = null;
+
+ if (object instanceof FilteredResource>)
+ {
+ final FilteredResource> filteredResource =
+ (FilteredResource>)object;
+
+ properties = filteredResource.properties();
+
+ if (filteredResource instanceof ResponseInfo>)
+ {
+ responseInfo = (ResponseInfo>)filteredResource;
+
+ String requestURI = OSLC4JUtils.resolveURI(httpServletRequest, true);
+ responseInfoURI = requestURI;
+
+ FilteredResource> container = responseInfo.getContainer();
+ if (container != null)
+ {
+ URI containerAboutURI = container.getAbout();
+ if (containerAboutURI != null)
+ {
+ descriptionURI = containerAboutURI.toString();
+ }
+ }
+ if (null == descriptionURI)
+ {
+ descriptionURI = requestURI;
+ }
+
+ final String queryString = httpServletRequest.getQueryString();
+
+ if ((queryString != null) &&
+ (isOslcQuery(queryString)))
+ {
+ responseInfoURI += "?" + queryString;
+ }
+
+ if (filteredResource instanceof ResponseInfoArray>)
+ {
+ objects = ((ResponseInfoArray>)filteredResource).array();
+ }
+ else
+ {
+ Collection> collection =
+ ((ResponseInfoCollection>)filteredResource).collection();
+
+ objects = collection.toArray(new Object[collection.size()]);
+ }
+ }
+ else
+ {
+ Object nestedObject = filteredResource.resource();
+
+ if (nestedObject instanceof Object[])
+ {
+ objects = (Object[])nestedObject;
+ }
+ else if (nestedObject instanceof Collection>)
+ {
+ objects = ((Collection>)nestedObject).toArray();
+ }
+ else
+ {
+ objects = new Object[] { object };
+ }
+ }
+ }
+ else
+ {
+ boolean isClientSide = false;
+ try {
+ httpServletRequest.getMethod();
+ } catch (RuntimeException e) {
+ isClientSide = true;
+ }
+ properties = isClientSide ? null : (Map) httpServletRequest.getAttribute(OSLC4JConstants.OSLC4J_SELECTED_PROPERTIES);
+
+ objects = new Object[] { object };
+ }
+
+ writeTo(objects,
+ mediaType,
+ map,
+ outputStream,
+ properties,
+ descriptionURI,
+ responseInfoURI,
+ responseInfo);
+ }
+
+ @Override
+ public boolean isReadable(final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType)
+ {
+ return isReadable(type,
+ OslcMediaType.APPLICATION_JSON_TYPE,
+ mediaType);
+ }
+
+ @Override
+ public AbstractResource readFrom(final Class type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType,
+ final MultivaluedMap map,
+ final InputStream inputStream)
+ throws IOException,
+ WebApplicationException
+ {
+ final AbstractResource[] objects = (AbstractResource[])readFrom(type,
+ mediaType,
+ map,
+ inputStream);
+
+ if ((objects != null) &&
+ (objects.length > 0))
+ {
+ return objects[0];
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcXmlRdfArrayProvider.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcXmlRdfArrayProvider.java
new file mode 100644
index 0000000..ef0531f
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcXmlRdfArrayProvider.java
@@ -0,0 +1,167 @@
+package com.ericsson.jira.oslc.provider;
+
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Russell Boykin - initial API and implementation
+ * Alberto Giammaria - initial API and implementation
+ * Chris Peters - initial API and implementation
+ * Gianluca Bernardini - initial API and implementation
+ *******************************************************************************/
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.eclipse.lyo.oslc4j.core.annotation.OslcNotQueryResult;
+import org.eclipse.lyo.oslc4j.core.model.OslcMediaType;
+import org.eclipse.lyo.oslc4j.provider.jena.AbstractOslcRdfXmlProvider;
+
+@Provider
+@Produces({OslcMediaType.APPLICATION_RDF_XML, OslcMediaType.APPLICATION_XML, OslcMediaType.TEXT_XML})
+@Consumes({OslcMediaType.APPLICATION_RDF_XML, OslcMediaType.APPLICATION_XML, OslcMediaType.TEXT_XML})
+public class OslcXmlRdfArrayProvider
+ extends AbstractOslcRdfXmlProvider
+ implements MessageBodyReader,
+ MessageBodyWriter>
+{
+ public OslcXmlRdfArrayProvider()
+ {
+ super();
+ }
+
+ @Override
+ public long getSize(final ResponseArrayWrapper> objects,
+ final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType)
+ {
+ return -1;
+ }
+
+ @Override
+ public boolean isWriteable(final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType)
+ {
+ if(ResponseArrayWrapper.class.isAssignableFrom(type)){
+ return
+ (isWriteable(
+ annotations,
+ mediaType,
+ OslcMediaType.APPLICATION_RDF_XML_TYPE,
+ OslcMediaType.APPLICATION_XML_TYPE,
+ OslcMediaType.TEXT_XML_TYPE,
+ OslcMediaType.TEXT_TURTLE_TYPE));
+ }
+ return false;
+ }
+
+ protected static boolean isWriteable(final Annotation[] annotations, final MediaType actualMediaType, final MediaType... requiredMediaTypes) {
+
+ // When handling "recursive" writing of an OSLC Error object, we get a
+ // zero-length array of annotations
+ if ((annotations != null) && (annotations.length > 0)) {
+ for (final Annotation annotation : annotations) {
+ if (annotation instanceof Produces) {
+ final Produces producesAnnotation = (Produces) annotation;
+
+ for (final String value : producesAnnotation.value()) {
+ for (final MediaType requiredMediaType : requiredMediaTypes) {
+ if (requiredMediaType.isCompatible(MediaType.valueOf(value))) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ // We do not have annotations when running from the non-web client.
+ return isCompatible(actualMediaType, requiredMediaTypes);
+ }
+
+ @Override
+ public void writeTo(final ResponseArrayWrapper> objects,
+ final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType,
+ final MultivaluedMap map,
+ final OutputStream outputStream)
+ throws IOException,
+ WebApplicationException
+ {
+
+ OslcNotQueryResult notQueryResult = null;
+ if(type != null && type.getComponentType() != null){
+ notQueryResult = type.getComponentType().getAnnotation(OslcNotQueryResult.class);
+ }
+
+ Object[] resources =objects.getResource();
+
+ writeTo(notQueryResult != null && notQueryResult.value() ? false : true,
+ resources,
+ mediaType,
+ map,
+ outputStream);
+ }
+
+ @Override
+ public boolean isReadable(final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType)
+ {
+ return (type.isArray()) &&
+ (isReadable(type.getComponentType(),
+ mediaType,
+ OslcMediaType.APPLICATION_RDF_XML_TYPE,
+ OslcMediaType.APPLICATION_XML_TYPE,
+ OslcMediaType.TEXT_XML_TYPE,
+ OslcMediaType.TEXT_TURTLE_TYPE));
+ }
+
+ @Override
+ public Object[] readFrom(final Class type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType,
+ final MultivaluedMap map,
+ final InputStream inputStream)
+ throws IOException,
+ WebApplicationException
+ {
+ return readFrom(type.getComponentType(),
+ mediaType,
+ map,
+ inputStream);
+ }
+}
\ No newline at end of file
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcXmlRdfErrorProvider.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcXmlRdfErrorProvider.java
new file mode 100644
index 0000000..c492a0e
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcXmlRdfErrorProvider.java
@@ -0,0 +1,68 @@
+package com.ericsson.jira.oslc.provider;
+
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 IBM Corporation. All rights reserved. This program
+ * and the accompanying materials are made available under the terms of the
+ * Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 which
+ * accompanies this distribution. The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution
+ * License is available at http://www.eclipse.org/org/documents/edl-v10.php.
+ * Contributors: Russell Boykin - initial API and implementation Alberto
+ * Giammaria - initial API and implementation Chris Peters - initial API and
+ * implementation Gianluca Bernardini - initial API and implementation Steve
+ * Pitschke - Add support for FilteredResource and ResponseInfo
+ *******************************************************************************/
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.eclipse.lyo.oslc4j.core.model.Error;
+import org.eclipse.lyo.oslc4j.core.model.OslcMediaType;
+import org.eclipse.lyo.oslc4j.core.model.ResponseInfo;
+import org.eclipse.lyo.oslc4j.provider.jena.AbstractOslcRdfXmlProvider;
+
+@Provider
+@Produces({ OslcMediaType.APPLICATION_RDF_XML, OslcMediaType.APPLICATION_XML, OslcMediaType.TEXT_XML })
+@Consumes({ OslcMediaType.APPLICATION_RDF_XML, OslcMediaType.APPLICATION_XML, OslcMediaType.TEXT_XML })
+public class OslcXmlRdfErrorProvider extends AbstractOslcRdfXmlProvider implements MessageBodyWriter {
+ public OslcXmlRdfErrorProvider() {
+ super();
+ }
+
+ @Override
+ public long getSize(final Error object, final Class> type, final Type genericType, final Annotation[] annotation, final MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ public boolean isWriteable(final Class> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
+ if (Error.class.isAssignableFrom(type)) {
+ return isWriteable(type, annotations, mediaType, OslcMediaType.APPLICATION_RDF_XML_TYPE, OslcMediaType.APPLICATION_XML_TYPE, OslcMediaType.TEXT_XML_TYPE, OslcMediaType.TEXT_TURTLE_TYPE);
+ }
+ return false;
+ }
+
+ @Override
+ public void writeTo(final Error object, final Class> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, final MultivaluedMap map, final OutputStream outputStream) throws IOException, WebApplicationException {
+ Object[] objects;
+ Map properties = null;
+ String descriptionURI = null;
+ String responseInfoURI = null;
+ ResponseInfo> responseInfo = null;
+ objects = new Object[] { object };
+
+ writeTo(objects, mediaType, map, outputStream, properties, descriptionURI, responseInfoURI, responseInfo);
+ }
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcXmlRdfProvider.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcXmlRdfProvider.java
new file mode 100644
index 0000000..2a98162
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/OslcXmlRdfProvider.java
@@ -0,0 +1,289 @@
+package com.ericsson.jira.oslc.provider;
+
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Russell Boykin - initial API and implementation
+ * Alberto Giammaria - initial API and implementation
+ * Chris Peters - initial API and implementation
+ * Gianluca Bernardini - initial API and implementation
+ * Steve Pitschke - Add support for FilteredResource and
+ * ResponseInfo
+ *******************************************************************************/
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.eclipse.lyo.oslc4j.core.OSLC4JConstants;
+import org.eclipse.lyo.oslc4j.core.OSLC4JUtils;
+import org.eclipse.lyo.oslc4j.core.model.AbstractResource;
+import org.eclipse.lyo.oslc4j.core.model.FilteredResource;
+import org.eclipse.lyo.oslc4j.core.model.OslcMediaType;
+import org.eclipse.lyo.oslc4j.core.model.ResponseInfo;
+import org.eclipse.lyo.oslc4j.core.model.ResponseInfoArray;
+import org.eclipse.lyo.oslc4j.core.model.ResponseInfoCollection;
+import org.eclipse.lyo.oslc4j.provider.jena.AbstractOslcRdfXmlProvider;
+
+@Provider
+@Produces({OslcMediaType.APPLICATION_RDF_XML, OslcMediaType.APPLICATION_XML, OslcMediaType.TEXT_XML})
+@Consumes({OslcMediaType.APPLICATION_RDF_XML, OslcMediaType.APPLICATION_XML, OslcMediaType.TEXT_XML})
+public class OslcXmlRdfProvider
+ extends AbstractOslcRdfXmlProvider
+ implements MessageBodyReader,
+ MessageBodyWriter
+{
+ public OslcXmlRdfProvider()
+ {
+ super();
+ }
+
+ @Override
+ public long getSize(final AbstractResource object,
+ final Class> type,
+ final Type genericType,
+ final Annotation[] annotation,
+ final MediaType mediaType)
+ {
+ return -1;
+ }
+
+ @Override
+ public boolean isWriteable(final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType)
+ {
+ Class> actualType;
+
+ if (FilteredResource.class.isAssignableFrom(type) &&
+ (genericType instanceof ParameterizedType))
+ {
+ ParameterizedType parameterizedType = (ParameterizedType)genericType;
+ Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+
+ if (actualTypeArguments.length != 1)
+ {
+ return false;
+ }
+
+ if (actualTypeArguments[0] instanceof Class>)
+ {
+ actualType = (Class>)actualTypeArguments[0];
+ }
+ else if (actualTypeArguments[0] instanceof ParameterizedType)
+ {
+ parameterizedType = (ParameterizedType)actualTypeArguments[0];
+ actualTypeArguments = parameterizedType.getActualTypeArguments();
+
+ if (actualTypeArguments.length != 1 ||
+ ! (actualTypeArguments[0] instanceof Class>))
+ {
+ return false;
+ }
+
+ actualType = (Class>)actualTypeArguments[0];
+ }
+ else if (actualTypeArguments[0] instanceof GenericArrayType)
+ {
+ GenericArrayType genericArrayType =
+ (GenericArrayType)actualTypeArguments[0];
+ Type componentType = genericArrayType.getGenericComponentType();
+
+ if (! (componentType instanceof Class>))
+ {
+ return false;
+ }
+
+ actualType = (Class>)componentType;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ actualType = type;
+ }
+ if(AbstractResource.class.isAssignableFrom(type)){
+ return isWriteable(actualType,
+ annotations,
+ mediaType,
+ OslcMediaType.APPLICATION_RDF_XML_TYPE,
+ OslcMediaType.APPLICATION_XML_TYPE,
+ OslcMediaType.TEXT_XML_TYPE,
+ OslcMediaType.TEXT_TURTLE_TYPE);
+ }
+ return false;
+ }
+
+ @Override
+ public void writeTo(final AbstractResource object,
+ final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType,
+ final MultivaluedMap map,
+ final OutputStream outputStream)
+ throws IOException,
+ WebApplicationException
+ {
+ Object[] objects;
+ Map properties = null;
+ String descriptionURI = null;
+ String responseInfoURI = null;
+ ResponseInfo> responseInfo = null;
+
+ if (object instanceof FilteredResource>)
+ {
+ final FilteredResource> filteredResource =
+ (FilteredResource>)object;
+
+ properties = filteredResource.properties();
+
+ if (filteredResource instanceof ResponseInfo>)
+ {
+ descriptionURI = OSLC4JUtils.resolveURI(httpServletRequest, true);
+ responseInfoURI = descriptionURI;
+
+ final String queryString = httpServletRequest.getQueryString();
+
+ if ((queryString != null) &&
+ (isOslcQuery(queryString)))
+ {
+ responseInfoURI += "?" + queryString;
+ }
+
+ if (filteredResource instanceof ResponseInfoArray>)
+ {
+ objects = ((ResponseInfoArray>)filteredResource).array();
+ }
+ else
+ {
+ Collection> collection =
+ ((ResponseInfoCollection>)filteredResource).collection();
+
+ objects = collection.toArray(new Object[collection.size()]);
+ }
+
+ responseInfo = (ResponseInfo>)filteredResource;
+ }
+ else
+ {
+ Object nestedObject = filteredResource.resource();
+
+ if (nestedObject instanceof Object[])
+ {
+ objects = (Object[])nestedObject;
+ }
+ else if (nestedObject instanceof Collection>)
+ {
+ objects = ((Collection>)nestedObject).toArray();
+ }
+ else
+ {
+ objects = new Object[] { object };
+ }
+ }
+ }
+ else
+ {
+ objects = new Object[] { object };
+ boolean isClientSide = false;
+
+ try {
+ httpServletRequest.getMethod();
+ } catch (RuntimeException e) {
+ isClientSide = true;
+ }
+
+ properties = isClientSide ? null : (Map) httpServletRequest.getAttribute(OSLC4JConstants.OSLC4J_SELECTED_PROPERTIES);
+ }
+
+
+
+ writeTo(objects,
+ mediaType,
+ map,
+ outputStream,
+ properties,
+ descriptionURI,
+ responseInfoURI,
+ responseInfo);
+ }
+
+ @Override
+ public boolean isReadable(final Class> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType)
+ {
+ return isReadable(type,
+ mediaType,
+ OslcMediaType.APPLICATION_RDF_XML_TYPE,
+ OslcMediaType.APPLICATION_XML_TYPE,
+ OslcMediaType.TEXT_XML_TYPE,
+ OslcMediaType.TEXT_TURTLE_TYPE);
+ }
+
+ @Override
+ public AbstractResource readFrom(final Class type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType,
+ final MultivaluedMap map,
+ final InputStream inputStream)
+ throws IOException,
+ WebApplicationException
+ {
+ final AbstractResource[] objects = (AbstractResource[])readFrom(type,
+ mediaType,
+ map,
+ inputStream);
+
+ if ((objects != null) &&
+ (objects.length > 0))
+ {
+ // Fix for defect 412755
+ if (OSLC4JUtils.useBeanClassForParsing() && objects.length > 1) {
+ throw new IOException("Object length should not be greater than 1.");
+ }
+
+ return objects[0];
+ }
+
+ return null;
+ }
+
+ @Override
+ public Object[] readFrom(Class> type, MediaType mediaType, MultivaluedMap map, InputStream inputStream) throws WebApplicationException {
+ return super.readFrom(type, mediaType, map, inputStream);
+ }
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/ResponseArrayWrapper.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/ResponseArrayWrapper.java
new file mode 100644
index 0000000..2aea68e
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/provider/ResponseArrayWrapper.java
@@ -0,0 +1,48 @@
+package com.ericsson.jira.oslc.provider;
+
+/*
+* Copyright (C) 2015 Ericsson AB. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import org.eclipse.lyo.oslc4j.core.model.AbstractResource;
+
+/**
+ * It's a wrapper for our resources which are used by RS MessageWriter.
+ * It's used for a array of Jira Change Requests
+ */
+public class ResponseArrayWrapper extends AbstractResource{
+ private T[] resource;
+
+ public T[] getResource() {
+ return resource;
+ }
+
+ public void setResource(T[] resource) {
+ this.resource = resource;
+ }
+
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/resources/ChangeRequest.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/resources/ChangeRequest.java
new file mode 100644
index 0000000..ddb26b3
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/resources/ChangeRequest.java
@@ -0,0 +1,871 @@
+/*******************************************************************************
+ * Copyright (c) 2012 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Russell Boykin - initial API and implementation
+ * Alberto Giammaria - initial API and implementation
+ * Chris Peters - initial API and implementation
+ * Gianluca Bernardini - initial API and implementation
+ *******************************************************************************/
+package com.ericsson.jira.oslc.resources;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.lyo.oslc4j.core.annotation.OslcAllowedValue;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcDescription;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcName;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcOccurs;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcPropertyDefinition;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcRange;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcReadOnly;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcRepresentation;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcTitle;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcValueType;
+import org.eclipse.lyo.oslc4j.core.model.AbstractResource;
+import org.eclipse.lyo.oslc4j.core.model.Link;
+import org.eclipse.lyo.oslc4j.core.model.Occurs;
+import org.eclipse.lyo.oslc4j.core.model.OslcConstants;
+import org.eclipse.lyo.oslc4j.core.model.Representation;
+import org.eclipse.lyo.oslc4j.core.model.ValueType;
+
+import com.ericsson.jira.oslc.Constants;
+
+/**
+ * It represents OSLC Change Request. It serves as data class which is sending between OSLC system
+ * It contains the basic parameters of OSLC resource
+ */
+public class ChangeRequest
+ extends AbstractResource
+{
+ private final Set affectedByDefects = new HashSet ();
+ private final Set affectsPlanItems = new HashSet ();
+ private final Set affectsRequirements = new HashSet ();
+ private final Set affectsTestResults = new HashSet ();
+ private final Set blocksTestExecutionRecords = new HashSet ();
+ private final List contributors = new ArrayList();
+ private final Set dctermsTypes = new TreeSet();
+ private final Set implementsRequirements = new HashSet ();
+ private final Set relatedChangeRequests = new HashSet ();
+ private final Set relatedResources = new HashSet ();
+ private final Set relatedTestCases = new HashSet ();
+ private final Set relatedTestExecutionRecords = new HashSet ();
+ private final Set relatedTestPlans = new HashSet ();
+ private final Set relatedTestScripts = new HashSet ();
+ private final Set subjects = new TreeSet();
+ private final Set testedByTestCases = new HashSet ();
+ private final Set tracksChangeSets = new HashSet ();
+ private final Set tracksRequirements = new HashSet ();
+ private final Set rdfTypes = new TreeSet();
+
+ private Boolean approved;
+ private Boolean closed;
+ private Date closeDate;
+ private Date created;
+ private String description;
+ private URI discussedBy;
+ private Boolean fixed;
+ private String identifier;
+ private Boolean inProgress;
+ private URI instanceShape;
+ private Date modified;
+ private Boolean reviewed;
+ private URI serviceProvider;
+ private Severity severity;
+ private String shortTitle;
+ private String status;
+ private String title;
+ private Boolean verified;
+
+ public ChangeRequest()
+ throws URISyntaxException
+ {
+ super();
+
+ rdfTypes.add(new URI(Constants.TYPE_CHANGE_REQUEST));
+ }
+
+ public ChangeRequest(final URI about)
+ throws URISyntaxException
+ {
+ super(about);
+
+ rdfTypes.add(new URI(Constants.TYPE_CHANGE_REQUEST));
+ }
+
+ public void addAffectedByDefect(final Link affectedByDefect)
+ {
+ this.affectedByDefects.add(affectedByDefect);
+ }
+
+ public void addAffectsPlanItem(final Link affectsPlanItem)
+ {
+ this.affectsPlanItems.add(affectsPlanItem);
+ }
+
+ public void addAffectsRequirement(final Link affectsRequirement)
+ {
+ this.affectsRequirements.add(affectsRequirement);
+ }
+
+ public void addAffectsTestResult(final Link affectsTestResult)
+ {
+ this.affectsTestResults.add(affectsTestResult);
+ }
+
+ public void addBlocksTestExecutionRecord(final Link blocksTestExecutionRecord)
+ {
+ this.blocksTestExecutionRecords.add(blocksTestExecutionRecord);
+ }
+
+ public void addContributor(final Person contributor)
+ {
+ this.contributors.add(contributor);
+ }
+
+
+ public void addDctermsType(final String dctermsType)
+ {
+ this.dctermsTypes.add(Type.fromString(dctermsType));
+ }
+
+ public void addImplementsRequirement(final Link implementsRequirement)
+ {
+ this.implementsRequirements.add(implementsRequirement);
+ }
+
+ public void addRdfType(final URI rdfType)
+ {
+ this.rdfTypes.add(rdfType);
+ }
+
+ public void addRelatedChangeRequest(final Link relatedChangeRequest)
+ {
+ this.relatedChangeRequests.add(relatedChangeRequest);
+ }
+
+ public void addRelatedResource(final Link relatedResource)
+ {
+ this.relatedResources.add(relatedResource);
+ }
+
+ public void addRelatedTestCase(final Link relatedTestCase)
+ {
+ this.relatedTestCases.add(relatedTestCase);
+ }
+
+ public void addRelatedTestExecutionRecord(final Link relatedTestExecutionRecord)
+ {
+ this.relatedTestExecutionRecords.add(relatedTestExecutionRecord);
+ }
+
+ public void addRelatedTestPlan(final Link relatedTestPlan)
+ {
+ this.relatedTestPlans.add(relatedTestPlan);
+ }
+
+ public void addRelatedTestScript(final Link relatedTestScript)
+ {
+ this.relatedTestScripts.add(relatedTestScript);
+ }
+
+ public void addSubject(final String subject)
+ {
+ this.subjects.add(subject);
+ }
+
+ public void addTestedByTestCase(final Link testedByTestCase)
+ {
+ this.testedByTestCases.add(testedByTestCase);
+ }
+
+ public void addTracksChangeSet(final Link tracksChangeSet)
+ {
+ this.tracksChangeSets.add(tracksChangeSet);
+ }
+
+ public void addTracksRequirement(final Link tracksRequirement)
+ {
+ this.tracksRequirements.add(tracksRequirement);
+ }
+
+ @OslcDescription("Change request is affected by a reported defect.")
+ @OslcName("affectedByDefect")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "affectedByDefect")
+ @OslcRange(Constants.TYPE_CHANGE_REQUEST)
+ @OslcReadOnly(false)
+ @OslcTitle("Affected By Defects")
+ public Link[] getAffectedByDefects()
+ {
+ return affectedByDefects.toArray(new Link[affectedByDefects.size()]);
+ }
+
+ @OslcDescription("Change request affects a plan item. ")
+ @OslcName("affectsPlanItem")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "affectsPlanItem")
+ @OslcRange(Constants.TYPE_CHANGE_REQUEST)
+ @OslcReadOnly(false)
+ @OslcTitle("Affects Plan Items")
+ public Link[] getAffectsPlanItems()
+ {
+ return affectsPlanItems.toArray(new Link[affectsPlanItems.size()]);
+ }
+
+ @OslcDescription("Change request affecting a Requirement.")
+ @OslcName("affectsRequirement")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "affectsRequirement")
+ @OslcRange(Constants.TYPE_REQUIREMENT)
+ @OslcReadOnly(false)
+ @OslcTitle("Affects Requirements")
+ public Link[] getAffectsRequirements()
+ {
+ return affectsRequirements.toArray(new Link[affectsRequirements.size()]);
+ }
+
+ @OslcDescription("Associated QM resource that is affected by this Change Request.")
+ @OslcName("affectsTestResult")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "affectsTestResult")
+ @OslcRange(Constants.TYPE_TEST_RESULT)
+ @OslcReadOnly(false)
+ @OslcTitle("Affects Test Results")
+ public Link[] getAffectsTestResults()
+ {
+ return affectsTestResults.toArray(new Link[affectsTestResults.size()]);
+ }
+
+ @OslcDescription("Associated QM resource that is blocked by this Change Request.")
+ @OslcName("blocksTestExecutionRecord")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "blocksTestExecutionRecord")
+ @OslcRange(Constants.TYPE_TEST_EXECUTION_RECORD)
+ @OslcReadOnly(false)
+ @OslcTitle("Blocks Test Execution Records")
+ public Link[] getBlocksTestExecutionRecords()
+ {
+ return blocksTestExecutionRecords.toArray(new Link[blocksTestExecutionRecords.size()]);
+ }
+
+ @OslcDescription("The date at which no further activity or work is intended to be conducted. ")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "closeDate")
+ @OslcReadOnly
+ @OslcTitle("Close Date")
+ public Date getCloseDate()
+ {
+ return closeDate;
+ }
+
+ @OslcDescription("The person(s) who are responsible for the work needed to complete the change request.")
+ @OslcName("contributor")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "contributor")
+ @OslcRepresentation(Representation.Inline)
+ @OslcValueType(ValueType.LocalResource)
+ @OslcRange(Constants.TYPE_PERSON)
+ @OslcTitle("Contributors")
+ public List getContributors()
+ {
+ return contributors;
+ }
+
+ @OslcDescription("Timestamp of resource creation.")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "created")
+ @OslcReadOnly
+ @OslcTitle("Created")
+ public Date getCreated()
+ {
+ return created;
+ }
+
+
+ @OslcAllowedValue({"Defect", "Task", "Story", "Bug Report", "Feature Request"})
+ @OslcDescription("A short string representation for the type, example 'Defect'.")
+ @OslcName("type")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "type")
+ @OslcTitle("Types")
+ public String[] getDctermsTypes()
+ {
+ final String[] result = new String[dctermsTypes.size()];
+
+ int index = 0;
+
+ for (final Type type : dctermsTypes)
+ {
+ result[index++] = type.toString();
+ }
+
+ return result;
+ }
+
+ @OslcDescription("Descriptive text (reference: Dublin Core) about resource represented as rich text in XHTML content.")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "description")
+ @OslcTitle("Description")
+ @OslcValueType(ValueType.XMLLiteral)
+ public String getDescription()
+ {
+ return description;
+ }
+
+ @OslcDescription("A series of notes and comments about this change request.")
+ @OslcPropertyDefinition(OslcConstants.OSLC_CORE_NAMESPACE + "discussedBy")
+ @OslcRange(Constants.TYPE_DISCUSSION)
+ @OslcTitle("Discussed By")
+ public URI getDiscussedBy()
+ {
+ return discussedBy;
+ }
+
+ @OslcDescription("A unique identifier for a resource. Assigned by the service provider when a resource is created. Not intended for end-user display.")
+ @OslcOccurs(Occurs.ExactlyOne)
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "identifier")
+ @OslcReadOnly
+ @OslcTitle("Identifier")
+ public String getIdentifier()
+ {
+ return identifier;
+ }
+
+ @OslcDescription("Implements associated Requirement.")
+ @OslcName("implementsRequirement")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "implementsRequirement")
+ @OslcRange(Constants.TYPE_REQUIREMENT)
+ @OslcReadOnly(false)
+ @OslcTitle("Implements Requirements")
+ public Link[] getImplementsRequirements()
+ {
+ return implementsRequirements.toArray(new Link[implementsRequirements.size()]);
+ }
+
+ @OslcDescription("Resource Shape that provides hints as to resource property value-types and allowed values. ")
+ @OslcPropertyDefinition(OslcConstants.OSLC_CORE_NAMESPACE + "instanceShape")
+ @OslcRange(OslcConstants.TYPE_RESOURCE_SHAPE)
+ @OslcTitle("Instance Shape")
+ public URI getInstanceShape()
+ {
+ return instanceShape;
+ }
+
+ @OslcDescription("Timestamp last latest resource modification.")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "modified")
+ @OslcReadOnly
+ @OslcTitle("Modified")
+ public Date getModified()
+ {
+ return modified;
+ }
+
+ @OslcDescription("The resource type URIs.")
+ @OslcName("type")
+ @OslcPropertyDefinition(OslcConstants.RDF_NAMESPACE + "type")
+ @OslcTitle("Types")
+ public URI[] getRdfTypes()
+ {
+ return rdfTypes.toArray(new URI[rdfTypes.size()]);
+ }
+
+ @OslcDescription("This relationship is loosely coupled and has no specific meaning.")
+ @OslcName("relatedChangeRequest")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "relatedChangeRequest")
+ @OslcRange(Constants.TYPE_CHANGE_REQUEST)
+ @OslcReadOnly(false)
+ @OslcTitle("Related Change Requests")
+ public Link[] getRelatedChangeRequests()
+ {
+ return relatedChangeRequests.toArray(new Link[relatedChangeRequests.size()]);
+ }
+
+ @OslcDescription("Related OSLC resources of any type.")
+ @OslcName("relatedResource")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "relatedResource")
+ @OslcTitle("Related Resources")
+ public Link[] getRelatedResources()
+ {
+ return relatedResources.toArray(new Link[relatedResources.size()]);
+ }
+
+ @OslcDescription("Related QM test case resource.")
+ @OslcName("relatedTestCase")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "relatedTestCase")
+ @OslcRange(Constants.TYPE_TEST_CASE)
+ @OslcReadOnly(false)
+ @OslcTitle("Related Test Cases")
+ public Link[] getRelatedTestCases()
+ {
+ return relatedTestCases.toArray(new Link[relatedTestCases.size()]);
+ }
+
+ @OslcDescription("Related to a QM test execution resource.")
+ @OslcName("relatedTestExecutionRecord")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "relatedTestExecutionRecord")
+ @OslcRange(Constants.TYPE_TEST_EXECUTION_RECORD)
+ @OslcReadOnly(false)
+ @OslcTitle("Related Test Execution Records")
+ public Link[] getRelatedTestExecutionRecords()
+ {
+ return relatedTestExecutionRecords.toArray(new Link[relatedTestExecutionRecords.size()]);
+ }
+
+ @OslcDescription("Related QM test plan resource.")
+ @OslcName("relatedTestPlan")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "relatedTestPlan")
+ @OslcRange(Constants.TYPE_TEST_PLAN)
+ @OslcReadOnly(false)
+ @OslcTitle("Related Test Plans")
+ public Link[] getRelatedTestPlans()
+ {
+ return relatedTestPlans.toArray(new Link[relatedTestPlans.size()]);
+ }
+
+ @OslcDescription("Related QM test script resource.")
+ @OslcName("relatedTestScript")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "relatedTestScript")
+ @OslcRange(Constants.TYPE_TEST_SCRIPT)
+ @OslcReadOnly(false)
+ @OslcTitle("Related Test Scripts")
+ public Link[] getRelatedTestScripts()
+ {
+ return relatedTestScripts.toArray(new Link[relatedTestScripts.size()]);
+ }
+
+ @OslcDescription("The scope of a resource is a URI for the resource's OSLC Service Provider.")
+ @OslcPropertyDefinition(OslcConstants.OSLC_CORE_NAMESPACE + "serviceProvider")
+ @OslcRange(OslcConstants.TYPE_SERVICE_PROVIDER)
+ @OslcTitle("Service Provider")
+ public URI getServiceProvider()
+ {
+ return serviceProvider;
+ }
+
+ @OslcAllowedValue({"Unclassified", "Minor", "Normal", "Major", "Critical", "Blocker"})
+ @OslcDescription("Severity of change request.")
+ @OslcOccurs(Occurs.ExactlyOne)
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "severity")
+ @OslcTitle("Severity")
+ public String getSeverity()
+ {
+ return (severity == null)?null:severity.toString();
+ }
+
+ @OslcDescription("Short name identifying a resource, often used as an abbreviated identifier for presentation to end-users.")
+ @OslcPropertyDefinition(OslcConstants.OSLC_CORE_NAMESPACE + "shortTitle")
+ @OslcTitle("Short Title")
+ @OslcValueType(ValueType.XMLLiteral)
+ public String getShortTitle()
+ {
+ return shortTitle;
+ }
+
+ @OslcDescription("Used to indicate the status of the change request based on values defined by the service provider. Most often a read-only property. Some possible values may include: 'Submitted', 'Done', 'InProgress', etc.")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "status")
+ @OslcTitle("Status")
+ public String getStatus()
+ {
+ return status;
+ }
+
+ @OslcDescription("Tag or keyword for a resource. Each occurrence of a dcterms:subject property denotes an additional tag for the resource.")
+ @OslcName("subject")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "subject")
+ @OslcReadOnly(false)
+ @OslcTitle("Subjects")
+ public String[] getSubjects()
+ {
+ return subjects.toArray(new String[subjects.size()]);
+ }
+
+ @OslcDescription("Test case by which this change request is tested.")
+ @OslcName("testedByTestCase")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "testedByTestCase")
+ @OslcRange(Constants.TYPE_TEST_CASE)
+ @OslcReadOnly(false)
+ @OslcTitle("Tested by Test Cases")
+ public Link[] getTestedByTestCases()
+ {
+ return testedByTestCases.toArray(new Link[testedByTestCases.size()]);
+ }
+
+ @OslcDescription("Title (reference: Dublin Core) or often a single line summary of the resource represented as rich text in XHTML content.")
+ @OslcOccurs(Occurs.ExactlyOne)
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "title")
+ @OslcTitle("Title")
+ @OslcValueType(ValueType.XMLLiteral)
+ public String getTitle()
+ {
+ return title;
+ }
+
+ @OslcDescription("Tracks SCM change set resource.")
+ @OslcName("tracksChangeSet")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "tracksChangeSet")
+ @OslcRange(Constants.TYPE_CHANGE_SET)
+ @OslcReadOnly(false)
+ @OslcTitle("Tracks Change Sets")
+ public Link[] getTracksChangeSets()
+ {
+ return tracksChangeSets.toArray(new Link[tracksChangeSets.size()]);
+ }
+
+ @OslcDescription("Tracks the associated Requirement or Requirement ChangeSet resources.")
+ @OslcName("tracksRequirement")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "tracksRequirement")
+ @OslcRange(Constants.TYPE_REQUIREMENT)
+ @OslcReadOnly(false)
+ @OslcTitle("Tracks Requirements")
+ public Link[] getTracksRequirements()
+ {
+ return tracksRequirements.toArray(new Link[tracksRequirements.size()]);
+ }
+
+ @OslcDescription("Whether or not the Change Request has been approved.")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "approved")
+ @OslcReadOnly
+ @OslcTitle("Approved")
+ public Boolean isApproved()
+ {
+ return approved;
+ }
+
+ @OslcDescription("Whether or not the Change Request is completely done, no further fixes or fix verification is needed.")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "closed")
+ @OslcReadOnly
+ @OslcTitle("Closed")
+ public Boolean isClosed()
+ {
+ return closed;
+ }
+
+ @OslcDescription("Whether or not the Change Request has been fixed.")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "fixed")
+ @OslcReadOnly
+ @OslcTitle("Fixed")
+ public Boolean isFixed()
+ {
+ return fixed;
+ }
+
+ @OslcDescription("Whether or not the Change Request in a state indicating that active work is occurring. If oslc_cm:inprogress is true, then oslc_cm:fixed and oslc_cm:closed must also be false.")
+ @OslcName("inprogress")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "inprogress")
+ @OslcReadOnly
+ @OslcTitle("In Progress")
+ public Boolean isInProgress()
+ {
+ return inProgress;
+ }
+
+ @OslcDescription("Whether or not the Change Request has been reviewed.")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "reviewed")
+ @OslcReadOnly
+ @OslcTitle("Reviewed")
+ public Boolean isReviewed()
+ {
+ return reviewed;
+ }
+
+ @OslcDescription("Whether or not the resolution or fix of the Change Request has been verified.")
+ @OslcPropertyDefinition(Constants.CHANGE_MANAGEMENT_NAMESPACE + "verified")
+ @OslcReadOnly
+ @OslcTitle("Verified")
+ public Boolean isVerified()
+ {
+ return verified;
+ }
+
+ public void setAffectedByDefects(final Link[] affectedByDefects)
+ {
+ this.affectedByDefects.clear();
+
+ if (affectedByDefects != null)
+ {
+ this.affectedByDefects.addAll(Arrays.asList(affectedByDefects));
+ }
+ }
+
+ public void setAffectsPlanItems(final Link[] affectsPlanItems)
+ {
+ this.affectsPlanItems.clear();
+
+ if (affectsPlanItems != null)
+ {
+ this.affectsPlanItems.addAll(Arrays.asList(affectsPlanItems));
+ }
+ }
+
+ public void setAffectsRequirements(final Link[] affectsRequirements)
+ {
+ this.affectsRequirements.clear();
+
+ if (affectsRequirements != null)
+ {
+ this.affectsRequirements.addAll(Arrays.asList(affectsRequirements));
+ }
+ }
+
+ public void setAffectsTestResults(final Link[] affectsTestResults)
+ {
+ this.affectsTestResults.clear();
+
+ if (affectsTestResults != null)
+ {
+ this.affectsTestResults.addAll(Arrays.asList(affectsTestResults));
+ }
+ }
+
+ public void setApproved(final Boolean approved)
+ {
+ this.approved = approved;
+ }
+
+ public void setBlocksTestExecutionRecords(final Link[] blocksTestExecutionRecords)
+ {
+ this.blocksTestExecutionRecords.clear();
+
+ if (blocksTestExecutionRecords != null)
+ {
+ this.blocksTestExecutionRecords.addAll(Arrays.asList(blocksTestExecutionRecords));
+ }
+ }
+
+ public void setClosed(final Boolean closed)
+ {
+ this.closed = closed;
+ }
+
+ public void setCloseDate(final Date closeDate)
+ {
+ this.closeDate = closeDate;
+ }
+
+ public void setContributors(final List contributors)
+ {
+ this.contributors.clear();
+
+ if (contributors != null)
+ {
+ this.contributors.addAll(contributors);
+ }
+ }
+
+ public void setCreated(final Date created)
+ {
+ this.created = created;
+ }
+
+
+ public void setDctermsTypes(final String[] dctermsTypes)
+ {
+ this.dctermsTypes.clear();
+
+ if (dctermsTypes != null)
+ {
+ for (final String type : dctermsTypes)
+ {
+ this.dctermsTypes.add(Type.fromString(type));
+ }
+ }
+ }
+
+ public void setDescription(final String description)
+ {
+ this.description = description;
+ }
+
+ public void setDiscussedBy(final URI discussedBy)
+ {
+ this.discussedBy = discussedBy;
+ }
+
+ public void setFixed(final Boolean fixed)
+ {
+ this.fixed = fixed;
+ }
+
+ public void setIdentifier(final String identifier)
+ {
+ this.identifier = identifier;
+ }
+
+ public void setImplementsRequirements(final Link[] implementsRequirements)
+ {
+ this.implementsRequirements.clear();
+
+ if (implementsRequirements != null)
+ {
+ this.implementsRequirements.addAll(Arrays.asList(implementsRequirements));
+ }
+ }
+
+ public void setInProgress(final Boolean inProgress)
+ {
+ this.inProgress = inProgress;
+ }
+
+ public void setInstanceShape(final URI instanceShape)
+ {
+ this.instanceShape = instanceShape;
+ }
+
+ public void setModified(final Date modified)
+ {
+ this.modified = modified;
+ }
+
+ public void setRdfTypes(final URI[] rdfTypes)
+ {
+ this.rdfTypes.clear();
+
+ if (rdfTypes != null)
+ {
+ this.rdfTypes.addAll(Arrays.asList(rdfTypes));
+ }
+ }
+
+ public void setRelatedChangeRequests(final Link[] relatedChangeRequests)
+ {
+ this.relatedChangeRequests.clear();
+
+ if (relatedChangeRequests != null)
+ {
+ this.relatedChangeRequests.addAll(Arrays.asList(relatedChangeRequests));
+ }
+ }
+
+ public void setRelatedResources(final Link[] relatedResources)
+ {
+ this.relatedResources.clear();
+
+ if (relatedResources != null)
+ {
+ this.relatedResources.addAll(Arrays.asList(relatedResources));
+ }
+ }
+
+ public void setRelatedTestCases(final Link[] relatedTestCases)
+ {
+ this.relatedTestCases.clear();
+
+ if (relatedTestCases != null)
+ {
+ this.relatedTestCases.addAll(Arrays.asList(relatedTestCases));
+ }
+ }
+
+ public void setRelatedTestExecutionRecords(final Link[] relatedTestExecutionRecords)
+ {
+ this.relatedTestExecutionRecords.clear();
+
+ if (relatedTestExecutionRecords != null)
+ {
+ this.relatedTestExecutionRecords.addAll(Arrays.asList(relatedTestExecutionRecords));
+ }
+ }
+
+ public void setRelatedTestPlans(final Link[] relatedTestPlans)
+ {
+ this.relatedTestPlans.clear();
+
+ if (relatedTestPlans != null)
+ {
+ this.relatedTestPlans.addAll(Arrays.asList(relatedTestPlans));
+ }
+ }
+
+ public void setRelatedTestScripts(final Link[] relatedTestScripts)
+ {
+ this.relatedTestScripts.clear();
+
+ if (relatedTestScripts != null)
+ {
+ this.relatedTestScripts.addAll(Arrays.asList(relatedTestScripts));
+ }
+ }
+
+ public void setReviewed(final Boolean reviewed)
+ {
+ this.reviewed = reviewed;
+ }
+
+ public void setServiceProvider(final URI serviceProvider)
+ {
+ this.serviceProvider = serviceProvider;
+ }
+
+ public void setSeverity(final String severity)
+ {
+ this.severity = Severity.valueOf(severity);
+ }
+
+ public void setShortTitle(final String shortTitle)
+ {
+ this.shortTitle = shortTitle;
+ }
+
+ public void setStatus(final String status)
+ {
+ this.status = status;
+ }
+
+ public void setSubjects(final String[] subjects)
+ {
+ this.subjects.clear();
+
+ if (subjects != null)
+ {
+ this.subjects.addAll(Arrays.asList(subjects));
+ }
+ }
+
+ public void setTestedByTestCases(final Link[] testedByTestCases)
+ {
+ this.testedByTestCases.clear();
+
+ if (testedByTestCases != null)
+ {
+ this.testedByTestCases.addAll(Arrays.asList(testedByTestCases));
+ }
+ }
+
+ public void setTitle(final String title)
+ {
+ this.title = title;
+ }
+
+ public void setTracksChangeSets(final Link[] tracksChangeSets)
+ {
+ this.tracksChangeSets.clear();
+
+ if (tracksChangeSets != null)
+ {
+ this.tracksChangeSets.addAll(Arrays.asList(tracksChangeSets));
+ }
+ }
+
+ public void setTracksRequirements(final Link[] tracksRequirements)
+ {
+ this.tracksRequirements.clear();
+
+ if (tracksRequirements != null)
+ {
+ this.tracksRequirements.addAll(Arrays.asList(tracksRequirements));
+ }
+ }
+
+ public void setVerified(final Boolean verified)
+ {
+ this.verified = verified;
+ }
+
+}
diff --git a/oslcjira/src/main/java/com/ericsson/jira/oslc/resources/JiraChangeRequest.java b/oslcjira/src/main/java/com/ericsson/jira/oslc/resources/JiraChangeRequest.java
new file mode 100644
index 0000000..d93e8ff
--- /dev/null
+++ b/oslcjira/src/main/java/com/ericsson/jira/oslc/resources/JiraChangeRequest.java
@@ -0,0 +1,1077 @@
+/*******************************************************************************
+ * Copyright (c) 2012 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Sam Padgett - initial API and implementation
+ * Michael Fiedler - adapted for OSLC4J
+ *
+ *******************************************************************************/
+package com.ericsson.jira.oslc.resources;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.lyo.oslc4j.core.annotation.OslcDescription;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcName;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcNamespace;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcOccurs;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcPropertyDefinition;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcReadOnly;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcResourceShape;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcTitle;
+import org.eclipse.lyo.oslc4j.core.model.Link;
+import org.eclipse.lyo.oslc4j.core.model.Occurs;
+import org.eclipse.lyo.oslc4j.core.model.OslcConstants;
+
+import com.atlassian.jira.bc.project.component.ProjectComponent;
+import com.atlassian.jira.component.ComponentAccessor;
+import com.atlassian.jira.issue.CustomFieldManager;
+import com.atlassian.jira.issue.Issue;
+import com.atlassian.jira.issue.MutableIssue;
+import com.atlassian.jira.issue.comments.Comment;
+import com.atlassian.jira.issue.comments.CommentManager;
+import com.atlassian.jira.issue.fields.CustomField;
+import com.atlassian.jira.issue.issuetype.IssueType;
+import com.atlassian.jira.issue.label.Label;
+import com.atlassian.jira.issue.link.IssueLink;
+import com.atlassian.jira.issue.link.IssueLinkManager;
+import com.atlassian.jira.issue.link.IssueLinkType;
+import com.atlassian.jira.issue.link.RemoteIssueLink;
+import com.atlassian.jira.issue.link.RemoteIssueLinkManager;
+import com.atlassian.jira.issue.priority.Priority;
+import com.atlassian.jira.issue.resolution.Resolution;
+import com.atlassian.jira.issue.status.Status;
+import com.atlassian.jira.issue.vote.VoteManager;
+import com.atlassian.jira.issue.watchers.WatcherManager;
+import com.atlassian.jira.issue.worklog.Worklog;
+import com.atlassian.jira.issue.worklog.WorklogManager;
+import com.atlassian.jira.project.Project;
+import com.atlassian.jira.project.version.Version;
+import com.atlassian.jira.user.ApplicationUser;
+import com.ericsson.eif.leansync.mapping.data.ActionType;
+import com.ericsson.eif.leansync.mapping.data.SyncConfiguration;
+import com.ericsson.jira.oslc.Constants;
+import com.ericsson.jira.oslc.constants.JiraConstants;
+import com.ericsson.jira.oslc.managers.FieldManager;
+import com.ericsson.jira.oslc.managers.JiraManager;
+import com.ericsson.jira.oslc.sync.InboundSyncUtils;
+import com.ericsson.jira.oslc.utils.AppLink;
+import com.ericsson.jira.oslc.utils.AppLinksRepository;
+import com.ericsson.jira.oslc.utils.JiraIssueInputParameters;
+import com.ericsson.jira.oslc.utils.OSLCUtils;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * It represents OSLC JIRA Change Request. It serves as data class which is sending between OSLC system
+ * It extended attributes beyond OSLC base ChangeRequest
+ */
+@OslcNamespace(Constants.CHANGE_MANAGEMENT_NAMESPACE)
+@OslcName(Constants.CHANGE_REQUEST)
+@OslcResourceShape(title = "Change Request Resource Shape", describes = Constants.TYPE_CHANGE_REQUEST)
+public final class JiraChangeRequest extends ChangeRequest {
+ private String project = null;
+ private List components = new ArrayList();
+ private List affects_versions = new ArrayList();
+ private List fix_versions = new ArrayList();
+ private JiraIssuePriority priority = null;
+ private JiraIssueResolution resolution = null;
+ private String environment = null;
+ private String reporter = null;
+ private String assignee = null;
+ private String creator = null;
+ private Long projectId = null;
+ private Date resolutionDate = null;
+ private JiraIssueType issueType = null;
+ private String dueDate = null;
+ private Long originalEstimate;
+ private Long remainingEstimate;
+ private Long loggedHours;
+ private Set labels = new HashSet();
+ private List subTasks = new ArrayList();
+ private List comments = new ArrayList();
+ private List worklogs = new ArrayList();
+ private List voters = new ArrayList();
+ private List watchers = new ArrayList();
+ private JiraIssueHistory history = null;
+ private List insideLinks = new ArrayList