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 <[email protected]> Authored: Wed Sep 17 14:24:47 2014 -0400 Committer: Willem Jiang <[email protected]> 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>
