You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ni...@apache.org on 2014/09/21 14:02:04 UTC

[3/5] git commit: CAMEL-7792 JIRA component

CAMEL-7792 JIRA component


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/f261f610
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/f261f610
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/f261f610

Branch: refs/heads/master
Commit: f261f610ddf4f7b2abc472d455d01caa8413cb00
Parents: 882857f
Author: Brett Meyer <br...@3riverdev.com>
Authored: Wed Sep 17 14:24:47 2014 -0400
Committer: Willem Jiang <wi...@gmail.com>
Committed: Sun Sep 21 20:01:36 2014 +0800

----------------------------------------------------------------------
 components/camel-jira/pom.xml                   |  74 +++++++++++
 .../camel/component/jira/JIRAComponent.java     |  34 +++++
 .../camel/component/jira/JIRAEndpoint.java      | 126 +++++++++++++++++++
 .../camel/component/jira/JIRAProducer.java      |  35 ++++++
 .../jira/consumer/AbstractJIRAConsumer.java     |  74 +++++++++++
 .../component/jira/consumer/ConsumerType.java   |  31 +++++
 .../jira/consumer/NewCommentConsumer.java       |  63 ++++++++++
 .../jira/consumer/NewIssueConsumer.java         |  66 ++++++++++
 .../services/org/apache/camel/component/jira    |  18 +++
 .../src/main/resources/log4j.properties         |  16 +++
 components/pom.xml                              |   1 +
 11 files changed, 538 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/f261f610/components/camel-jira/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-jira/pom.xml b/components/camel-jira/pom.xml
new file mode 100644
index 0000000..b08f509
--- /dev/null
+++ b/components/camel-jira/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
+  license agreements. See the NOTICE file distributed with this work for additional 
+  information regarding copyright ownership. The ASF licenses this file to 
+  You under the Apache License, Version 2.0 (the "License"); you may not use 
+  this file except in compliance with the License. You may obtain a copy of 
+  the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required 
+  by applicable law or agreed to in writing, software distributed under the 
+  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 
+  OF ANY KIND, either express or implied. See the License for the specific 
+  language governing permissions and limitations under the License. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>components</artifactId>
+    <version>2.15-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-jira</artifactId>
+  <packaging>bundle</packaging>
+  <name>Camel :: JIRA</name>
+
+   <properties>
+      <camel.osgi.export.pkg>org.apache.camel.component.jira.*</camel.osgi.export.pkg>
+      <camel.osgi.export.service>org.apache.camel.spi.ComponentResolver;component=jira</camel.osgi.export.service>
+   </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.atlassian.jira</groupId>
+      <artifactId>jira-rest-java-client</artifactId>
+      <version>1.2-m01</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- testing -->
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <repositories>
+    <repository>
+      <id>atlassian-public</id>
+      <url>https://maven.atlassian.com/repository/public</url>
+    </repository>
+  </repositories>
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/f261f610/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAComponent.java
----------------------------------------------------------------------
diff --git a/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAComponent.java b/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAComponent.java
new file mode 100644
index 0000000..022f8b5
--- /dev/null
+++ b/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAComponent.java
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.jira;
+
+import java.util.Map;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.impl.DefaultComponent;
+
+/**
+ * Represents the component that manages {@link JIRAEndpoint}.
+ */
+public class JIRAComponent extends DefaultComponent {
+
+    protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+        Endpoint endpoint = new JIRAEndpoint(uri, this);
+        setProperties(endpoint, parameters);
+        return endpoint;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f261f610/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAEndpoint.java
----------------------------------------------------------------------
diff --git a/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAEndpoint.java b/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAEndpoint.java
new file mode 100644
index 0000000..1aa2e2e
--- /dev/null
+++ b/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAEndpoint.java
@@ -0,0 +1,126 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.jira;
+
+import java.util.regex.Pattern;
+
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.component.jira.consumer.ConsumerType;
+import org.apache.camel.component.jira.consumer.NewCommentConsumer;
+import org.apache.camel.component.jira.consumer.NewIssueConsumer;
+import org.apache.camel.impl.DefaultEndpoint;
+
+/**
+ * The endpoint encapsulates portions of the JIRA API, relying on the jira-rest-java-client SDK.
+ * Available endpoint URIs include:
+ * 
+ * CONSUMERS
+ * jira://newIssue (new tickets)
+ * jira://newComment (new comments on tickets)
+ * 
+ * The endpoints will respond with jira-rest-java-client POJOs (Issue, Comment, etc.)
+ * 
+ * Note: Rather than webhooks, this endpoint relies on simple polling.  Reasons include:
+ * - concerned about reliability/stability if this somehow relied on an exposed, embedded server (Jetty?)
+ * - the types of payloads we're polling aren't typically large (plus, paging is available in the API)
+ * - need to support apps running somewhere not publicly accessible where a webhook would fail
+ */
+public class JIRAEndpoint extends DefaultEndpoint {
+	
+	private String serverUrl = null;
+	
+	private String username = null;
+	
+	private String password = null;
+	
+	private String jql = null;
+
+	public JIRAEndpoint(String uri, JIRAComponent component) {
+        super(uri, component);
+    }
+
+    public Producer createProducer() throws Exception {
+        return new JIRAProducer(this);
+    }
+    
+    public Consumer createConsumer(Processor processor) throws Exception {
+        String uri = getEndpointUri();
+        String[] uriSplit = splitUri(getEndpointUri());
+        
+        if (uriSplit.length > 0) {
+            switch (ConsumerType.fromUri(uriSplit[0])) {
+            case NEWCOMMENT:
+                return new NewCommentConsumer(this, processor);
+            case NEWISSUE:
+                return new NewIssueConsumer(this, processor);
+            default:
+                break;
+            }
+        }
+
+        throw new IllegalArgumentException("Cannot create any consumer with uri " + uri
+                + ". A consumer type was not provided (or an incorrect pairing was used).");
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+
+    private static String[] splitUri(String uri) {
+        Pattern p1 = Pattern.compile("jira:(//)*");
+        Pattern p2 = Pattern.compile("\\?.*");
+
+        uri = p1.matcher(uri).replaceAll("");
+        uri = p2.matcher(uri).replaceAll("");
+
+        return uri.split("/");
+    }
+
+    public String getServerUrl() {
+		return serverUrl;
+	}
+
+	public void setServerUrl(String serverUrl) {
+		this.serverUrl = serverUrl;
+	}
+
+	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 String getJql() {
+		return jql;
+	}
+
+	public void setJql(String jql) {
+		this.jql = jql;
+	}
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f261f610/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAProducer.java
----------------------------------------------------------------------
diff --git a/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAProducer.java b/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAProducer.java
new file mode 100644
index 0000000..9f54bc2
--- /dev/null
+++ b/components/camel-jira/src/main/java/org/apache/camel/component/jira/JIRAProducer.java
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.jira;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.impl.DefaultProducer;
+
+/**
+ * The Camel :: JIRA producer.
+ */
+public class JIRAProducer extends DefaultProducer {
+	
+    public JIRAProducer(JIRAEndpoint endpoint) {
+        super(endpoint);
+    }
+
+    public void process(Exchange exchange) throws Exception {
+        // nothing to do, yet
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f261f610/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/AbstractJIRAConsumer.java
----------------------------------------------------------------------
diff --git a/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/AbstractJIRAConsumer.java b/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/AbstractJIRAConsumer.java
new file mode 100644
index 0000000..90bf6d4
--- /dev/null
+++ b/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/AbstractJIRAConsumer.java
@@ -0,0 +1,74 @@
+package org.apache.camel.component.jira.consumer;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.Processor;
+import org.apache.camel.component.jira.JIRAEndpoint;
+import org.apache.camel.impl.ScheduledPollConsumer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.atlassian.jira.rest.client.JiraRestClient;
+import com.atlassian.jira.rest.client.domain.BasicIssue;
+import com.atlassian.jira.rest.client.domain.SearchResult;
+import com.atlassian.jira.rest.client.internal.jersey.JerseyJiraRestClientFactory;
+
+public abstract class AbstractJIRAConsumer extends ScheduledPollConsumer {
+	
+	private static final transient Logger LOG = LoggerFactory.getLogger(AbstractJIRAConsumer.class);
+	
+	private final JIRAEndpoint endpoint;
+    
+    private final JiraRestClient client;
+    
+    public AbstractJIRAConsumer(JIRAEndpoint endpoint, Processor processor) {
+        super(endpoint, processor);
+        this.endpoint = endpoint;
+        
+        // Use a more reasonable default.
+        setDelay(6000);
+        
+        final JerseyJiraRestClientFactory factory = new JerseyJiraRestClientFactory();
+		final URI jiraServerUri = URI.create(endpoint.getServerUrl());
+		client = factory.createWithBasicHttpAuthentication(
+				jiraServerUri, endpoint.getUsername(), endpoint.getPassword());
+    }
+    
+    protected List<BasicIssue> getIssues() {
+		return getIssues(endpoint.getJql(), 0, 0, 500);
+    }
+    
+    // Ignore maxResults if it's <= 0.
+    protected List<BasicIssue> getIssues(String jql, int start, int maxResults, int maxPerQuery) {
+		LOG.info("Indexing current JIRA issues...");
+		
+		List<BasicIssue> issues = new ArrayList<BasicIssue>();
+		while ( true ) {
+			SearchResult searchResult = client.getSearchClient().searchJqlWithFullIssues(
+					jql, maxPerQuery, start, null );
+
+			for (BasicIssue issue : searchResult.getIssues()) {
+				issues.add(issue);
+			}
+
+			// Note: #getTotal == the total # the query would return *without* pagination, effectively telling us
+			// we've reached the end.  Also exit early if we're limiting the # of results.
+			if ( start >= searchResult.getTotal() || 
+					(maxResults > 0 && issues.size() >= maxResults)) {
+				break;
+			}
+
+			start += maxPerQuery;
+		}
+		
+		return issues;
+    }
+    
+    protected JiraRestClient client() {
+        return client;
+    }
+
+    protected abstract int poll() throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f261f610/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/ConsumerType.java
----------------------------------------------------------------------
diff --git a/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/ConsumerType.java b/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/ConsumerType.java
new file mode 100644
index 0000000..db71d05
--- /dev/null
+++ b/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/ConsumerType.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.jira.consumer;
+
+public enum ConsumerType {
+
+    NEWCOMMENT, NEWISSUE, UNKNOWN;
+
+    public static ConsumerType fromUri(String uri) {
+        for (ConsumerType consumerType : ConsumerType.values()) {
+            if (consumerType.name().equalsIgnoreCase(uri)) {
+                return consumerType;
+            }
+        }
+        return ConsumerType.UNKNOWN;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f261f610/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/NewCommentConsumer.java
----------------------------------------------------------------------
diff --git a/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/NewCommentConsumer.java b/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/NewCommentConsumer.java
new file mode 100644
index 0000000..80e8b21
--- /dev/null
+++ b/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/NewCommentConsumer.java
@@ -0,0 +1,63 @@
+package org.apache.camel.component.jira.consumer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.component.jira.JIRAEndpoint;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.atlassian.jira.rest.client.domain.BasicIssue;
+import com.atlassian.jira.rest.client.domain.Comment;
+import com.atlassian.jira.rest.client.domain.Issue;
+
+/**
+ * Consumes new comments on JIRA issues.
+ * 
+ * NOTE: In your JQL, try to optimize the query as much as possible!  For example, the JIRA Toolkit Plugin includes a
+// "Number of comments" custom field -- use '"Number of comments" > 0' in your query.  Also try to minimize based on
+// state (status=Open), increase the polling delay, etc.  We have to do a separate query for *every single* resulting
+ * ticket in order to load its comments!  For large organizations, the JIRA API can be significantly slow.
+ */
+public class NewCommentConsumer extends AbstractJIRAConsumer {
+	private static final transient Logger LOG = LoggerFactory.getLogger(NewCommentConsumer.class);
+	
+    private List<Long> commentIds = new ArrayList<Long>();
+
+	public NewCommentConsumer(JIRAEndpoint endpoint, Processor processor) {
+		super(endpoint, processor);
+		LOG.info("JIRA NewCommentConsumer: Indexing current issue comments...");
+        getComments();
+	}
+
+	@Override
+	protected int poll() throws Exception {
+	    Stack<Comment> newComments = getComments();
+	    while(!newComments.empty()) {
+			Comment newComment = newComments.pop();
+        	Exchange e = getEndpoint().createExchange();
+            e.getIn().setBody(newComment);
+            getProcessor().process(e);
+        }
+		return newComments.size();
+	}
+	
+	// In the end, we want *new* comments oldest to newest.
+	private Stack<Comment> getComments() {
+	    Stack<Comment> newComments = new Stack<Comment>();
+        List<BasicIssue> issues = getIssues();
+        for (BasicIssue issue : issues) {
+            Issue fullIssue = client().getIssueClient().getIssue(issue.getKey(), null);
+            for (Comment comment : fullIssue.getComments()) {
+                if (!commentIds.contains(comment.getId())) {
+                    newComments.push(comment);
+                    commentIds.add(comment.getId());
+                }
+            }
+        }
+        return newComments;
+	}
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f261f610/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/NewIssueConsumer.java
----------------------------------------------------------------------
diff --git a/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/NewIssueConsumer.java b/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/NewIssueConsumer.java
new file mode 100644
index 0000000..a858be7
--- /dev/null
+++ b/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/NewIssueConsumer.java
@@ -0,0 +1,66 @@
+package org.apache.camel.component.jira.consumer;
+
+import java.util.List;
+import java.util.Stack;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.component.jira.JIRAEndpoint;
+
+import com.atlassian.jira.rest.client.domain.BasicIssue;
+
+/**
+ * Consumes new JIRA issues.
+ * 
+ * NOTE: We manually add "ORDER BY key desc" to the JQL in order to optimize startup (the latest issues one at a time),
+ * rather than having to index everything.
+ */
+public class NewIssueConsumer extends AbstractJIRAConsumer {
+	
+	private final String jql;
+	
+	private long latestIssueId = -1;
+
+	public NewIssueConsumer(JIRAEndpoint endpoint, Processor processor) {
+		super(endpoint, processor);
+		
+		jql = endpoint.getJql() + " ORDER BY key desc";
+		
+		// grab only the top
+		List<BasicIssue> issues = getIssues(jql, 0, 1, 1);
+		// in case there aren't any issues...
+		if (issues.size() >= 1) {
+			latestIssueId = issues.get(0).getId();
+		}
+	}
+
+	@Override
+	protected int poll() throws Exception {
+		Stack<BasicIssue> newIssues = new Stack<BasicIssue>();
+		getNewIssues(0, newIssues);
+		while(!newIssues.empty()) {
+			BasicIssue newIssue = newIssues.pop();
+        	Exchange e = getEndpoint().createExchange();
+            e.getIn().setBody(newIssue);
+            getProcessor().process(e);
+        }
+		return newIssues.size();
+	}
+	
+	// In the end, we want *new* issues oldest to newest.
+	private void getNewIssues(int start, Stack<BasicIssue> stack) {
+		// grab only the top
+		List<BasicIssue> issues = getIssues(jql, start, 1, 1);
+		// in case there aren't any issues...
+		if (issues.size() >= 1) {
+			long id = issues.get(0).getId();
+			if (id > latestIssueId) {
+				stack.push(issues.get(0));
+				// try again in case multiple new issues exist
+				getNewIssues(start + 1, stack);
+				// make sure this happens now, rather than before calling #getNewIssues
+				latestIssueId = id;
+			}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/f261f610/components/camel-jira/src/main/resources/META-INF/services/org/apache/camel/component/jira
----------------------------------------------------------------------
diff --git a/components/camel-jira/src/main/resources/META-INF/services/org/apache/camel/component/jira b/components/camel-jira/src/main/resources/META-INF/services/org/apache/camel/component/jira
new file mode 100644
index 0000000..3a73e9f
--- /dev/null
+++ b/components/camel-jira/src/main/resources/META-INF/services/org/apache/camel/component/jira
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class=org.apache.camel.component.jira.JIRAComponent

http://git-wip-us.apache.org/repos/asf/camel/blob/f261f610/components/camel-jira/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/components/camel-jira/src/main/resources/log4j.properties b/components/camel-jira/src/main/resources/log4j.properties
new file mode 100644
index 0000000..4621723
--- /dev/null
+++ b/components/camel-jira/src/main/resources/log4j.properties
@@ -0,0 +1,16 @@
+
+#
+# The logging properties used
+#
+log4j.rootLogger=INFO, out
+
+# uncomment the following line to turn on Camel debugging
+#log4j.logger.org.apache.camel=DEBUG
+
+# CONSOLE appender not used by default
+log4j.appender.out=org.apache.log4j.ConsoleAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=[%30.30t] %-30.30c{1} %-5p %m%n
+#log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+log4j.throwableRenderer=org.apache.log4j.EnhancedThrowableRenderer
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/f261f610/components/pom.xml
----------------------------------------------------------------------
diff --git a/components/pom.xml b/components/pom.xml
index e185ace..99c82eb 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -120,6 +120,7 @@
     <!-- camel-jibx doesn't work under JDK8, but may be build without tests -->
     <module>camel-jibx</module>
     <module>camel-jing</module>
+    <module>camel-jira</module>
     <module>camel-jmx</module>
     <module>camel-josql</module>
     <module>camel-jpa</module>