├── CHANGELOG
├── .travis.yml
├── docs
├── img
│ └── quick-search.png
└── userguide.md
├── elasticsearch-cockpit-plugin
├── src
│ ├── main
│ │ ├── resources
│ │ │ ├── META-INF
│ │ │ │ └── services
│ │ │ │ │ └── org.camunda.bpm.cockpit.plugin.spi.CockpitPlugin
│ │ │ └── plugin-webapp
│ │ │ │ └── dashboards
│ │ │ │ └── app
│ │ │ │ ├── pages
│ │ │ │ ├── activity-monitor.html
│ │ │ │ ├── main.js
│ │ │ │ ├── process-definition.html
│ │ │ │ ├── search.js
│ │ │ │ ├── processDefinition.js
│ │ │ │ ├── search.html
│ │ │ │ └── activityMonitor.js
│ │ │ │ ├── plugin.js
│ │ │ │ └── lib
│ │ │ │ └── nv.d3.min.css
│ │ └── java
│ │ │ └── org
│ │ │ └── camunda
│ │ │ └── bpm
│ │ │ └── cockpit
│ │ │ └── plugin
│ │ │ ├── dashboards
│ │ │ ├── resources
│ │ │ │ ├── DateHistogramBucketPair.java
│ │ │ │ ├── AggregationsResult.java
│ │ │ │ ├── SearchResult.java
│ │ │ │ ├── DashboardsRootResource.java
│ │ │ │ ├── SearchResource.java
│ │ │ │ └── ProcessInstanceHistogramResource.java
│ │ │ └── DashboardsPlugin.java
│ │ │ └── ElasticSearchClientProvider.java
│ ├── test
│ │ └── resources
│ │ │ ├── META-INF
│ │ │ └── services
│ │ │ │ └── org.camunda.bpm.engine.rest.spi.ProcessEngineProvider
│ │ │ └── camunda.cfg.xml
│ └── develop
│ │ ├── resources
│ │ └── WEB-INF
│ │ │ └── applicationContext.xml
│ │ └── java
│ │ └── org
│ │ └── camunda
│ │ └── bpm
│ │ └── extension
│ │ └── elasticsearch
│ │ └── cockpit
│ │ └── demodata
│ │ └── DemoDataGenerator.java
└── pom.xml
├── start-es.sh
├── elasticsearch-engine-integration
├── src
│ ├── test
│ │ ├── resources
│ │ │ ├── query
│ │ │ │ ├── match_variable_value.json
│ │ │ │ ├── bool_all.json
│ │ │ │ └── querystring_all.json
│ │ │ ├── config
│ │ │ │ ├── elasticsearch-test.yml
│ │ │ │ └── elasticsearch-test-remote.yml
│ │ │ ├── camunda-bpm-elasticsearch-remote.json
│ │ │ ├── camunda-bpm-elasticsearch.json
│ │ │ ├── logging.properties
│ │ │ ├── camunda.cfg.remote.xml
│ │ │ ├── camunda.cfg.xml
│ │ │ └── applicationContext.xml
│ │ └── java
│ │ │ └── org
│ │ │ └── camunda
│ │ │ └── bpm
│ │ │ ├── elasticsearch
│ │ │ ├── jackson
│ │ │ │ └── JacksonMixInFilterTest.java
│ │ │ ├── ProcessDataContainer.java
│ │ │ ├── query
│ │ │ │ └── HistoryEventsQueryTest.java
│ │ │ ├── AbstractElasticSearchActiveMQTest.java
│ │ │ ├── entity
│ │ │ │ └── EntityFilterTest.java
│ │ │ ├── cfg
│ │ │ │ └── ElasticSearchHistoryPluginConfigurationTest.java
│ │ │ ├── index
│ │ │ │ ├── HistoryEventsIndexingTest.java
│ │ │ │ └── HistoryEventsMappingTest.java
│ │ │ ├── TestDataGenerator.java
│ │ │ └── AbstractElasticSearchTest.java
│ │ │ └── example
│ │ │ └── invoice
│ │ │ └── service
│ │ │ └── ArchiveInvoiceService.java
│ └── main
│ │ ├── resources
│ │ ├── camunda-bpm-elasticsearch.json
│ │ └── mapping
│ │ │ └── processinstance.json
│ │ └── java
│ │ └── org
│ │ └── camunda
│ │ └── bpm
│ │ └── elasticsearch
│ │ ├── jackson
│ │ ├── HistoricDetailEventMixIn.java
│ │ ├── HistoricTaskInstanceEventMixIn.java
│ │ ├── HistoricProcessInstanceEventMixIn.java
│ │ ├── HistoricScopeInstanceEventMixIn.java
│ │ ├── HistoricActivityInstanceEventMixIn.java
│ │ ├── HistoricFormPropertyEventMixIn.java
│ │ ├── HistoricVariableUpdateEventMixIn.java
│ │ ├── HistoryEventMixIn.java
│ │ ├── JsonTransformer.java
│ │ └── JacksonMixInFilterModule.java
│ │ ├── handler
│ │ ├── ElasticSearchHistoryEventHandler.java
│ │ ├── ElasticSearchTransactionAwareCompositeEventHandler.java
│ │ └── ElasticSearchTransactionAwareHistoryEventHandler.java
│ │ ├── session
│ │ ├── ElasticSearchSessionFactory.java
│ │ └── ElasticSearchSession.java
│ │ ├── index
│ │ ├── ElasticSearchIndexStrategy.java
│ │ ├── ElasticSearchBulkIndexStrategy.java
│ │ └── ElasticSearchDefaultIndexStrategy.java
│ │ ├── ElasticSearchHistoryEventDispatcher.java
│ │ ├── util
│ │ ├── ClassUtil.java
│ │ └── ElasticSearchHelper.java
│ │ └── entity
│ │ └── ElasticSearchProcessInstanceHistoryEntity.java
└── pom.xml
├── .gitignore
├── README.md
├── elasticsearch-commons
├── pom.xml
└── src
│ └── main
│ └── java
│ └── org
│ └── camunda
│ └── bpm
│ └── elasticsearch
│ ├── util
│ ├── JsonHelper.java
│ └── IoUtil.java
│ ├── ElasticSearchClient.java
│ └── ElasticSearchHistoryPluginConfiguration.java
├── elasticsearch-jboss-module
├── src
│ └── main
│ │ └── modules
│ │ └── org
│ │ └── camunda
│ │ └── bpm
│ │ ├── jboss
│ │ └── camunda-jboss-subsystem
│ │ │ └── main
│ │ │ └── module.xml
│ │ └── elasticsearch
│ │ └── main
│ │ └── module.xml
└── pom.xml
├── elasticsearch-lib-module
└── pom.xml
└── pom.xml
/CHANGELOG:
--------------------------------------------------------------------------------
1 | # 1.0.0
2 |
3 | - initial release
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - oraclejdk8
4 | - oraclejdk7
5 | services:
6 | - elasticsearch
--------------------------------------------------------------------------------
/docs/img/quick-search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/camunda-community-hub/camunda-bpm-elasticsearch/HEAD/docs/img/quick-search.png
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/resources/META-INF/services/org.camunda.bpm.cockpit.plugin.spi.CockpitPlugin:
--------------------------------------------------------------------------------
1 | org.camunda.bpm.cockpit.plugin.dashboards.DashboardsPlugin
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/test/resources/META-INF/services/org.camunda.bpm.engine.rest.spi.ProcessEngineProvider:
--------------------------------------------------------------------------------
1 | org.camunda.bpm.cockpit.engine.TestProcessEngineProvider
--------------------------------------------------------------------------------
/start-es.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | distro/elasticsearch-1.4.4/bin/elasticsearch --node.name=camunda_bpm_remote_es --cluster.name=camunda_bpm_es_cluster -Des.script.disable_dynamic=false
3 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/resources/plugin-webapp/dashboards/app/pages/activity-monitor.html:
--------------------------------------------------------------------------------
1 |
Activity Monitor
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/resources/plugin-webapp/dashboards/app/plugin.js:
--------------------------------------------------------------------------------
1 | ngDefine('cockpit.plugin.dashboards', [
2 | 'module:dashboards.pages:./pages/main',
3 | ], function(module) {
4 |
5 | });
6 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/resources/query/match_variable_value.json:
--------------------------------------------------------------------------------
1 | {
2 | "query": {
3 | "nested": {
4 | "path": "variables",
5 | "query": {
6 | "match": {
7 | "variables.textValue": "somebody"
8 | }
9 | }
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/resources/config/elasticsearch-test.yml:
--------------------------------------------------------------------------------
1 | node.local : true
2 | node.client : false
3 | node.data : true
4 | node.http.enabled : true
5 | discovery.zen.ping.multicast.enabled : false
6 | network.host : _local_
7 | script.disable_dynamic : false
8 | cluster.name : camunda_bpm_es_cluster
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/resources/plugin-webapp/dashboards/app/pages/main.js:
--------------------------------------------------------------------------------
1 | ngDefine('dashboards.pages', [
2 | 'module:dashboards.pages.search:./search',
3 | 'module:dashboards.pages.activityMonitor:./activityMonitor',
4 | 'module:dashboards.pages.processDefinition:./processDefinition'
5 | ], function(module) {
6 |
7 | });
8 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/resources/config/elasticsearch-test-remote.yml:
--------------------------------------------------------------------------------
1 | node.local : false
2 | node.client : true
3 | node.data : false
4 | node.http.enabled : true
5 | discovery.zen.ping.multicast.enabled : true
6 | #discovery.zen.ping.unicast.hosts: 127.0.0.1:9300
7 | script.disable_dynamic: false
8 | cluster.name : camunda_bpm_es_cluster
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/java/org/camunda/bpm/elasticsearch/jackson/JacksonMixInFilterTest.java:
--------------------------------------------------------------------------------
1 | package org.camunda.bpm.elasticsearch.jackson;
2 |
3 | /**
4 | * Tests the JacksonMixInFilter extension
5 | */
6 | public class JacksonMixInFilterTest {
7 |
8 | // ObjectMapper
9 | // add Module
10 | // add FilterProvider
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | .DS_Store
3 |
4 | # Package Files #
5 | *.jar
6 | *.war
7 | *.ear
8 |
9 | # IntelliJ files #
10 | .idea
11 | *.iml
12 |
13 | # eclipse files #
14 | .classpath
15 | .project
16 | .settings
17 |
18 | # vim #
19 | .swp
20 |
21 | # Generated folders #
22 | distro
23 | target
24 |
25 | # Generated files
26 | test*.json
27 |
28 | #elastic search data #
29 | **/data/elasticsearch
30 | *Test.json
31 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/resources/camunda-bpm-elasticsearch.json:
--------------------------------------------------------------------------------
1 | {
2 | "index": "camundabpm",
3 | "type": "processinstance",
4 | "indexingStrategy": "org.camunda.bpm.elasticsearch.index.ElasticSearchDefaultIndexStrategy",
5 | "esHost": "localhost",
6 | "esPort": "9300",
7 | "esClusterName": "camunda_bpm_es_cluster",
8 | "transportClient": false,
9 | "properties": {
10 | "es.script.disable_dynamic": false
11 | }
12 | }
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/resources/plugin-webapp/dashboards/app/pages/process-definition.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/resources/camunda-bpm-elasticsearch-remote.json:
--------------------------------------------------------------------------------
1 | {
2 | "index": "camundabpm",
3 | "type": "processinstance",
4 | "indexingStrategy": "org.camunda.bpm.elasticsearch.index.ElasticSearchDefaultIndexStrategy",
5 | "esHost": "localhost",
6 | "esPort": "9300",
7 | "esClusterName": "camunda_bpm_es_cluster",
8 | "transportClient": false,
9 | "properties": {
10 | "es.node.local" : false,
11 | "es.path.logs" : "${es.path.logs}",
12 | "es.path.data" : "${es.path.data}",
13 | "es.path.work" : "${es.path.work}",
14 | "es.index.number_of_shards" : "1",
15 | "es.index.number_of_replicas" : "0"
16 | }
17 | }
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/resources/camunda-bpm-elasticsearch.json:
--------------------------------------------------------------------------------
1 | {
2 | "index": "camundabpm",
3 | "type": "processinstance",
4 | "indexingStrategy": "org.camunda.bpm.elasticsearch.index.ElasticSearchDefaultIndexStrategy",
5 | "esHost": "localhost",
6 | "esPort": "9300",
7 | "esClusterName": "camunda_bpm_es_cluster",
8 | "transportClient": false,
9 | "properties": {
10 | "es.node.local" : true,
11 | "es.network.host" : "_local_",
12 | "es.path.logs" : "${es.path.logs}",
13 | "es.path.data" : "${es.path.data}",
14 | "es.path.work" : "${es.path.work}",
15 | "es.index.number_of_shards" : "1",
16 | "es.index.number_of_replicas" : "0",
17 | "es.index.store.type" : "memory",
18 | "es.index.store.fs.memory.enabled" : "true"
19 | }
20 | }
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/java/org/camunda/bpm/cockpit/plugin/dashboards/resources/DateHistogramBucketPair.java:
--------------------------------------------------------------------------------
1 | package org.camunda.bpm.cockpit.plugin.dashboards.resources;
2 |
3 | /**
4 | * Created by hawky4s on 12.08.14.
5 | */
6 | public class DateHistogramBucketPair {
7 |
8 | protected Number key;
9 | protected long docCount;
10 |
11 | public DateHistogramBucketPair(Number key, long docCount) {
12 | this.key = key;
13 | this.docCount = docCount;
14 | }
15 |
16 | public Number getKey() {
17 | return key;
18 | }
19 |
20 | public void setKey(Number key) {
21 | this.key = key;
22 | }
23 |
24 | public long getDocCount() {
25 | return docCount;
26 | }
27 |
28 | public void setDocCount(long docCount) {
29 | this.docCount = docCount;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/jackson/HistoricDetailEventMixIn.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.jackson;
18 |
19 | public interface HistoricDetailEventMixIn {
20 | }
21 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/jackson/HistoricTaskInstanceEventMixIn.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.jackson;
18 |
19 | public interface HistoricTaskInstanceEventMixIn {
20 | }
21 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/jackson/HistoricProcessInstanceEventMixIn.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.jackson;
18 |
19 | public interface HistoricProcessInstanceEventMixIn {
20 | }
21 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/jackson/HistoricScopeInstanceEventMixIn.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.jackson;
18 |
19 | public interface HistoricScopeInstanceEventMixIn {
20 | }
21 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/jackson/HistoricActivityInstanceEventMixIn.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.jackson;
18 |
19 | public interface HistoricActivityInstanceEventMixIn {
20 | }
21 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/resources/logging.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | handlers = java.util.logging.ConsoleHandler
18 | .level = FINE
19 | java.util.logging.ConsoleHandler.level=FINE
20 |
21 | org.apache.ibatis.level=FINE
22 |
23 | javax.activation.level=INFO
24 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/jackson/HistoricFormPropertyEventMixIn.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.jackson;
18 |
19 | public interface HistoricFormPropertyEventMixIn extends HistoricDetailEventMixIn {
20 | }
21 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/jackson/HistoricVariableUpdateEventMixIn.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.jackson;
18 |
19 | public interface HistoricVariableUpdateEventMixIn extends HistoricDetailEventMixIn {
20 | }
21 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/java/org/camunda/bpm/cockpit/plugin/dashboards/resources/AggregationsResult.java:
--------------------------------------------------------------------------------
1 | package org.camunda.bpm.cockpit.plugin.dashboards.resources;
2 |
3 | import org.elasticsearch.search.aggregations.Aggregation;
4 |
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | /**
9 | * Created by hawky4s on 12.08.14.
10 | */
11 | public class AggregationsResult {
12 |
13 | private Map> dateHistogramBuckets;
14 | private long totalHits;
15 |
16 | public void setDateHistogramBuckets(Map> dateHistogramBuckets) {
17 | this.dateHistogramBuckets = dateHistogramBuckets;
18 | }
19 |
20 | public Map> getDateHistogramBuckets() {
21 | return dateHistogramBuckets;
22 | }
23 |
24 | public void setTotalHits(long totalHits) {
25 | this.totalHits = totalHits;
26 | }
27 |
28 | public long getTotalHits() {
29 | return totalHits;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/test/resources/camunda.cfg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/jackson/HistoryEventMixIn.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.jackson;
18 |
19 |
20 | import com.fasterxml.jackson.annotation.JsonFilter;
21 | import com.fasterxml.jackson.annotation.JsonIgnore;
22 |
23 | @JsonFilter("HistoryEventMixIn")
24 | public interface HistoryEventMixIn {
25 |
26 | @JsonIgnore Object getPersistentState();
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/java/org/camunda/bpm/elasticsearch/ProcessDataContainer.java:
--------------------------------------------------------------------------------
1 | package org.camunda.bpm.elasticsearch;
2 |
3 | import org.camunda.bpm.engine.runtime.VariableInstance;
4 |
5 | import java.util.List;
6 |
7 | public class ProcessDataContainer {
8 |
9 | private String pid;
10 | private String name;
11 | private List variables;
12 |
13 | public ProcessDataContainer() {}
14 |
15 | public ProcessDataContainer(String pid) {
16 | this.pid = pid;
17 | }
18 |
19 | public ProcessDataContainer(String pid, String name) {
20 | this(pid);
21 | this.name = name;
22 | this.variables = null;
23 | }
24 |
25 | public ProcessDataContainer(String pid, String name, List variables) {
26 | this(pid, name);
27 | this.variables = variables;
28 | }
29 |
30 | public String getPid() {
31 | return pid;
32 | }
33 |
34 | public void setPid(String pid) {
35 | this.pid = pid;
36 | }
37 |
38 | public List getVariables() {
39 | return variables;
40 | }
41 |
42 | public void setVariables(List variables) {
43 | this.variables = variables;
44 | }
45 |
46 | public String getName() {
47 | return name;
48 | }
49 |
50 | public void setName(String name) {
51 | this.name = name;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/handler/ElasticSearchHistoryEventHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.handler;
18 |
19 | import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
20 | import org.camunda.bpm.engine.impl.history.handler.HistoryEventHandler;
21 |
22 | import java.util.List;
23 |
24 | public abstract class ElasticSearchHistoryEventHandler implements HistoryEventHandler {
25 |
26 | public abstract void handleEvent(HistoryEvent historyEvent);
27 |
28 | public void handleEvents(List historyEvents) {
29 | for (HistoryEvent historyEvent : historyEvents) {
30 | handleEvent(historyEvent);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/java/org/camunda/bpm/cockpit/plugin/dashboards/DashboardsPlugin.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 nico.rehwaldt.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.cockpit.plugin.dashboards;
18 |
19 | import java.util.HashSet;
20 | import java.util.Set;
21 |
22 | import org.camunda.bpm.cockpit.plugin.dashboards.resources.DashboardsRootResource;
23 | import org.camunda.bpm.cockpit.plugin.spi.impl.AbstractCockpitPlugin;
24 |
25 | /**
26 | *
27 | * @author nico.rehwaldt
28 | */
29 | public class DashboardsPlugin extends AbstractCockpitPlugin {
30 |
31 | public static final String ID = "dashboards";
32 |
33 | @Override
34 | public String getId() {
35 | return ID;
36 | }
37 |
38 | @Override
39 | public Set> getResourceClasses() {
40 | HashSet> classes = new HashSet>();
41 |
42 | classes.add(DashboardsRootResource.class);
43 |
44 | return classes;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/session/ElasticSearchSessionFactory.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.camunda.bpm.elasticsearch.session;
14 |
15 | import org.camunda.bpm.elasticsearch.index.ElasticSearchIndexStrategy;
16 | import org.camunda.bpm.engine.impl.interceptor.Session;
17 | import org.camunda.bpm.engine.impl.interceptor.SessionFactory;
18 |
19 | /**
20 | * @author Daniel Meyer
21 | *
22 | */
23 | public class ElasticSearchSessionFactory implements SessionFactory {
24 |
25 | protected ElasticSearchIndexStrategy indexingStrategy;
26 |
27 | public ElasticSearchSessionFactory(ElasticSearchIndexStrategy indexingStrategy) {
28 | this.indexingStrategy = indexingStrategy;
29 | }
30 |
31 | public Class> getSessionType() {
32 | return ElasticSearchSession.class;
33 | }
34 |
35 | public Session openSession() {
36 | return new ElasticSearchSession(indexingStrategy);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/resources/plugin-webapp/dashboards/app/pages/search.js:
--------------------------------------------------------------------------------
1 | ngDefine('dashboards.pages.search', [
2 | 'angular',
3 | 'require',
4 | ], function(module, angular, require) {
5 |
6 | var Controller = ['$scope', '$http', 'Uri', '$compile',
7 | function($scope, $http, Uri, $compile) {
8 |
9 | $scope.searchString = "";
10 | $scope.includeVariables = true;
11 | $scope.includeTasks = true;
12 | $scope.includeActivities = false;
13 |
14 | $scope.searchResults = [];
15 |
16 | $scope.doSearch = function() {
17 |
18 | $http({
19 | method: 'GET',
20 | url: Uri.appUri("plugin://dashboards/default/search"),
21 | params: {
22 | query: $scope.searchString,
23 | includeVariables: $scope.includeVariables,
24 | includeTasks: $scope.includeTasks,
25 | includeActivities: $scope.includeActivities
26 | }
27 | }).success(function(data) {
28 | $scope.searchResults = data;
29 | });
30 |
31 | };
32 |
33 | $scope.clear = function() {
34 | $scope.searchResults = [];
35 | $scope.searchString = "";
36 | };
37 |
38 | }];
39 |
40 | var ViewConfig = [ 'ViewsProvider', function(ViewsProvider) {
41 | ViewsProvider.registerDefaultView('cockpit.dashboard', {
42 | id: 'search',
43 | priority: 20,
44 | label: 'Seach',
45 | controller: Controller,
46 | url: require.toUrl('./search.html')
47 | });
48 | }];
49 |
50 | module
51 | .config(ViewConfig);
52 | });
53 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/resources/query/bool_all.json:
--------------------------------------------------------------------------------
1 | {
2 | "query": {
3 | "filtered": {
4 | "query": {
5 | "match_all": {}
6 | },
7 | "filter": {
8 | "bool": {
9 | "should": [
10 | {
11 | "nested": {
12 | "path": "variables",
13 | "query": {
14 | "query_string": {
15 | "fields": ["variables.*"],
16 | "query": "$myValue",
17 | "use_dis_max" : true,
18 | "lenient" : true
19 | }
20 | }
21 | }
22 | },
23 | {
24 | "nested": {
25 | "path": "activities",
26 | "query": {
27 | "query_string": {
28 | "fields": ["activities.*"],
29 | "query": "$myValue",
30 | "use_dis_max" : true,
31 | "lenient" : true
32 | }
33 | }
34 | }
35 | },
36 | {
37 | "nested": {
38 | "path": "tasks",
39 | "query": {
40 | "query_string": {
41 | "fields": ["tasks.*"],
42 | "query": "$myValue",
43 | "use_dis_max" : true,
44 | "lenient" : true
45 | }
46 | }
47 | }
48 | }
49 | ]
50 | }
51 | }
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/handler/ElasticSearchTransactionAwareCompositeEventHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.handler;
18 |
19 | import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
20 | import org.camunda.bpm.engine.impl.history.handler.DbHistoryEventHandler;
21 |
22 | public class ElasticSearchTransactionAwareCompositeEventHandler extends ElasticSearchHistoryEventHandler {
23 |
24 | protected DbHistoryEventHandler dbHistoryEventHandler = new DbHistoryEventHandler();
25 | protected ElasticSearchTransactionAwareHistoryEventHandler elasticSearchTransactionAwareHistoryEventHandler = new ElasticSearchTransactionAwareHistoryEventHandler();
26 |
27 | @Override
28 | public void handleEvent(HistoryEvent historyEvent) {
29 | dbHistoryEventHandler.handleEvent(historyEvent);
30 | elasticSearchTransactionAwareHistoryEventHandler.handleEvent(historyEvent);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/java/org/camunda/bpm/cockpit/plugin/dashboards/resources/SearchResult.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.camunda.bpm.cockpit.plugin.dashboards.resources;
14 |
15 | /**
16 | * @author Daniel Meyer
17 | *
18 | */
19 | public class SearchResult {
20 |
21 | protected String processInstanceId;
22 | protected String processDefinitionId;
23 | protected String processDefinitionName;
24 |
25 | public String getProcessInstanceId() {
26 | return processInstanceId;
27 | }
28 | public void setProcessInstanceId(String processInstanceId) {
29 | this.processInstanceId = processInstanceId;
30 | }
31 | public String getProcessDefinitionId() {
32 | return processDefinitionId;
33 | }
34 | public void setProcessDefinitionId(String processDefinitionId) {
35 | this.processDefinitionId = processDefinitionId;
36 | }
37 | public String getProcessDefinitionName() {
38 | return processDefinitionName;
39 | }
40 | public void setProcessDefinitionName(String processDefinitionName) {
41 | this.processDefinitionName = processDefinitionName;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/session/ElasticSearchSession.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.camunda.bpm.elasticsearch.session;
14 |
15 | import java.util.LinkedList;
16 | import java.util.List;
17 |
18 | import org.camunda.bpm.elasticsearch.index.ElasticSearchIndexStrategy;
19 | import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
20 | import org.camunda.bpm.engine.impl.interceptor.Session;
21 |
22 | /**
23 | * @author Daniel Meyer
24 | *
25 | */
26 | public class ElasticSearchSession implements Session {
27 |
28 | protected ElasticSearchIndexStrategy indexingStrategy;
29 |
30 | protected List historyEvents = new LinkedList();
31 |
32 | public ElasticSearchSession(ElasticSearchIndexStrategy indexingStrategy) {
33 | this.indexingStrategy = indexingStrategy;
34 | }
35 |
36 | public void addHistoryEvent(HistoryEvent historyEvent) {
37 | historyEvents.add(historyEvent);
38 | }
39 |
40 | public void flush() {
41 | indexingStrategy.executeRequest(historyEvents);
42 | }
43 |
44 | public void close() {
45 |
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/java/org/camunda/bpm/cockpit/plugin/dashboards/resources/DashboardsRootResource.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.camunda.bpm.cockpit.plugin.dashboards.resources;
14 |
15 | import javax.ws.rs.Path;
16 | import javax.ws.rs.PathParam;
17 |
18 | import org.camunda.bpm.cockpit.plugin.dashboards.DashboardsPlugin;
19 | import org.camunda.bpm.cockpit.plugin.resource.AbstractCockpitPluginRootResource;
20 |
21 | @Path("plugin/" + DashboardsPlugin.ID)
22 | public class DashboardsRootResource extends AbstractCockpitPluginRootResource {
23 |
24 | public DashboardsRootResource() {
25 | super(DashboardsPlugin.ID);
26 | }
27 |
28 | @Path("{engineName}"+SearchResource.PATH)
29 | public SearchResource search(@PathParam("engineName") String engineName) {
30 | return subResource(new SearchResource(engineName), engineName);
31 | }
32 |
33 | @Path("{engineName}"+ProcessInstanceHistogramResource.PATH)
34 | public ProcessInstanceHistogramResource dateHistogram(@PathParam("engineName") String engineName) {
35 | return subResource(new ProcessInstanceHistogramResource(engineName), engineName);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/java/org/camunda/bpm/elasticsearch/query/HistoryEventsQueryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.query;
18 |
19 | import org.camunda.bpm.elasticsearch.AbstractElasticSearchTest;
20 | import org.camunda.bpm.elasticsearch.ProcessDataContainer;
21 | import org.camunda.bpm.elasticsearch.TestDataGenerator;
22 | import org.camunda.bpm.engine.test.ProcessEngineRule;
23 | import org.junit.Ignore;
24 | import org.junit.Rule;
25 | import org.junit.Test;
26 |
27 | import java.util.HashMap;
28 |
29 | public class HistoryEventsQueryTest extends AbstractElasticSearchTest {
30 |
31 | @Ignore
32 | @Test
33 | public void queryShouldFindProcessInstanceViaVariableValue() {
34 | HashMap stringProcessDataContainerHashMap = TestDataGenerator.startInvoiceProcess(processEngineRule.getProcessEngine(), 10);
35 |
36 | // flushAndRefresh();
37 |
38 | // QueryBuilders.boolQuery().must(QueryBuilders.nestedQuery(""))
39 | // SearchRequestBuilder srb = client.prepareSearch().setQuery();
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/resources/query/querystring_all.json:
--------------------------------------------------------------------------------
1 | {
2 | "query": {
3 | "filtered": {
4 | "query": {
5 | "match": {}
6 | },
7 | "filter": {
8 | "bool": {
9 | "should": [
10 | {
11 | "nested": {
12 | "path": "variables",
13 | "query": {
14 | "query_string": {
15 | "fields": ["variables.*"],
16 | "query": "somebody",
17 | "use_dis_max" : true,
18 | "lenient" : true
19 | }
20 | }
21 | }
22 | },
23 | {
24 | "nested": {
25 | "path": "activities",
26 | "query": {
27 | "query_string": {
28 | "fields": ["activities.*"],
29 | "query": "somebody",
30 | "use_dis_max" : true,
31 | "lenient" : true
32 | }
33 | }
34 | }
35 | },
36 | {
37 | "nested": {
38 | "path": "tasks",
39 | "query": {
40 | "query_string": {
41 | "fields": ["tasks.*"],
42 | "query": "somebody",
43 | "use_dis_max" : true,
44 | "lenient" : true
45 | }
46 | }
47 | }
48 | }
49 | ]
50 | }
51 | }
52 | }
53 | },
54 | "facets" : {
55 | "histo1" : {
56 | "date_histogram" : {
57 | "key_field" : "endTime",
58 | "interval" : "day"
59 | }
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/resources/plugin-webapp/dashboards/app/pages/processDefinition.js:
--------------------------------------------------------------------------------
1 | ngDefine('dashboards.pages.processDefinition', [
2 | 'angular',
3 | 'require'
4 | ], function(module, angular, require) {
5 |
6 | var Controller = ['$scope', 'processDefinition', 'Views',
7 | function($scope, processDefinition, Views) {
8 |
9 | $scope.processDefinition = processDefinition;
10 |
11 | $scope.exportedVars = { read: [ 'processDefinition' ] };
12 | $scope.dashboardProviders = Views.getProviders({ component: 'dashboard.processDefinition.view' });
13 |
14 | }];
15 |
16 | var RouteConfig = [ '$routeProvider', function($routeProvider) {
17 | $routeProvider.when('/process-definition/:processDefinitionId/dashboard', {
18 | templateUrl: require.toUrl('./process-definition.html'),
19 | controller: Controller,
20 | authentication: 'required',
21 | resolve: {
22 | processDefinition: [ 'ResourceResolver', 'ProcessDefinitionResource',
23 | function(ResourceResolver, ProcessDefinitionResource) {
24 | return ResourceResolver.getByRouteParam('processDefinitionId', {
25 | name: 'process definition',
26 | resolve: function(id) {
27 | return ProcessDefinitionResource.get({ id : id });
28 | }
29 | });
30 | }]
31 | },
32 | reloadOnSearch: false
33 | });
34 | }];
35 |
36 | var ViewConfig = [ 'ViewsProvider', function(ViewsProvider) {
37 | ViewsProvider.registerDefaultView('cockpit.processDefinition.view', {
38 | id: 'dashboard',
39 | priority: 20,
40 | label: 'Dashboard'
41 | });
42 | }];
43 |
44 | module
45 | .config(RouteConfig)
46 | .config(ViewConfig);
47 | });
48 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/index/ElasticSearchIndexStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.index;
18 |
19 | import org.camunda.bpm.elasticsearch.ElasticSearchHistoryEventDispatcher;
20 | import org.camunda.bpm.elasticsearch.jackson.JsonTransformer;
21 | import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
22 | import org.elasticsearch.client.Client;
23 |
24 | import java.util.List;
25 |
26 | public abstract class ElasticSearchIndexStrategy {
27 |
28 | protected ElasticSearchHistoryEventDispatcher dispatcher = new ElasticSearchHistoryEventDispatcher();
29 | protected JsonTransformer transformer = new JsonTransformer();
30 | protected Client esClient;
31 |
32 | public abstract void executeRequest(List historyEvents);
33 |
34 | public abstract void executeRequest(HistoryEvent historyEvent);
35 |
36 | public void setDispatcher(ElasticSearchHistoryEventDispatcher dispatcher) {
37 | this.dispatcher = dispatcher;
38 | }
39 |
40 | public void setEsClient(Client esClient) {
41 | this.esClient = esClient;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | camunda BPM - ElasticSearch extension
2 | ===========================
3 |
4 | [](https://travis-ci.org/camunda/camunda-bpm-elasticsearch)
5 | [](https://drone.io/github.com/camunda/camunda-bpm-elasticsearch/latest)
6 | [](https://waffle.io/camunda/camunda-bpm-elasticsearch)
7 |
8 | camunda-bpm-elasticsearch combines the camunda-bpm platform with the powerful search capabilities of ElasticSearch.
9 | It comes with a process engine plugin which indexes all generated history events through ElasticSearch.
10 | To use the indexed data, a cockpit plugin is also provided to allow querying and retrieval of process instances through ElasticSearch.
11 |
12 | Documentation
13 | -------------
14 |
15 | Read the [User Guide](docs/userguide.md).
16 |
17 | Components (Modules)
18 | --------------------
19 |
20 |
21 | Module Description
22 |
23 |
24 | elasticsearch-cockpit-plugin Integration of ElasticSearch with camunda BPM cockpit as a cockpit plugin.
25 |
26 |
27 | elasticsearch-engine-integration Integration of ElasticSearch with camunda BPM engine as a process engine plugin.
28 |
29 |
30 | elasticsearch-jboss-module Produces in its target directory a folder named modules with all required libraries to use with JBoss 7.x.
31 |
32 |
33 | elasticsearch-lib-module Produces in its target directory a folder with all required libraries to drop into Tomcat's lib directory.
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/elasticsearch-commons/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 | 4.0.0
22 |
23 |
24 | org.camunda.bpm.extension.elasticsearch
25 | camunda-bpm-elasticsearch
26 | 1.0.0-SNAPSHOT
27 |
28 |
29 | elasticsearch-commons
30 | camunda bpm - elasticsearch - commons
31 |
32 |
33 |
34 | org.elasticsearch
35 | elasticsearch
36 |
37 |
38 |
39 | com.fasterxml.jackson.core
40 | jackson-databind
41 |
42 |
43 |
44 | com.google.guava
45 | guava
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/handler/ElasticSearchTransactionAwareHistoryEventHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.handler;
18 |
19 | import org.camunda.bpm.elasticsearch.session.ElasticSearchSession;
20 | import org.camunda.bpm.engine.impl.context.Context;
21 | import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
22 |
23 | /**
24 | * {@link ElasticSearchHistoryEventHandler} implementation which adds the event to the current {@link ElasticSearchSession}.
25 | * The ElasticSearchSession is opened once per command and batches all events fired until the session is closed.
26 | *
27 | * @author Christian Lipphardt
28 | * @author Daniel Meyer
29 | *
30 | */
31 | public class ElasticSearchTransactionAwareHistoryEventHandler extends ElasticSearchHistoryEventHandler {
32 |
33 | public void handleEvent(HistoryEvent historyEvent) {
34 |
35 | // get or create current ElasticSearchSession
36 | ElasticSearchSession elasticSearchSession = Context.getCommandContext()
37 | .getSession(ElasticSearchSession.class);
38 |
39 | // add event to elastic search session.
40 | elasticSearchSession.addHistoryEvent(historyEvent);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/resources/plugin-webapp/dashboards/app/pages/search.html:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
Search Results
17 |
18 |
38 |
39 |
40 |
61 |
62 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/java/org/camunda/bpm/example/invoice/service/ArchiveInvoiceService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /* Licensed under the Apache License, Version 2.0 (the "License");
18 | * you may not use this file except in compliance with the License.
19 | * You may obtain a copy of the License at
20 | *
21 | * http://www.apache.org/licenses/LICENSE-2.0
22 | *
23 | * Unless required by applicable law or agreed to in writing, software
24 | * distributed under the License is distributed on an "AS IS" BASIS,
25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26 | * See the License for the specific language governing permissions and
27 | * limitations under the License.
28 | */
29 | package org.camunda.bpm.example.invoice.service;
30 |
31 | import org.camunda.bpm.engine.delegate.DelegateExecution;
32 | import org.camunda.bpm.engine.delegate.JavaDelegate;
33 |
34 | import java.util.logging.Logger;
35 |
36 | /**
37 | * This is an empty service implementation illustrating how to use a plain
38 | * Java Class as a BPMN 2.0 Service Task delegate.
39 | */
40 | public class ArchiveInvoiceService implements JavaDelegate {
41 |
42 | private final Logger LOGGER = Logger.getLogger(ArchiveInvoiceService.class.getName());
43 |
44 | public void execute(DelegateExecution execution) throws Exception {
45 |
46 | LOGGER.info("\n\n ... Now archiving invoice " + execution.getProcessInstanceId() +"\n");
47 |
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/ElasticSearchHistoryEventDispatcher.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch;
18 |
19 | import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
20 |
21 | public class ElasticSearchHistoryEventDispatcher {
22 |
23 | public static final String DEFAULT_DISPATCH_INDEX = ElasticSearchHistoryPluginConfiguration.ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM;
24 | public static final String DEFAULT_DISPATCH_TYPE = ElasticSearchHistoryPluginConfiguration.ES_DEFAULT_TYPE_NAME_CAMUNDA_BPM;
25 |
26 |
27 | protected String getDispatchTargetIndexForHistoryEvent(HistoryEvent historyEvent) {
28 | return null;
29 | }
30 |
31 | public String getDispatchTargetIndex(HistoryEvent historyEvent) {
32 | String dispatchTargetTypeForHistoryEvent = getDispatchTargetIndexForHistoryEvent(historyEvent);
33 | if (dispatchTargetTypeForHistoryEvent != null) {
34 | return dispatchTargetTypeForHistoryEvent;
35 | } else {
36 | return DEFAULT_DISPATCH_INDEX;
37 | }
38 | }
39 |
40 | protected String getDispatchTargetTypeForHistoryEvent(HistoryEvent historyEvent) {
41 | return null;
42 | }
43 |
44 | public String getDispatchTargetType(HistoryEvent historyEvent) {
45 | String dispatchTargetForHistoryEvent = getDispatchTargetTypeForHistoryEvent(historyEvent);
46 | if (dispatchTargetForHistoryEvent != null) {
47 | return dispatchTargetForHistoryEvent;
48 | } else {
49 | return DEFAULT_DISPATCH_TYPE;
50 | }
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/elasticsearch-jboss-module/src/main/modules/org/camunda/bpm/jboss/camunda-jboss-subsystem/main/module.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/resources/camunda.cfg.remote.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/resources/camunda.cfg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/java/org/camunda/bpm/cockpit/plugin/ElasticSearchClientProvider.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.camunda.bpm.cockpit.plugin;
14 |
15 | import java.util.List;
16 |
17 | import org.camunda.bpm.elasticsearch.ElasticSearchClient;
18 | import org.camunda.bpm.elasticsearch.ElasticSearchHistoryPlugin;
19 | import org.camunda.bpm.elasticsearch.ElasticSearchHistoryPluginConfiguration;
20 | import org.camunda.bpm.engine.ProcessEngine;
21 | import org.camunda.bpm.engine.impl.ProcessEngineImpl;
22 | import org.camunda.bpm.engine.impl.cfg.ProcessEnginePlugin;
23 | import org.elasticsearch.client.Client;
24 |
25 | /**
26 | * @author Daniel Meyer
27 | *
28 | */
29 | public class ElasticSearchClientProvider {
30 |
31 | protected static ElasticSearchClient cachedClient = null;
32 |
33 | public static Client getClient(ProcessEngine processEngine) {
34 |
35 | if(cachedClient == null) {
36 | createClient(processEngine);
37 | }
38 |
39 | return cachedClient.get();
40 | }
41 |
42 | protected static void createClient(ProcessEngine processEngine) {
43 | List processEnginePlugins = ((ProcessEngineImpl) processEngine).getProcessEngineConfiguration()
44 | .getProcessEnginePlugins();
45 |
46 | // check whether process enigne has elastic search plugin configured
47 | for (ProcessEnginePlugin processEnginePlugin : processEnginePlugins) {
48 | if (processEnginePlugin instanceof ElasticSearchHistoryPlugin) {
49 | ElasticSearchHistoryPlugin historyPlugin = (ElasticSearchHistoryPlugin) processEnginePlugin;
50 | cachedClient = historyPlugin.getElasticSearchClient();
51 | break;
52 | }
53 | }
54 |
55 | if(cachedClient == null) {
56 | // create new client from classpath configuration
57 | ElasticSearchHistoryPluginConfiguration elasticSearchPluginConfiguration = ElasticSearchHistoryPluginConfiguration.readConfigurationFromClasspath();
58 | cachedClient = new ElasticSearchClient(elasticSearchPluginConfiguration);
59 | }
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/java/org/camunda/bpm/elasticsearch/AbstractElasticSearchActiveMQTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch;
18 |
19 | import org.apache.activemq.ActiveMQConnectionFactory;
20 | import org.apache.activemq.broker.BrokerService;
21 | import org.apache.activemq.broker.TransportConnector;
22 | import org.apache.activemq.broker.region.policy.PolicyEntry;
23 | import org.apache.activemq.broker.region.policy.PolicyMap;
24 | import org.junit.After;
25 | import org.junit.Before;
26 |
27 | import javax.jms.Connection;
28 | import java.net.URI;
29 |
30 | public class AbstractElasticSearchActiveMQTest { // extends AbstractElasticSearchTest {
31 |
32 | private BrokerService broker;
33 | private TransportConnector connector;
34 | private Connection connection;
35 |
36 | @Before
37 | public void setup() throws Exception {
38 | broker = createBroker();
39 | connector = new TransportConnector();
40 | connector.setUri(new URI("vm://localhost"));
41 | broker.addConnector(connector);
42 | broker.start();
43 |
44 | ActiveMQConnectionFactory connFactory =
45 | new ActiveMQConnectionFactory(connector.getConnectUri() + "?jms.prefetchPolicy.all=1");
46 | connection = connFactory.createConnection();
47 | connection.start();
48 | }
49 |
50 | @After
51 | public void tearDown() throws Exception {
52 | connector.stop();
53 | connection.close();
54 | broker.stop();
55 | }
56 |
57 | protected BrokerService createBroker() {
58 | BrokerService brokerService = new BrokerService();
59 | brokerService.setPersistent(false);
60 | brokerService.setUseJmx(false);
61 |
62 | PolicyMap policyMap = new PolicyMap();
63 | PolicyEntry policy = new PolicyEntry();
64 | policy.setConsumersBeforeDispatchStarts(2);
65 | policy.setTimeBeforeDispatchStarts(1000);
66 | policyMap.setDefaultEntry(policy);
67 |
68 | brokerService.setDestinationPolicy(policyMap);
69 |
70 | return brokerService;
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/java/org/camunda/bpm/elasticsearch/entity/EntityFilterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.entity;
18 |
19 | import com.fasterxml.jackson.core.JsonProcessingException;
20 | import org.camunda.bpm.elasticsearch.jackson.JsonTransformer;
21 | import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
22 | import org.junit.After;
23 | import org.junit.Before;
24 | import org.junit.Test;
25 |
26 | import java.io.IOException;
27 | import java.util.Map;
28 |
29 | import static org.junit.Assert.assertEquals;
30 | import static org.junit.Assert.assertFalse;
31 |
32 | public class EntityFilterTest {
33 |
34 | private HistoryEvent historyEvent;
35 | private JsonTransformer jsonTransformer;
36 |
37 | @Before
38 | public void setUp() {
39 | jsonTransformer = new JsonTransformer();
40 |
41 | historyEvent = new HistoryEvent();
42 | historyEvent.setId("id");
43 | historyEvent.setExecutionId("ExecutionId");
44 | historyEvent.setProcessDefinitionId("processDefinitionId");
45 | historyEvent.setProcessInstanceId("processInstanceId");
46 | historyEvent.setEventType(HistoryEvent.ACTIVITY_EVENT_TYPE_START);
47 |
48 | assertEquals(HistoryEvent.class, historyEvent.getPersistentState());
49 | }
50 |
51 | @After
52 | public void tearDown() {
53 | jsonTransformer = null;
54 | }
55 |
56 | @Test
57 | public void shouldTransformHistoryEventToJsonAndApplyMixInFilter() throws JsonProcessingException {
58 | String json = jsonTransformer.transformToJson(historyEvent);
59 |
60 | assertFalse(json.contains(HistoryEvent.class.getName()));
61 | }
62 |
63 | @Test
64 | public void shouldTransformJsonToMapAndApplyMixInFilter() throws IOException {
65 | String json = jsonTransformer.transformToJson(historyEvent);
66 | assertFalse(json.contains(HistoryEvent.class.getName()));
67 |
68 | Map jsonMap = jsonTransformer.transformJsonToMap(json);
69 | assertFalse(jsonMap.containsKey(""));
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/elasticsearch-jboss-module/src/main/modules/org/camunda/bpm/elasticsearch/main/module.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/index/ElasticSearchBulkIndexStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.index;
18 |
19 | import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
20 | import org.elasticsearch.action.bulk.BulkItemResponse;
21 | import org.elasticsearch.action.bulk.BulkRequestBuilder;
22 | import org.elasticsearch.action.bulk.BulkResponse;
23 | import org.elasticsearch.action.update.UpdateRequestBuilder;
24 | import org.elasticsearch.common.unit.TimeValue;
25 |
26 | import java.io.IOException;
27 | import java.util.List;
28 | import java.util.logging.Level;
29 |
30 | public class ElasticSearchBulkIndexStrategy extends ElasticSearchDefaultIndexStrategy {
31 |
32 | @Override
33 | public void executeRequest(List historyEvents) {
34 | BulkRequestBuilder bulkRequestBuilder = esClient.prepareBulk();
35 | try {
36 | for (HistoryEvent historyEvent : historyEvents) {
37 | UpdateRequestBuilder updateRequestBuilder = prepareUpdateRequest(historyEvent);
38 | bulkRequestBuilder.add(updateRequestBuilder);
39 | }
40 |
41 | BulkResponse bulkResponse;
42 | if (WAIT_FOR_RESPONSE > 0) {
43 | bulkResponse = bulkRequestBuilder.get(TimeValue.timeValueSeconds(WAIT_FOR_RESPONSE));
44 | } else {
45 | bulkResponse = bulkRequestBuilder.get();
46 | }
47 |
48 | if (bulkResponse.hasFailures()) {
49 | LOGGER.severe("Error while executing bulk request: " + bulkResponse.buildFailureMessage());
50 | }
51 |
52 | if (LOGGER.isLoggable(Level.FINEST)) {
53 | for (BulkItemResponse bulkItemResponse : bulkResponse) {
54 | LOGGER.finest("[" + bulkItemResponse.getIndex() +
55 | "][" + bulkItemResponse.getType() +
56 | "][" + bulkItemResponse.getOpType() +
57 | "] process instance with id '" + bulkItemResponse.getId() + "'");
58 | }
59 | }
60 | } catch (IOException e) {
61 | LOGGER.log(Level.SEVERE, e.getMessage(), e);
62 | }
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/java/org/camunda/bpm/elasticsearch/cfg/ElasticSearchHistoryPluginConfigurationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.cfg;
18 |
19 |
20 | import com.fasterxml.jackson.core.JsonProcessingException;
21 | import com.fasterxml.jackson.databind.ObjectMapper;
22 | import org.camunda.bpm.elasticsearch.index.ElasticSearchDefaultIndexStrategy;
23 | import org.camunda.bpm.elasticsearch.ElasticSearchHistoryPluginConfiguration;
24 | import org.junit.Test;
25 |
26 | import static org.junit.Assert.assertEquals;
27 | import static org.junit.Assert.assertFalse;
28 | import static org.junit.Assert.assertTrue;
29 |
30 | public class ElasticSearchHistoryPluginConfigurationTest {
31 |
32 | @Test
33 | public void writeElasticSearchHistoryPluginConfiguration() throws JsonProcessingException {
34 | ObjectMapper objectMapper = new ObjectMapper();
35 |
36 | String config = objectMapper.writeValueAsString(new ElasticSearchHistoryPluginConfiguration());
37 | System.out.println(config);
38 | }
39 |
40 | @Test
41 | public void readElasticSearchHistoryPluginConfiguration() {
42 | ElasticSearchHistoryPluginConfiguration historyPluginConfiguration = ElasticSearchHistoryPluginConfiguration.readConfigurationFromClasspath();
43 | assertEquals("camundabpm", historyPluginConfiguration.getIndex());
44 | assertEquals("processinstance", historyPluginConfiguration.getType());
45 | assertEquals(ElasticSearchDefaultIndexStrategy.class.getName(), historyPluginConfiguration.getIndexingStrategy());
46 |
47 | assertEquals("localhost", historyPluginConfiguration.getEsHost());
48 | assertEquals("9300", historyPluginConfiguration.getEsPort());
49 | assertEquals("camunda_bpm_es_cluster", historyPluginConfiguration.getEsClusterName());
50 | assertFalse(historyPluginConfiguration.isTransportClient());
51 |
52 | // check properties
53 | assertEquals("_local_", historyPluginConfiguration.getProperties().get("es.network.host"));
54 | assertTrue((Boolean) historyPluginConfiguration.getProperties().get("es.node.local"));
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/resources/plugin-webapp/dashboards/app/pages/activityMonitor.js:
--------------------------------------------------------------------------------
1 | ngDefine('dashboards.pages.activityMonitor', [
2 | 'angular',
3 | 'require',
4 | '../lib/freewall',
5 | '../lib/d3.min',
6 | '../lib/nv.d3.min',
7 | 'module:nvd3:../lib/angular-nvd3'
8 | ], function(module, angular, require) {
9 |
10 | var Controller = ['$scope', '$http', 'Uri', '$compile', '$timeout',
11 | function($scope, $http, Uri, $compile, $timeout) {
12 |
13 | $('head').append(' ');
14 |
15 | $scope.interval = '1s';
16 | $scope.timeframe = undefined;
17 | $scope.options = {
18 | chart: {
19 | type: 'stackedAreaChart',
20 | height: 400,
21 | margin : {
22 | top: 20,
23 | right: 20,
24 | bottom: 60,
25 | left: 40
26 | },
27 | x: function(bucket){return bucket.key;},
28 | y: function(bucket){return bucket.docCount;},
29 | useVoronoi: false,
30 | clipEdge: true,
31 | transitionDuration: 1,
32 | useInteractiveGuideline: true,
33 | xAxis: {
34 | showMaxMin: true,
35 | tickFormat: function(d) {
36 | return d3.time.format('%x')(new Date(d));
37 | }
38 | },
39 | yAxis: {
40 | tickFormat: function(d){
41 | return d3.format(',.2f')(d);
42 | }
43 | }
44 | }
45 | };
46 |
47 |
48 | $scope.getHistogramData = function() {
49 |
50 |
51 | $http({
52 | method: 'GET',
53 | url: Uri.appUri("plugin://dashboards/default/histogram/processinstance"),
54 | params: {
55 | interval: $scope.interval,
56 | timeframe: $scope.timeframe
57 | }
58 | }).success(function(data) {
59 | histogramData = [];
60 |
61 |
62 | var values = [];
63 |
64 |
65 | $scope.data = [];
66 | var ended = { "key": "Ended Process Instances", "values": [] };
67 | $scope.data.push(ended);
68 |
69 | angular.forEach(data.dateHistogramBuckets.ended, function(bucket) {
70 | $scope.data[0].values.push(bucket);
71 | }, values);
72 |
73 | $timeout($scope.getHistogramData, 2000);
74 | });
75 |
76 | };
77 |
78 | $scope.getHistogramData();
79 | }];
80 |
81 | var ViewConfig = [ 'ViewsProvider', function(ViewsProvider) {
82 | ViewsProvider.registerDefaultView('cockpit.dashboard', {
83 | id: 'activity-monitor',
84 | priority: 19,
85 | label: 'Activity Monitor',
86 | controller: Controller,
87 | url: require.toUrl('./activity-monitor.html')
88 | });
89 | }];
90 |
91 | module
92 | .config(ViewConfig);
93 | });
94 |
--------------------------------------------------------------------------------
/elasticsearch-lib-module/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 | 4.0.0
22 |
23 |
24 | org.camunda.bpm.extension.elasticsearch
25 | camunda-bpm-elasticsearch
26 | 1.0.0-SNAPSHOT
27 |
28 |
29 | elasticsearch-lib-module
30 | camunda bpm - elasticsearch - tomcat
31 |
32 |
33 |
34 | org.camunda.bpm.extension.elasticsearch
35 | elasticsearch-engine-integration
36 | ${project.version}
37 | provided
38 |
39 |
40 | org.elasticsearch
41 | elasticsearch
42 |
43 |
44 |
45 |
46 |
47 | lib
48 |
49 | true
50 |
51 |
52 |
53 |
54 | org.apache.maven.plugins
55 | maven-dependency-plugin
56 | 2.8
57 |
58 |
59 | copy-dependencies
60 | prepare-package
61 |
62 | copy-dependencies
63 |
64 |
65 | ${project.build.directory}/lib/
66 | false
67 | false
68 | true
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/util/ClassUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.util;
18 |
19 | import org.camunda.bpm.engine.ClassLoadingException;
20 | import org.camunda.bpm.engine.ProcessEngineException;
21 | import org.camunda.bpm.engine.impl.util.ClassLoaderUtil;
22 |
23 | public class ClassUtil {
24 |
25 | public static T createInstance(Class extends T> clazz) {
26 | try {
27 | return clazz.newInstance();
28 |
29 | } catch (InstantiationException e) {
30 | throw new ProcessEngineException("Could not instantiate class", e);
31 | } catch (IllegalAccessException e) {
32 | throw new ProcessEngineException("IllegalAccessException while instantiating class", e);
33 | }
34 | }
35 |
36 | @SuppressWarnings("unchecked")
37 | public static Class extends T> loadClass(String className, ClassLoader customClassloader, Class clazz) {
38 | try {
39 | if(customClassloader != null) {
40 | return (Class extends T>) customClassloader.loadClass(className);
41 | }else {
42 | return (Class extends T>) ClassUtil.loadClass(className);
43 | }
44 |
45 | } catch (ClassNotFoundException e) {
46 | throw new ProcessEngineException("Could not load configuration class", e);
47 |
48 | } catch (ClassCastException e) {
49 | throw new ProcessEngineException("Custom class of wrong type. Must extend "+clazz.getName(), e);
50 |
51 | }
52 | }
53 |
54 | public static Class> loadClass(String className) {
55 | Class> clazz = null;
56 | ClassLoader classLoader = null;
57 |
58 | Throwable throwable = null;
59 |
60 | try {
61 | ClassLoader contextClassloader = ClassLoaderUtil.getContextClassloader();
62 | if(contextClassloader != null) {
63 | clazz = Class.forName(className, true, contextClassloader);
64 | }
65 | } catch(Throwable t) {
66 | if(throwable == null) {
67 | throwable = t;
68 | }
69 | }
70 | if(clazz == null) {
71 | try {
72 | clazz = Class.forName(className, true, ClassUtil.class.getClassLoader());
73 | } catch(Throwable t) {
74 | if(throwable == null) {
75 | throwable = t;
76 | }
77 | }
78 | }
79 | if(clazz == null) {
80 | throw new ClassLoadingException(className, throwable);
81 | }
82 | return clazz;
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/elasticsearch-commons/src/main/java/org/camunda/bpm/elasticsearch/util/JsonHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.util;
18 |
19 | import com.fasterxml.jackson.databind.ObjectMapper;
20 | import com.fasterxml.jackson.databind.type.MapType;
21 | import com.fasterxml.jackson.databind.type.TypeFactory;
22 |
23 | import java.io.IOException;
24 | import java.io.InputStream;
25 | import java.util.HashMap;
26 | import java.util.Map;
27 | import java.util.Scanner;
28 |
29 | public class JsonHelper {
30 |
31 | private static ObjectMapper objectMapper = new ObjectMapper();
32 |
33 | public static T readJsonFromClasspath(Class clazz, String fileName) {
34 | InputStream inputStream = null;
35 |
36 | try {
37 | inputStream = IoUtil.getResourceAsStream(fileName);
38 | if (inputStream == null) {
39 | throw new RuntimeException("File '" + fileName + "' not found!");
40 | }
41 |
42 | T typedJson = objectMapper.readValue(inputStream, clazz);
43 |
44 | return typedJson;
45 | } catch (IOException e) {
46 | throw new RuntimeException("Unable to load json [" + fileName + "] from classpath", e);
47 | } finally {
48 | IoUtil.closeSilently(inputStream);
49 | }
50 | }
51 |
52 | public static Map readJsonFromClasspathAsMap(String fileName) {
53 | InputStream inputStream = null;
54 |
55 | try {
56 | inputStream = IoUtil.getResourceAsStream(fileName);
57 | if (inputStream == null) {
58 | throw new RuntimeException("File '" + fileName + "' not found!");
59 | }
60 |
61 | MapType type = TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, Object.class);
62 | HashMap mapping = objectMapper.readValue(inputStream, type);
63 |
64 | return mapping;
65 | } catch (IOException e) {
66 | throw new RuntimeException("Unable to load json [" + fileName + "] from classpath", e);
67 | } finally {
68 | IoUtil.closeSilently(inputStream);
69 | }
70 | }
71 |
72 | public static String readJsonFromClasspathAsString(String fileName) {
73 | InputStream inputStream = null;
74 |
75 | try {
76 | inputStream = IoUtil.getResourceAsStream(fileName);
77 | if (inputStream == null) {
78 | throw new RuntimeException("File '" + fileName + "' not found!");
79 | }
80 |
81 | Scanner s = new java.util.Scanner(inputStream, "UTF-8").useDelimiter("\\A");
82 | return s.hasNext() ? s.next() : null;
83 | } finally {
84 | IoUtil.closeSilently(inputStream);
85 | }
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/elasticsearch-commons/src/main/java/org/camunda/bpm/elasticsearch/util/IoUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.util;
18 |
19 | import com.google.common.io.Files;
20 |
21 | import java.io.File;
22 | import java.io.IOException;
23 | import java.io.InputStream;
24 | import java.io.OutputStream;
25 | import java.nio.charset.Charset;
26 |
27 | public class IoUtil {
28 |
29 | public static final Charset DEFAULT_UTF_8_CHARSET = Charset.forName("UTF-8");
30 |
31 | public static void writeToFile(String content, String fileName) {
32 | writeToFile(content.getBytes(DEFAULT_UTF_8_CHARSET), fileName, true);
33 | }
34 |
35 | public static void writeToFile(String content, String fileName, boolean createFile) {
36 | writeToFile(content.getBytes(DEFAULT_UTF_8_CHARSET), fileName, createFile);
37 | }
38 |
39 | public static void writeToFile(byte[] content, String fileName, boolean createFile) {
40 | File file = new File(fileName);
41 | try {
42 | if (createFile) {
43 | Files.createParentDirs(file);
44 | file.createNewFile();
45 | }
46 |
47 | Files.write(content, file);
48 | } catch (IOException e) {
49 | // nop
50 | }
51 | }
52 |
53 | public static InputStream getResourceAsStream(String name) {
54 | InputStream resourceStream = null;
55 |
56 | // Try the current Thread context classloader
57 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
58 | resourceStream = classLoader.getResourceAsStream(name);
59 | if(resourceStream == null) {
60 | // Finally, try the classloader for this class
61 | classLoader = IoUtil.class.getClassLoader();
62 | resourceStream = classLoader.getResourceAsStream(name);
63 | }
64 |
65 | return resourceStream;
66 | }
67 |
68 | /**
69 | * Closes the given stream. The same as calling {@link InputStream#close()}, but
70 | * errors while closing are silently ignored.
71 | */
72 | public static void closeSilently(InputStream inputStream) {
73 | try {
74 | if(inputStream != null) {
75 | inputStream.close();
76 | }
77 | } catch(IOException ignore) {
78 | // Exception is silently ignored
79 | }
80 | }
81 |
82 | /**
83 | * Closes the given stream. The same as calling {@link java.io.OutputStream#close()}, but
84 | * errors while closing are silently ignored.
85 | */
86 | public static void closeSilently(OutputStream outputStream) {
87 | try {
88 | if(outputStream != null) {
89 | outputStream.close();
90 | }
91 | } catch(IOException ignore) {
92 | // Exception is silently ignored
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/jackson/JsonTransformer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.jackson;
18 |
19 | import com.fasterxml.jackson.annotation.JsonInclude;
20 | import com.fasterxml.jackson.core.JsonProcessingException;
21 | import com.fasterxml.jackson.databind.JavaType;
22 | import com.fasterxml.jackson.databind.ObjectMapper;
23 | import com.fasterxml.jackson.databind.type.CollectionType;
24 | import com.fasterxml.jackson.databind.type.MapType;
25 | import com.fasterxml.jackson.databind.type.SimpleType;
26 | import com.fasterxml.jackson.databind.type.TypeFactory;
27 | import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
28 |
29 | import java.io.IOException;
30 | import java.util.ArrayList;
31 | import java.util.HashMap;
32 | import java.util.List;
33 | import java.util.Map;
34 |
35 | public class JsonTransformer {
36 |
37 | protected ObjectMapper mapper = new ObjectMapper();
38 |
39 | public JsonTransformer() {
40 | configureJsonMapper(null);
41 | }
42 |
43 | public JsonTransformer(HashMap, Class> mixInFilters) {
44 | configureJsonMapper(mixInFilters);
45 | }
46 |
47 | protected void configureJsonMapper(HashMap, Class> mixInFilters) {
48 | //mapper.configure(WRITE_DATE_KEYS_AS_TIMESTAMPS, true);
49 |
50 | // serialize only non null values
51 | mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
52 | // allow filtering of entities
53 | mapper.registerModule(new JacksonMixInFilterModule(mixInFilters));
54 | mapper.setFilters(JacksonMixInFilterModule.getCustomFilterProvider());
55 | }
56 |
57 | public String transformToJson(HistoryEvent historyEvent) throws JsonProcessingException {
58 | return mapper.writeValueAsString(historyEvent);
59 | }
60 |
61 | public byte[] transformToBytes(HistoryEvent historyEvent) throws JsonProcessingException {
62 | return mapper.writeValueAsBytes(historyEvent);
63 | }
64 |
65 | public Map transformJsonToMap(String json) throws IOException {
66 | MapType mapType = TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, Object.class);
67 | return mapper.readValue(json, mapType);
68 | }
69 |
70 | public List> transformJsonToList(String json) throws IOException {
71 | JavaType javaType = MapType.construct(HashMap.class, SimpleType.construct(String.class), SimpleType.construct(Object.class));
72 |
73 | CollectionType listType = TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, javaType);
74 | return mapper.readValue(json, listType);
75 | }
76 |
77 | }
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/develop/resources/WEB-INF/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/java/org/camunda/bpm/elasticsearch/index/HistoryEventsIndexingTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.index;
18 |
19 | import org.camunda.bpm.elasticsearch.AbstractElasticSearchTest;
20 | import org.camunda.bpm.elasticsearch.ProcessDataContainer;
21 | import org.camunda.bpm.elasticsearch.TestDataGenerator;
22 | import org.camunda.bpm.elasticsearch.util.IoUtil;
23 | import org.elasticsearch.action.search.SearchRequestBuilder;
24 | import org.elasticsearch.action.search.SearchResponse;
25 | import org.elasticsearch.index.query.FilterBuilders;
26 | import org.elasticsearch.index.query.FilteredQueryBuilder;
27 | import org.elasticsearch.index.query.QueryBuilders;
28 | import org.elasticsearch.search.SearchHit;
29 | import org.elasticsearch.search.SearchHits;
30 | import org.junit.Test;
31 |
32 | import java.io.IOException;
33 | import java.util.ArrayList;
34 | import java.util.HashMap;
35 | import java.util.Map;
36 |
37 | import static org.camunda.bpm.elasticsearch.ElasticSearchHistoryPluginConfiguration.ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM;
38 | import static org.camunda.bpm.elasticsearch.ElasticSearchHistoryPluginConfiguration.ES_DEFAULT_TYPE_NAME_CAMUNDA_BPM;
39 | import static org.junit.Assert.assertEquals;
40 | import static org.junit.Assert.assertNotNull;
41 |
42 | public class HistoryEventsIndexingTest extends AbstractElasticSearchTest {
43 |
44 | @Test
45 | public void testIndexingSingleInvoice() throws IOException {
46 | HashMap processesById = TestDataGenerator.startInvoiceProcess(processEngineRule.getProcessEngine(), 1);
47 |
48 | String[] pids = processesById.keySet().toArray(new String[0]);
49 |
50 | // elasticsearch //////////////////////////////
51 |
52 | flushAndRefresh();
53 |
54 | FilteredQueryBuilder query = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.termFilter("processInstanceId", pids[0]));
55 |
56 | SearchRequestBuilder searchRequestBuilder = client.prepareSearch(ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM)
57 | .setQuery(query);
58 |
59 | IoUtil.writeToFile(searchRequestBuilder.toString(), this.getClass().getSimpleName() + ".json", true);
60 |
61 | SearchResponse searchResponse = searchRequestBuilder.get();
62 | SearchHits hits = searchResponse.getHits();
63 | assertEquals(1, hits.totalHits());
64 |
65 | SearchHit hit = hits.getAt(0);
66 | assertEquals(pids[0], hit.getId());
67 | assertEquals(ES_DEFAULT_TYPE_NAME_CAMUNDA_BPM, hit.getType());
68 |
69 | Map source = hit.getSource();
70 | assertNotNull(source.get("startTime"));
71 | assertNotNull(source.get("endTime"));
72 | ArrayList variables = (ArrayList) source.get("variables");
73 | assertEquals(5, variables.size());
74 | ArrayList tasks = (ArrayList) source.get("tasks");
75 | assertEquals(9, tasks.size());
76 | ArrayList activities = (ArrayList) source.get("activities");
77 | assertEquals(19, activities.size());
78 |
79 | logger.info(searchResponse.toString());
80 |
81 | // for (SearchHit searchHit : searchResponse.getHits()) {
82 | // logger.info(searchHit.sourceAsString());
83 | // }
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/java/org/camunda/bpm/elasticsearch/index/HistoryEventsMappingTest.java:
--------------------------------------------------------------------------------
1 | package org.camunda.bpm.elasticsearch.index;
2 |
3 | import com.github.tlrx.elasticsearch.test.annotations.ElasticsearchIndex;
4 | import com.github.tlrx.elasticsearch.test.annotations.ElasticsearchMapping;
5 | import org.camunda.bpm.elasticsearch.AbstractElasticSearchTest;
6 | import org.camunda.bpm.elasticsearch.ProcessDataContainer;
7 | import org.camunda.bpm.elasticsearch.TestDataGenerator;
8 | import org.camunda.bpm.elasticsearch.util.IoUtil;
9 | import org.elasticsearch.action.search.SearchRequestBuilder;
10 | import org.elasticsearch.action.search.SearchResponse;
11 | import org.elasticsearch.index.query.FilterBuilders;
12 | import org.elasticsearch.index.query.FilteredQueryBuilder;
13 | import org.elasticsearch.index.query.QueryBuilders;
14 | import org.elasticsearch.search.SearchHit;
15 | import org.elasticsearch.search.SearchHits;
16 | import org.junit.Test;
17 |
18 | import java.io.IOException;
19 | import java.util.ArrayList;
20 | import java.util.HashMap;
21 | import java.util.Map;
22 |
23 | import static org.camunda.bpm.elasticsearch.ElasticSearchHistoryPluginConfiguration.ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM;
24 | import static org.camunda.bpm.elasticsearch.ElasticSearchHistoryPluginConfiguration.ES_DEFAULT_TYPE_NAME_CAMUNDA_BPM;
25 | import static org.junit.Assert.assertEquals;
26 | import static org.junit.Assert.assertNotNull;
27 |
28 | public class HistoryEventsMappingTest extends AbstractElasticSearchTest {
29 |
30 | @Test
31 | // @ElasticsearchIndex(
32 | // indexName = ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM,
33 | // cleanAfter = true,
34 | // mappings = @ElasticsearchMapping(typeName = ES_DEFAULT_TYPE_NAME_CAMUNDA_BPM, properties = {
35 | // @Elasticsearch
36 | // }))
37 | public void testIndexingSingleInvoice() throws IOException {
38 | HashMap processesById = TestDataGenerator.startInvoiceProcess(processEngineRule.getProcessEngine(), 1);
39 |
40 | String[] pids = processesById.keySet().toArray(new String[0]);
41 |
42 | // elasticsearch //////////////////////////////
43 |
44 | flushAndRefresh();
45 |
46 | showMappings(ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM);
47 |
48 | FilteredQueryBuilder query = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.termFilter("processInstanceId", pids[0]));
49 |
50 | SearchRequestBuilder searchRequestBuilder = client.prepareSearch(ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM)
51 | .setQuery(query);
52 |
53 | IoUtil.writeToFile(searchRequestBuilder.toString(), this.getClass().getSimpleName() + ".json", true);
54 |
55 | SearchResponse searchResponse = searchRequestBuilder.get();
56 | SearchHits hits = searchResponse.getHits();
57 | assertEquals(1, hits.totalHits());
58 |
59 |
60 | SearchHit hit = hits.getAt(0);
61 | assertEquals(pids[0], hit.getId());
62 | assertEquals(ES_DEFAULT_TYPE_NAME_CAMUNDA_BPM, hit.getType());
63 |
64 | Map source = hit.getSource();
65 | assertNotNull(source.get("startTime"));
66 | assertNotNull(source.get("endTime"));
67 | ArrayList variables = (ArrayList) source.get("variables");
68 | assertEquals(5, variables.size());
69 | ArrayList tasks = (ArrayList) source.get("tasks");
70 | assertEquals(9, tasks.size());
71 | ArrayList activities = (ArrayList) source.get("activities");
72 | assertEquals(19, activities.size());
73 |
74 | logger.info(searchResponse.toString());
75 |
76 | // for (SearchHit searchHit : searchResponse.getHits()) {
77 | // logger.info(searchHit.sourceAsString());
78 | // }
79 |
80 | showMappings(ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM);
81 |
82 | assertMappings(ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM);
83 | // TODO: write assertions for mapping
84 | }
85 |
86 | protected void assertMappings(String esDefaultIndexNameCamundaBpm) {
87 |
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/jackson/JacksonMixInFilterModule.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.jackson;
18 |
19 | import com.fasterxml.jackson.annotation.JsonFilter;
20 | import com.fasterxml.jackson.core.Version;
21 | import com.fasterxml.jackson.databind.module.SimpleModule;
22 | import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
23 | import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
24 | import org.camunda.bpm.engine.impl.history.event.*;
25 |
26 | import java.util.HashMap;
27 | import java.util.Map;
28 |
29 | public class JacksonMixInFilterModule extends SimpleModule {
30 |
31 | protected HashMap, Class> customMixInFilters;
32 |
33 | public JacksonMixInFilterModule(HashMap, Class> customMixInFilters) {
34 | super("ElasticSearchHistoryEntityMixInFilterModule", Version.unknownVersion());
35 | this.customMixInFilters = customMixInFilters;
36 | }
37 |
38 | @Override
39 | public void setupModule(SetupContext context) {
40 | setupMixIns(context, getDefaultMixInFilters());
41 |
42 | if (customMixInFilters != null) {
43 | setupMixIns(context, customMixInFilters);
44 | }
45 |
46 | super.setupModule(context);
47 | }
48 |
49 | public static String getJsonFilterAnnotationValue(Class clazz) {
50 | JsonFilter jsonFilterAnnotation = (JsonFilter) clazz.getAnnotation(JsonFilter.class);
51 | if (jsonFilterAnnotation != null) {
52 | return jsonFilterAnnotation.value();
53 | }
54 |
55 | return null;
56 | }
57 |
58 | protected void setupMixIns(SetupContext context, HashMap, Class> mixInFilters) {
59 | for (Map.Entry, Class> mixInFilter : mixInFilters.entrySet()) {
60 | context.setMixInAnnotations(mixInFilter.getKey(), mixInFilter.getValue());
61 | }
62 | }
63 |
64 | public static SimpleFilterProvider getCustomFilterProvider() {
65 | SimpleFilterProvider filterProvider = new SimpleFilterProvider();
66 | for (Map.Entry, Class> mixInFilter : getDefaultMixInFilters().entrySet()) {
67 | String jsonFilterId = getJsonFilterAnnotationValue(mixInFilter.getValue());
68 | if (jsonFilterId != null) {
69 | SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.serializeAllExcept("");
70 | // TODO: add extension mechanism to declare filter values via ElasticSearchHistoryPluginConfiguration
71 | filterProvider.addFilter(jsonFilterId, filter);
72 | }
73 | }
74 |
75 | return filterProvider;
76 | }
77 |
78 | public static HashMap, Class> getDefaultMixInFilters() {
79 | HashMap, Class> mixInFilters = new HashMap, Class>();
80 |
81 | mixInFilters.put(HistoryEvent.class, HistoryEventMixIn.class);
82 | mixInFilters.put(HistoricScopeInstanceEvent.class, HistoricScopeInstanceEventMixIn.class); // introduced with 7.0.x
83 | mixInFilters.put(HistoricProcessInstanceEventEntity.class, HistoricProcessInstanceEventMixIn.class);
84 | mixInFilters.put(HistoricTaskInstanceEventEntity.class, HistoricTaskInstanceEventMixIn.class);
85 | mixInFilters.put(HistoricActivityInstanceEventEntity.class, HistoricActivityInstanceEventMixIn.class);
86 | mixInFilters.put(HistoricDetailEventEntity.class, HistoricDetailEventMixIn.class);
87 | mixInFilters.put(HistoricVariableUpdateEventEntity.class, HistoricVariableUpdateEventMixIn.class);
88 | mixInFilters.put(HistoricFormPropertyEventEntity.class, HistoricFormPropertyEventMixIn.class);
89 |
90 | return mixInFilters;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/resources/mapping/processinstance.json:
--------------------------------------------------------------------------------
1 | {
2 | "processinstance" : {
3 | "dynamic_templates": [
4 | {
5 | "id_fields_template": {
6 | "match": "id",
7 | "match_mapping_type": "string",
8 | "mapping": {
9 | "type": "string",
10 | "index": "not_analyzed"
11 | }
12 | }
13 | },
14 | {
15 | "id_fields_template": {
16 | "match": "*Id",
17 | "match_mapping_type": "string",
18 | "mapping": {
19 | "type": "string",
20 | "index": "not_analyzed"
21 | }
22 | }
23 | },
24 | {
25 | "key_fields_template": {
26 | "match_mapping_type": "string",
27 | "match": "*Key",
28 | "mapping": {
29 | "type": "multi_field",
30 | "fields": {
31 | "{name}": {
32 | "type": "{dynamic_type}", "index": "analyzed"
33 | },
34 | "raw": {
35 | "type": "{dynamic_type}", "index": "not_analyzed"
36 | }
37 | }
38 | }
39 | }
40 | },
41 | {
42 | "key_fields_template": {
43 | "match_mapping_type": "string",
44 | "match": "*Name",
45 | "mapping": {
46 | "type": "multi_field",
47 | "fields": {
48 | "{name}": {
49 | "type": "{dynamic_type}", "index": "analyzed"
50 | },
51 | "raw": {
52 | "type": "{dynamic_type}", "index": "not_analyzed"
53 | }
54 | }
55 | }
56 | }
57 | },
58 | {
59 | "time_fields_template": {
60 | "match_mapping_type": "long",
61 | "match": "*Time",
62 | "mapping": {
63 | "type": "date"
64 | }
65 | }
66 | },
67 | {
68 | "timestamp_field_template": {
69 | "match_mapping_type": "long",
70 | "match": "timestamp",
71 | "mapping": {
72 | "type": "date"
73 | }
74 | }
75 | }
76 | ],
77 | "properties": {
78 |
79 | "id": {
80 | "type": "string",
81 | "index": "not_analyzed"
82 | },
83 | "processInstanceId": {
84 | "type": "string",
85 | "index": "not_analyzed"
86 | },
87 | "executionId": {
88 | "type": "string",
89 | "index": "not_analyzed"
90 | },
91 | "processDefinitionId": {
92 | "type": "multi_field",
93 | "fields": {
94 | "processDefinitionId": { "type": "string", "index": "not_analyzed" },
95 | "analyzed": { "type": "string", "index": "analyzed" }
96 | }
97 | },
98 | "eventType": {
99 | "type": "string",
100 | "index": "not_analyzed"
101 | },
102 |
103 |
104 | "businessKey": {
105 | "type": "multi_field",
106 | "fields": {
107 | "businessKey": { "type": "string", "index": "analyzed" },
108 | "raw": { "type": "string", "index": "not_analyzed" }
109 | }
110 | },
111 | "startUserId": {
112 | "type": "multi_field",
113 | "fields": {
114 | "startUserId": { "type": "string", "index": "analyzed" },
115 | "raw": { "type": "string", "index": "not_analyzed" }
116 | }
117 | },
118 | "superProcessInstanceId": {
119 | "type": "string",
120 | "index": "not_analyzed"
121 | },
122 | "deleteReason": {
123 | "type": "multi_field",
124 | "fields": {
125 | "deleteReason": { "type": "string", "index": "analyzed" },
126 | "raw": { "type": "string", "index": "not_analyzed" }
127 | }
128 | },
129 | "startTime": {
130 | "type": "date"
131 | },
132 | "endTime": {
133 | "type": "date"
134 | },
135 | "durationInMillis": {
136 | "type": "long"
137 | },
138 | "startActivityId": {
139 | "type": "string",
140 | "index": "not_analyzed"
141 | },
142 | "endActivityId": {
143 | "type": "string",
144 | "index": "not_analyzed"
145 | },
146 |
147 |
148 | "activities": {
149 | "type": "nested"
150 | },
151 | "tasks": {
152 | "type": "nested"
153 | },
154 | "variables": {
155 | "type": "nested"
156 | }
157 |
158 | }
159 | }
160 | }
--------------------------------------------------------------------------------
/elasticsearch-commons/src/main/java/org/camunda/bpm/elasticsearch/ElasticSearchClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch;
18 |
19 | import org.elasticsearch.client.Client;
20 | import org.elasticsearch.client.transport.TransportClient;
21 | import org.elasticsearch.common.settings.ImmutableSettings;
22 | import org.elasticsearch.common.transport.InetSocketTransportAddress;
23 | import org.elasticsearch.node.Node;
24 | import org.elasticsearch.node.NodeBuilder;
25 |
26 | import java.util.HashMap;
27 | import java.util.Map;
28 | import java.util.logging.Level;
29 | import java.util.logging.Logger;
30 |
31 | public class ElasticSearchClient {
32 |
33 | protected static final Logger LOGGER = Logger.getLogger(ElasticSearchClient.class.getName());
34 |
35 | protected Client esClient;
36 | protected Node esNode;
37 |
38 | protected ElasticSearchHistoryPluginConfiguration historyPluginConfiguration;
39 |
40 | public ElasticSearchClient(ElasticSearchHistoryPluginConfiguration historyPluginConfiguration) {
41 | this.historyPluginConfiguration = historyPluginConfiguration;
42 | this.esClient = init();
43 | }
44 |
45 | protected Client init() {
46 | Client client = null;
47 |
48 | ImmutableSettings.Builder settingsBuilder = ImmutableSettings.builder()
49 | .put("cluster.name", historyPluginConfiguration.getEsClusterName());
50 |
51 | if (historyPluginConfiguration.isTransportClient()) {
52 | // sniff for rest of cluster settingsBuilder.put("client.transport.sniff", true);
53 | addCustomESProperties(settingsBuilder, historyPluginConfiguration.getProperties());
54 |
55 | TransportClient transportClient = new TransportClient(settingsBuilder).addTransportAddress(
56 | new InetSocketTransportAddress(historyPluginConfiguration.getEsHost(), Integer.parseInt(historyPluginConfiguration.getEsPort())));
57 | LOGGER.info("Successfully connected to " + transportClient.connectedNodes());
58 | client = transportClient;
59 | } else {
60 | if (esNode == null) {
61 | // initialize default settings
62 | settingsBuilder
63 | .put("node.name", "rocking-camunda-bpm-history")
64 | .put("node.client", true) // make node a client, so it won't become a master
65 | .put("node.local", false)
66 | .put("node.data", false)
67 | .put("node.http.enabled", true);
68 | // .put("discovery.zen.ping.multicast.enabled", false)
69 | // .put("discovery.zen.ping.unicast.hosts", "127.0.0.1:9300");
70 |
71 | addCustomESProperties(settingsBuilder, historyPluginConfiguration.getProperties());
72 |
73 | esNode = NodeBuilder.nodeBuilder()
74 | .loadConfigSettings(true)
75 | .settings(settingsBuilder)
76 | .build();
77 |
78 | if (LOGGER.isLoggable(Level.INFO)) {
79 | LOGGER.info("Initialized node with settings: " + esNode.settings().getAsMap().toString());
80 | }
81 |
82 | esNode.start();
83 | }
84 |
85 | client = esNode.client();
86 | }
87 |
88 | return client;
89 | }
90 |
91 | public void dispose() {
92 | if (esClient != null) {
93 | esClient.close();
94 | }
95 | if (esNode != null) {
96 | esNode.close();
97 | }
98 | }
99 |
100 | public Client get() {
101 | if (esClient == null) {
102 | esClient = init();
103 | }
104 | return esClient;
105 | }
106 |
107 | public void set(Client esClient) {
108 | this.esClient = esClient;
109 | }
110 |
111 | protected void addCustomESProperties(ImmutableSettings.Builder settingsBuilder, HashMap properties) {
112 | for (Map.Entry property : properties.entrySet()) {
113 | if (property.getKey().toLowerCase().startsWith("es.")) {
114 | settingsBuilder.put(property.getKey().substring(3), property.getValue());
115 | }
116 | }
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/elasticsearch-jboss-module/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
20 |
21 | 4.0.0
22 |
23 |
24 | org.camunda.bpm.extension.elasticsearch
25 | camunda-bpm-elasticsearch
26 | 1.0.0-SNAPSHOT
27 |
28 |
29 | elasticsearch-jboss-module
30 | camunda bpm - elasticsearch - jboss
31 |
32 |
33 |
34 | org.camunda.bpm.extension.elasticsearch
35 | elasticsearch-engine-integration
36 | ${project.version}
37 | provided
38 |
39 |
40 | org.elasticsearch
41 | elasticsearch
42 |
43 |
44 |
45 |
46 |
47 | jboss
48 |
49 | true
50 |
51 |
52 | ${project.build.directory}
53 |
54 |
55 |
56 |
57 | org.apache.maven.plugins
58 | maven-dependency-plugin
59 | 2.8
60 |
61 |
62 | copy-dependencies
63 | prepare-package
64 |
65 | copy-dependencies
66 |
67 |
68 | ${project.build.directory}/modules/org/camunda/bpm/elasticsearch/main/
69 | false
70 | false
71 | true
72 |
73 |
74 |
75 |
76 |
77 | maven-resources-plugin
78 | 2.6
79 |
80 |
81 | copy-modules
82 | prepare-package
83 |
84 | copy-resources
85 |
86 |
87 | ${project.build.directory}/modules
88 |
89 |
90 | src/main/modules
91 | true
92 |
93 |
94 |
95 |
96 |
97 | copy-standalone-xml
98 | prepare-package
99 |
100 | copy-resources
101 |
102 |
103 | ${project.build.directory}/standalone
104 |
105 |
106 | src/main/standalone
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/resources/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | vm://localhost
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
49 |
50 |
51 |
52 |
55 |
56 |
57 |
59 |
63 |
64 |
65 |
66 |
68 |
69 |
70 |
72 |
73 |
74 |
76 |
77 |
78 |
79 |
80 |
81 |
83 |
84 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/util/ElasticSearchHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.util;
18 |
19 | import org.elasticsearch.action.ActionRequest;
20 | import org.elasticsearch.action.ActionResponse;
21 | import org.elasticsearch.client.Client;
22 | import org.elasticsearch.common.bytes.BytesArray;
23 | import org.elasticsearch.common.io.stream.BytesStreamOutput;
24 | import org.elasticsearch.common.xcontent.XContentBuilder;
25 | import org.elasticsearch.common.xcontent.XContentFactory;
26 |
27 | import java.io.IOException;
28 | import java.util.Map;
29 | import java.util.logging.Logger;
30 |
31 | public class ElasticSearchHelper {
32 |
33 | protected static final Logger LOGGER = Logger.getLogger(ElasticSearchHelper.class.getName());
34 |
35 | public static void checkIndex(Client esClient, String indexName) {
36 | if (!checkIndexExists(esClient, indexName)) {
37 | if (createIndex(esClient, indexName)) {
38 | LOGGER.info("Index [" + indexName + "] not found. Creating new index [" + indexName + "]");
39 | } else {
40 | throw new RuntimeException("Unable to create index [" + indexName + "] in Elasticsearch.");
41 | }
42 | } else {
43 | LOGGER.info("Index [" + indexName + "] already exists in Elasticsearch.");
44 | }
45 | }
46 |
47 | public static boolean createIndex(Client esClient, String indexName) {
48 | return esClient.admin().indices().prepareCreate(indexName).get().isAcknowledged();
49 | }
50 |
51 | public static void checkTypeAndMapping(Client esClient, String indexName, String typeName) {
52 | Map mapping =
53 | JsonHelper.readJsonFromClasspathAsMap("mapping/" + typeName + ".json");
54 | checkTypeAndMapping(esClient, indexName, typeName, mapping);
55 | }
56 |
57 | public static void checkTypeAndMapping(Client esClient, String indexName, String typeName, Map mapping) {
58 | if (!checkTypeExists(esClient, indexName, typeName)) {
59 | if (mapping == null) {
60 | throw new RuntimeException("No mapping provided.");
61 | }
62 |
63 | if (updateMappingForType(esClient, indexName, typeName, mapping)) {
64 | LOGGER.info("Created mapping for [" + indexName + "]/[" + typeName + "]");
65 | } else {
66 | throw new RuntimeException("Could not define mapping for ["+ indexName +"]/["+ typeName +"]");
67 | }
68 | }
69 | }
70 |
71 | public static boolean checkIndexExists(Client esClient, String indexName) {
72 | return esClient.admin().indices().prepareExists(indexName).get().isExists();
73 | }
74 |
75 | public static boolean checkTypeExists(Client esClient, String index, String type) {
76 | return esClient.admin().indices().prepareExists(index, type).get().isExists();
77 | }
78 |
79 | public static boolean updateMappingForType(Client esClient, String indexName, String typeName, Map mapping) {
80 | return esClient.admin().indices().preparePutMapping(indexName).setType(typeName).setSource(mapping).get().isAcknowledged();
81 | }
82 |
83 | public static String convertRequestToJson(ActionRequest request) throws IOException {
84 | BytesStreamOutput bytesStreamOutput = new BytesStreamOutput();
85 | request.writeTo(bytesStreamOutput);
86 |
87 | XContentBuilder builder = XContentFactory.jsonBuilder(bytesStreamOutput);
88 | builder.prettyPrint();
89 |
90 | // builder.startObject();
91 | // builder.endObject();
92 | BytesArray bytesArray = builder.bytes().toBytesArray();
93 | return new String(bytesArray.array(), bytesArray.arrayOffset(), bytesArray.length());
94 | }
95 |
96 | public static String convertResponseToJson(ActionResponse response) throws IOException {
97 | BytesStreamOutput bytesStreamOutput = new BytesStreamOutput();
98 | response.writeTo(bytesStreamOutput);
99 |
100 | XContentBuilder builder = XContentFactory.jsonBuilder(bytesStreamOutput);
101 | builder.prettyPrint();
102 |
103 | // builder.startObject();
104 | // builder.endObject();
105 | return builder.bytes().toUtf8();
106 | }
107 |
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/develop/java/org/camunda/bpm/extension/elasticsearch/cockpit/demodata/DemoDataGenerator.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.camunda.bpm.extension.elasticsearch.cockpit.demodata;
14 |
15 | import static org.camunda.bpm.engine.authorization.Authorization.ANY;
16 | import static org.camunda.bpm.engine.authorization.Authorization.AUTH_TYPE_GRANT;
17 | import static org.camunda.bpm.engine.authorization.Permissions.ALL;
18 |
19 | import java.util.List;
20 | import java.util.Random;
21 | import java.util.Timer;
22 | import java.util.TimerTask;
23 |
24 | import org.camunda.bpm.engine.AuthorizationService;
25 | import org.camunda.bpm.engine.IdentityService;
26 | import org.camunda.bpm.engine.ProcessEngine;
27 | import org.camunda.bpm.engine.authorization.Groups;
28 | import org.camunda.bpm.engine.authorization.Resource;
29 | import org.camunda.bpm.engine.authorization.Resources;
30 | import org.camunda.bpm.engine.identity.Group;
31 | import org.camunda.bpm.engine.identity.User;
32 | import org.camunda.bpm.engine.impl.persistence.entity.AuthorizationEntity;
33 | import org.camunda.bpm.engine.task.Task;
34 | import org.springframework.beans.factory.InitializingBean;
35 |
36 | /**
37 | * @author Daniel Meyer
38 | *
39 | */
40 | public class DemoDataGenerator implements InitializingBean {
41 |
42 | private static final int maxInstances = 10;
43 |
44 | protected ProcessEngine processEngine;
45 |
46 | public void afterPropertiesSet() throws Exception {
47 |
48 | System.out.println("Generating demo data");
49 |
50 | scheduleInstanceStart();
51 |
52 | // ensure admin user exists
53 | IdentityService identityService = processEngine.getIdentityService();
54 | User user = identityService.createUserQuery().userId("demo").singleResult();
55 | if(user == null) {
56 | User newUser = identityService.newUser("demo");
57 | newUser.setPassword("demo");
58 | identityService.saveUser(newUser);
59 | System.out.println("Created used 'demo', password 'demo'");
60 | AuthorizationService authorizationService = processEngine.getAuthorizationService();
61 |
62 | // create group
63 | if(identityService.createGroupQuery().groupId(Groups.CAMUNDA_ADMIN).count() == 0) {
64 | Group camundaAdminGroup = identityService.newGroup(Groups.CAMUNDA_ADMIN);
65 | camundaAdminGroup.setName("camunda BPM Administrators");
66 | camundaAdminGroup.setType(Groups.GROUP_TYPE_SYSTEM);
67 | identityService.saveGroup(camundaAdminGroup);
68 | }
69 |
70 | // create ADMIN authorizations on all built-in resources
71 | for (Resource resource : Resources.values()) {
72 | if(authorizationService.createAuthorizationQuery().groupIdIn(Groups.CAMUNDA_ADMIN).resourceType(resource).resourceId(ANY).count() == 0) {
73 | AuthorizationEntity userAdminAuth = new AuthorizationEntity(AUTH_TYPE_GRANT);
74 | userAdminAuth.setGroupId(Groups.CAMUNDA_ADMIN);
75 | userAdminAuth.setResource(resource);
76 | userAdminAuth.setResourceId(ANY);
77 | userAdminAuth.addPermission(ALL);
78 | authorizationService.saveAuthorization(userAdminAuth);
79 | }
80 | }
81 |
82 | processEngine.getIdentityService()
83 | .createMembership("demo", Groups.CAMUNDA_ADMIN);
84 | }
85 | }
86 |
87 |
88 |
89 | protected void scheduleInstanceStart() {
90 | new Timer(true).schedule(new TimerTask() {
91 | public void run() {
92 |
93 | // start some process instances
94 | int rand = new Random().nextInt(maxInstances);
95 | for (int i = 0; i < rand; i++) {
96 | processEngine.getRuntimeService().startProcessInstanceByKey("generatedFormsQuickstart");
97 | }
98 |
99 | // complete some process instances
100 | rand = new Random().nextInt(maxInstances);
101 | List tasks = processEngine.getTaskService().createTaskQuery()
102 | .listPage(0, rand);
103 | for (Task task : tasks) {
104 | processEngine.getTaskService().complete(task.getId());
105 | }
106 |
107 | // reschedule
108 | scheduleInstanceStart();
109 | }
110 | }, 1000);
111 | }
112 |
113 |
114 |
115 | public void setProcessEngine(ProcessEngine processEngine) {
116 | this.processEngine = processEngine;
117 | }
118 |
119 | public ProcessEngine getProcessEngine() {
120 | return processEngine;
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/elasticsearch-commons/src/main/java/org/camunda/bpm/elasticsearch/ElasticSearchHistoryPluginConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch;
18 |
19 | import org.camunda.bpm.elasticsearch.util.JsonHelper;
20 |
21 | import java.util.HashMap;
22 |
23 | public class ElasticSearchHistoryPluginConfiguration {
24 |
25 | public static final String ES_CAM_BPM_CONFIGURATION = "camunda-bpm-elasticsearch.json";
26 |
27 | public static final String ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM = "camundabpm";
28 | public static final String ES_DEFAULT_TYPE_NAME_CAMUNDA_BPM = "processinstance";
29 |
30 | public static final String ES_DEFAULT_HOST = "localhost";
31 | public static final String ES_DEFAULT_PORT_NODE_CLIENT = "9300";
32 | public static final String ES_DEFAULT_PORT_TRANSPORT_CLIENT = "9300";
33 | public static final String ES_DEFAULT_CLUSTER_NAME = "elasticsearch";
34 |
35 | private String index;
36 | private String type;
37 | private String indexingStrategy;
38 |
39 | private String esHost;
40 | private String esPort;
41 | private String esClusterName;
42 | private boolean transportClient = false;
43 | private HashMap properties = new HashMap();
44 | private String eventHandler;
45 |
46 | public String getIndex() {
47 | if (index == null) {
48 | index = ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM;
49 | }
50 | return index;
51 | }
52 |
53 | public void setIndex(String index) {
54 | this.index = index;
55 | }
56 |
57 | public String getType() {
58 | if (type == null) {
59 | type = ES_DEFAULT_TYPE_NAME_CAMUNDA_BPM;
60 | }
61 | return type;
62 | }
63 |
64 | public void setType(String type) {
65 | this.type = type;
66 | }
67 |
68 | public String getIndexingStrategy() {
69 | return indexingStrategy;
70 | }
71 |
72 | public void setIndexingStrategy(String indexingStrategy) {
73 | this.indexingStrategy = indexingStrategy;
74 | }
75 |
76 | public String getEsHost() {
77 | if (esHost == null) {
78 | esHost = ES_DEFAULT_HOST;
79 | }
80 | return esHost;
81 | }
82 |
83 | public void setEsHost(String esHost) {
84 | this.esHost = esHost;
85 | }
86 |
87 | public String getEsPort() {
88 | if (esPort == null) {
89 | if (isTransportClient()) {
90 | esPort = ES_DEFAULT_PORT_TRANSPORT_CLIENT;
91 | } else {
92 | esPort = ES_DEFAULT_PORT_NODE_CLIENT;
93 | }
94 | }
95 | return esPort;
96 | }
97 |
98 | public void setEsPort(String esPort) {
99 | this.esPort = esPort;
100 | }
101 |
102 | public String getEsClusterName() {
103 | if (esClusterName == null) {
104 | esClusterName = ES_DEFAULT_CLUSTER_NAME;
105 | }
106 | return esClusterName;
107 | }
108 |
109 | public void setEsClusterName(String esClusterName) {
110 | this.esClusterName = esClusterName;
111 | }
112 |
113 | public boolean isTransportClient() {
114 | return transportClient;
115 | }
116 |
117 | public void setTransportClient(boolean transportClient) {
118 | this.transportClient = transportClient;
119 | }
120 |
121 | public HashMap getProperties() {
122 | return properties;
123 | }
124 |
125 | public void setProperties(HashMap properties) {
126 | this.properties = properties;
127 | }
128 |
129 | public String getEventHandler() {
130 | return eventHandler;
131 | }
132 |
133 | public void setEventHandler(String eventHandler) {
134 | this.eventHandler = eventHandler;
135 | }
136 |
137 | public void validate() {
138 | StringBuilder sb = new StringBuilder();
139 |
140 | if (getEsClusterName() == null || getEsClusterName().isEmpty()) {
141 |
142 | }
143 | if (getEsHost() == null || getEsHost().isEmpty()) {
144 |
145 | }
146 | if (getEsPort() == null || getEsPort().isEmpty()) {
147 |
148 | }
149 | if (getIndex() == null || getIndex().isEmpty()) {
150 |
151 | }
152 | if (getType() == null || getType().isEmpty()) {
153 |
154 | }
155 | if (getIndexingStrategy() == null || getIndexingStrategy().isEmpty()) {
156 |
157 | }
158 | if (getEventHandler() == null || getEventHandler().isEmpty()) {
159 |
160 | }
161 | }
162 |
163 | public static ElasticSearchHistoryPluginConfiguration readConfigurationFromClasspath() {
164 | return readConfigurationFromClasspath(ES_CAM_BPM_CONFIGURATION);
165 | }
166 |
167 | public static ElasticSearchHistoryPluginConfiguration readConfigurationFromClasspath(String fileName) {
168 | return JsonHelper.readJsonFromClasspath(ElasticSearchHistoryPluginConfiguration.class, fileName);
169 | }
170 |
171 | }
172 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/java/org/camunda/bpm/cockpit/plugin/dashboards/resources/SearchResource.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.camunda.bpm.cockpit.plugin.dashboards.resources;
14 | import static org.camunda.bpm.elasticsearch.ElasticSearchHistoryPluginConfiguration.ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM;
15 |
16 | import java.util.ArrayList;
17 | import java.util.HashSet;
18 | import java.util.List;
19 | import java.util.Set;
20 | import java.util.logging.Logger;
21 |
22 | import javax.ws.rs.GET;
23 | import javax.ws.rs.QueryParam;
24 |
25 | import org.camunda.bpm.cockpit.plugin.ElasticSearchClientProvider;
26 | import org.camunda.bpm.cockpit.plugin.resource.AbstractCockpitPluginResource;
27 | import org.camunda.bpm.engine.repository.ProcessDefinition;
28 | import org.elasticsearch.action.search.SearchRequestBuilder;
29 | import org.elasticsearch.action.search.SearchResponse;
30 | import org.elasticsearch.client.Client;
31 | import org.elasticsearch.index.query.FilterBuilder;
32 | import org.elasticsearch.index.query.FilterBuilders;
33 | import org.elasticsearch.index.query.FilteredQueryBuilder;
34 | import org.elasticsearch.index.query.QueryBuilders;
35 | import org.elasticsearch.search.SearchHit;
36 | import org.elasticsearch.search.SearchHits;
37 |
38 | /**
39 | * @author Daniel Meyer
40 | *
41 | */
42 | public class SearchResource extends AbstractCockpitPluginResource {
43 |
44 | private final static Logger LOG = Logger.getLogger(SearchResource.class.getName());
45 |
46 | public static final String PATH = "/search";
47 |
48 | public SearchResource(String engineName) {
49 | super(engineName);
50 | }
51 |
52 | @GET
53 | public List doSearch(
54 | @QueryParam("query") String query,
55 | @QueryParam("includeVariables") boolean includeVariables,
56 | @QueryParam("includeActivities") boolean includeActivities,
57 | @QueryParam("includeTasks") boolean includeTasks
58 | ) {
59 |
60 | Client client = ElasticSearchClientProvider.getClient(getProcessEngine());
61 |
62 | List filters = new ArrayList();
63 | if(includeVariables) {
64 | filters.add(FilterBuilders.nestedFilter("variables", QueryBuilders.queryString(query).field("variables.*").lenient(true).useDisMax(true)));
65 | }
66 | if(includeActivities) {
67 | filters.add(FilterBuilders.nestedFilter("activities", QueryBuilders.queryString(query).field("activities.*").lenient(true).useDisMax(true)));
68 | }
69 | if(includeTasks) {
70 | filters.add(FilterBuilders.nestedFilter("tasks", QueryBuilders.queryString(query).field("tasks.*").lenient(true).useDisMax(true)));
71 | }
72 |
73 | FilteredQueryBuilder elasticSearchQuery = QueryBuilders.filteredQuery(
74 | QueryBuilders.matchAllQuery(),
75 | FilterBuilders.boolFilter().should(
76 | filters.toArray(new FilterBuilder[0])
77 | )
78 | );
79 |
80 | SearchRequestBuilder searchRequestBuilder = client.prepareSearch(ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM)
81 | .setQuery(elasticSearchQuery)
82 | .setFetchSource(false)
83 | .addField("processDefinitionId")
84 | .setSize(20);
85 |
86 | System.out.println(searchRequestBuilder);
87 |
88 | SearchResponse searchResponse = searchRequestBuilder.get();
89 | SearchHits hits = searchResponse.getHits();
90 |
91 | List searchResults = new ArrayList();
92 | Set processDefinitionIds = new HashSet();
93 | for (SearchHit searchHit : hits.getHits()) {
94 | SearchResult searchResult = new SearchResult();
95 | searchResult.setProcessInstanceId(searchHit.getId());
96 | searchResult.setProcessDefinitionId((String) searchHit.getFields().get("processDefinitionId").getValue());
97 | searchResults.add(searchResult);
98 | processDefinitionIds.add(searchResult.getProcessDefinitionId());
99 | }
100 |
101 | // get process definition names
102 | List processDefinitions = getProcessEngine().getRepositoryService()
103 | .createProcessDefinitionQuery()
104 | .processDefinitionIdIn(processDefinitionIds.toArray(new String[0]))
105 | .list();
106 | for (ProcessDefinition processDefinition : processDefinitions) {
107 | for (SearchResult searchResult : searchResults) {
108 | if(searchResult.getProcessDefinitionId().equals(processDefinition.getId())) {
109 | if(processDefinition.getName() == null || processDefinition.getName().isEmpty()) {
110 | searchResult.setProcessDefinitionName(processDefinition.getKey());
111 | } else {
112 | searchResult.setProcessDefinitionName(processDefinition.getName());
113 | }
114 | }
115 | }
116 | }
117 | return searchResults;
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/entity/ElasticSearchProcessInstanceHistoryEntity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.entity;
18 |
19 | import org.camunda.bpm.engine.impl.history.event.HistoricActivityInstanceEventEntity;
20 | import org.camunda.bpm.engine.impl.history.event.HistoricProcessInstanceEventEntity;
21 | import org.camunda.bpm.engine.impl.history.event.HistoricTaskInstanceEventEntity;
22 | import org.camunda.bpm.engine.impl.history.event.HistoricVariableUpdateEventEntity;
23 | import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
24 |
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | /**
29 | *
30 | */
31 | public class ElasticSearchProcessInstanceHistoryEntity extends HistoricProcessInstanceEventEntity {
32 |
33 | private List activities = null;
34 | private List tasks = null;
35 | private List variables = null;
36 |
37 | public ElasticSearchProcessInstanceHistoryEntity() {
38 | }
39 |
40 | public static ElasticSearchProcessInstanceHistoryEntity createFromHistoryEvent(HistoryEvent historyEvent) {
41 | ElasticSearchProcessInstanceHistoryEntity esHistoryEntity = new ElasticSearchProcessInstanceHistoryEntity();
42 |
43 | esHistoryEntity.setId(historyEvent.getId()); // maybe use process instance id here?
44 | esHistoryEntity.setProcessInstanceId(historyEvent.getProcessInstanceId());
45 |
46 | if (historyEvent instanceof HistoricProcessInstanceEventEntity) {
47 | HistoricProcessInstanceEventEntity pie = (HistoricProcessInstanceEventEntity) historyEvent;
48 |
49 | esHistoryEntity.setExecutionId(pie.getExecutionId());
50 | esHistoryEntity.setProcessDefinitionId(pie.getProcessDefinitionId());
51 |
52 | esHistoryEntity.setStartActivityId(pie.getStartActivityId());
53 | esHistoryEntity.setEndActivityId(pie.getEndActivityId());
54 | esHistoryEntity.setStartTime(pie.getStartTime());
55 | esHistoryEntity.setEndTime(pie.getEndTime());
56 | esHistoryEntity.setDurationInMillis(pie.getDurationInMillis());
57 |
58 | esHistoryEntity.setBusinessKey(pie.getBusinessKey());
59 | esHistoryEntity.setStartUserId(pie.getStartUserId());
60 | esHistoryEntity.setDeleteReason(pie.getDeleteReason());
61 | esHistoryEntity.setSuperProcessInstanceId(pie.getSuperProcessInstanceId());
62 |
63 | } else if (historyEvent instanceof HistoricActivityInstanceEventEntity) {
64 |
65 | HistoricActivityInstanceEventEntity aie = (HistoricActivityInstanceEventEntity) historyEvent;
66 | esHistoryEntity.addHistoricActivityInstanceEvent(aie);
67 |
68 | } else if (historyEvent instanceof HistoricTaskInstanceEventEntity) {
69 |
70 | HistoricTaskInstanceEventEntity tie = (HistoricTaskInstanceEventEntity) historyEvent;
71 | esHistoryEntity.addHistoricTaskInstanceEvent(tie);
72 |
73 | } else if (historyEvent instanceof HistoricVariableUpdateEventEntity) {
74 |
75 | HistoricVariableUpdateEventEntity vue = (HistoricVariableUpdateEventEntity) historyEvent;
76 | esHistoryEntity.addHistoricVariableUpdateEvent(vue);
77 |
78 | } else {
79 | // unknown event - throw exception or return null?
80 | }
81 |
82 | return esHistoryEntity;
83 | }
84 |
85 | public List getVariables() {
86 | return variables;
87 | }
88 |
89 | public void setVariables(List variables) {
90 | this.variables = variables;
91 | }
92 |
93 | public void addHistoricVariableUpdateEvent(HistoricVariableUpdateEventEntity variableUpdateEvent) {
94 | if (variables == null) {
95 | variables = new ArrayList();
96 | }
97 | variables.add(variableUpdateEvent);
98 | }
99 |
100 | public List getActivities() {
101 | return activities;
102 | }
103 |
104 | public void setActivities(List activities) {
105 | this.activities = activities;
106 | }
107 |
108 | public void addHistoricActivityInstanceEvent(HistoricActivityInstanceEventEntity activityInstanceEvent) {
109 | if (activities == null) {
110 | activities = new ArrayList();
111 | }
112 | activities.add(activityInstanceEvent);
113 | }
114 |
115 | public List getTasks() {
116 | return tasks;
117 | }
118 |
119 | public void setTasks(List tasks) {
120 | this.tasks = tasks;
121 | }
122 |
123 | public void addHistoricTaskInstanceEvent(HistoricTaskInstanceEventEntity taskInstanceEvent) {
124 | if (tasks == null) {
125 | tasks = new ArrayList();
126 | }
127 | tasks.add(taskInstanceEvent);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/java/org/camunda/bpm/cockpit/plugin/dashboards/resources/ProcessInstanceHistogramResource.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.camunda.bpm.cockpit.plugin.dashboards.resources;
14 |
15 | import org.camunda.bpm.cockpit.plugin.ElasticSearchClientProvider;
16 | import org.camunda.bpm.cockpit.plugin.resource.AbstractCockpitPluginResource;
17 | import org.elasticsearch.action.search.SearchRequestBuilder;
18 | import org.elasticsearch.action.search.SearchResponse;
19 | import org.elasticsearch.action.search.SearchType;
20 | import org.elasticsearch.client.Client;
21 | import org.elasticsearch.index.query.*;
22 | import org.elasticsearch.search.aggregations.AggregationBuilders;
23 | import org.elasticsearch.search.aggregations.bucket.filter.Filter;
24 | import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
25 | import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogram;
26 | import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramBuilder;
27 |
28 | import javax.ws.rs.GET;
29 | import javax.ws.rs.QueryParam;
30 | import java.util.*;
31 | import java.util.logging.Logger;
32 |
33 | import static org.camunda.bpm.elasticsearch.ElasticSearchHistoryPluginConfiguration.ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM;
34 |
35 | /**
36 | * @author Christian Lipphardt
37 | */
38 | public class ProcessInstanceHistogramResource extends AbstractCockpitPluginResource {
39 |
40 | private final static Logger LOG = Logger.getLogger(ProcessInstanceHistogramResource.class.getName());
41 |
42 | public static final String PATH = "/histogram/processinstance";
43 |
44 | public ProcessInstanceHistogramResource(String engineName) {
45 | super(engineName);
46 | }
47 |
48 | @GET
49 | public AggregationsResult getDateHistogramAggregrations(
50 | @QueryParam("interval") String interval,
51 | @QueryParam("timeframe") String timeframe
52 | ) {
53 |
54 | Client client = ElasticSearchClientProvider.getClient(getProcessEngine());
55 |
56 | DateHistogram.Interval dateInterval = null;
57 | switch (interval) {
58 | case "s":
59 | case "m":
60 | case "h":
61 | case "d":
62 | case "w":
63 | case "M":
64 | case "q":
65 | case "y":
66 | default:
67 | dateInterval = DateHistogram.Interval.SECOND;
68 | break;
69 | }
70 |
71 | // create buckets based on startTime
72 | DateHistogramBuilder histogramStartTime = AggregationBuilders.dateHistogram("dateHistogram")
73 | .minDocCount(0)
74 | .interval(dateInterval)
75 | .field("startTime");
76 | // only get the running process instances
77 | FilterAggregationBuilder runningPIsAgg = AggregationBuilders.filter("running")
78 | .filter(FilterBuilders.missingFilter("endTime"));
79 | runningPIsAgg.subAggregation(histogramStartTime);
80 |
81 | // create buckets based on endTime
82 | DateHistogramBuilder histogramEndTime = AggregationBuilders.dateHistogram("dateHistogram")
83 | .minDocCount(0)
84 | .interval(dateInterval)
85 | .field("endTime");
86 | // only get the ended process instances
87 | FilterAggregationBuilder endedPIsAgg = AggregationBuilders.filter("ended")
88 | .filter(FilterBuilders.existsFilter("endTime"));
89 | endedPIsAgg.subAggregation(histogramEndTime);
90 |
91 |
92 | SearchRequestBuilder searchRequestBuilder = client.prepareSearch(ES_DEFAULT_INDEX_NAME_CAMUNDA_BPM)
93 | .setQuery(QueryBuilders.matchAllQuery())
94 | .addAggregation(runningPIsAgg)
95 | .addAggregation(endedPIsAgg)
96 | .setSearchType(SearchType.COUNT);
97 |
98 | System.out.println(searchRequestBuilder);
99 |
100 | SearchResponse searchResponse = searchRequestBuilder.get();
101 |
102 | long totalHits = searchResponse.getHits().getTotalHits();
103 |
104 | Filter running = searchResponse.getAggregations().get("running");
105 | // long runningTotal = running.getDocCount();
106 |
107 | DateHistogram runningDateHistogram = running.getAggregations().get("dateHistogram");
108 | List runningDateHistogramBuckets = parseDateHistogramAggregation(runningDateHistogram);
109 |
110 |
111 | Filter ended = searchResponse.getAggregations().get("ended");
112 | // long endedTotal = ended.getDocCount();
113 |
114 | DateHistogram endedDateHistogram = ended.getAggregations().get("dateHistogram");
115 | List endedDateHistogramBuckets = parseDateHistogramAggregation(endedDateHistogram);
116 |
117 | HashMap> dateHistogramBucketPairs = new HashMap<>();
118 | dateHistogramBucketPairs.put("running", runningDateHistogramBuckets);
119 | dateHistogramBucketPairs.put("ended", endedDateHistogramBuckets);
120 |
121 | AggregationsResult aggregationsResult = new AggregationsResult();
122 | aggregationsResult.setDateHistogramBuckets(dateHistogramBucketPairs);
123 | aggregationsResult.setTotalHits(totalHits);
124 |
125 | return aggregationsResult;
126 | }
127 |
128 | protected List parseDateHistogramAggregation(DateHistogram dateHistogram) {
129 | ArrayList dateHistogramBuckets = new ArrayList<>();
130 |
131 | for (DateHistogram.Bucket bucket : dateHistogram.getBuckets()) {
132 | dateHistogramBuckets.add(new DateHistogramBucketPair(bucket.getKeyAsNumber(), bucket.getDocCount()));
133 | }
134 |
135 | return dateHistogramBuckets;
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 | 4.0.0
22 |
23 |
24 | org.camunda.bpm.extension.elasticsearch
25 | camunda-bpm-elasticsearch
26 | 1.0.0-SNAPSHOT
27 |
28 |
29 | elasticsearch-cockpit-plugin
30 | camunda bpm - elasticsearch - cockpit plugin
31 |
32 |
33 |
34 | org.camunda.bpm.extension.elasticsearch
35 | elasticsearch-engine-integration
36 | ${project.version}
37 |
38 |
39 |
40 | org.camunda.bpm.webapp
41 | camunda-webapp-core
42 | ${version.camunda-bpm}
43 | provided
44 |
45 |
46 |
47 | org.jboss.spec
48 | jboss-javaee-6.0
49 | pom
50 | provided
51 | 3.0.2.Final
52 |
53 |
54 |
55 |
56 | org.camunda.bpm
57 | camunda-engine
58 | provided
59 |
60 |
61 | com.h2database
62 | h2
63 | test
64 |
65 |
66 | junit
67 | junit
68 | test
69 |
70 |
71 |
72 |
73 |
74 |
75 | src/main/resources
76 | true
77 |
78 |
79 |
80 |
81 |
82 |
83 | develop
84 |
85 |
86 | org.camunda.bpm.webapp
87 | camunda-webapp-tomcat-standalone
88 | ${version.camunda-bpm}
89 | war
90 |
91 |
92 |
93 |
94 |
95 | org.mortbay.jetty
96 | jetty-maven-plugin
97 | 8.1.14.v20131031
98 |
99 |
100 | /camunda
101 |
102 | ${project.basedir}/src/main/resources
103 | ${project.basedir}/src/develop/resources
104 |
105 |
106 |
107 |
108 |
109 | org.codehaus.mojo
110 | build-helper-maven-plugin
111 | 1.9
112 |
113 |
114 | add-source
115 | generate-sources
116 |
117 | add-source
118 |
119 |
120 |
121 | src/develop/java
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | develop-ee
132 |
133 |
134 | org.camunda.bpm.webapp
135 | camunda-webapp-ee-tomcat-standalone
136 | ${version.camunda-bpm}
137 | war
138 |
139 |
140 |
141 |
142 |
143 | org.mortbay.jetty
144 | jetty-maven-plugin
145 | 8.1.14.v20131031
146 |
147 |
148 | /camunda
149 |
150 | ${project.basedir}/src/main/resources
151 | ${project.basedir}/src/develop/resources
152 |
153 |
154 |
155 |
156 |
157 | org.codehaus.mojo
158 | build-helper-maven-plugin
159 | 1.9
160 |
161 |
162 | add-source
163 | generate-sources
164 |
165 | add-source
166 |
167 |
168 |
169 | src/develop/java
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/java/org/camunda/bpm/elasticsearch/TestDataGenerator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch;
18 |
19 | import org.camunda.bpm.engine.ProcessEngine;
20 | import org.camunda.bpm.engine.RepositoryService;
21 | import org.camunda.bpm.engine.RuntimeService;
22 | import org.camunda.bpm.engine.TaskService;
23 | import org.camunda.bpm.engine.impl.test.ProcessEngineAssert;
24 | import org.camunda.bpm.engine.impl.util.ClockUtil;
25 | import org.camunda.bpm.engine.repository.Deployment;
26 | import org.camunda.bpm.engine.runtime.ProcessInstance;
27 | import org.camunda.bpm.engine.runtime.VariableInstance;
28 | import org.camunda.bpm.engine.task.Task;
29 | import org.elasticsearch.common.base.Charsets;
30 | import org.junit.Assert;
31 |
32 | import java.io.BufferedReader;
33 | import java.io.IOException;
34 | import java.io.InputStreamReader;
35 | import java.net.URL;
36 | import java.util.Date;
37 | import java.util.HashMap;
38 | import java.util.List;
39 | import java.util.Random;
40 | import java.util.logging.Logger;
41 |
42 | import static org.junit.Assert.assertEquals;
43 |
44 | public class TestDataGenerator {
45 |
46 | private static final Logger LOGGER = Logger.getLogger(TestDataGenerator.class.getName());
47 | private static final Random RANDOM = new Random();
48 |
49 | public static String getRandomString() {
50 | URL names = TestDataGenerator.class.getClassLoader().getResource("data/names.txt");
51 | return randomString(names);
52 | }
53 |
54 | public static Double getRandomDouble() {
55 | return RANDOM.nextDouble() * 1000;
56 | }
57 |
58 | public static Long getRandomLong() {
59 | return RANDOM.nextLong();
60 | }
61 |
62 | public static String randomString(URL nodeNames) {
63 | BufferedReader reader = null;
64 | try {
65 | reader = new BufferedReader(new InputStreamReader(nodeNames.openStream(), Charsets.UTF_8));
66 | int numberOfNames = 0;
67 | while (reader.readLine() != null) {
68 | numberOfNames++;
69 | }
70 | reader.close();
71 | reader = new BufferedReader(new InputStreamReader(nodeNames.openStream(), Charsets.UTF_8));
72 | int number = ((new Random().nextInt(numberOfNames)) % numberOfNames);
73 | for (int i = 0; i < number; i++) {
74 | reader.readLine();
75 | }
76 | return reader.readLine();
77 | } catch (IOException e) {
78 | return null;
79 | } finally {
80 | try {
81 | if (reader != null) {
82 | reader.close();
83 | }
84 | } catch (IOException e) {
85 | // ignore this exception
86 | }
87 | }
88 | }
89 |
90 | public static HashMap startInvoiceProcess(ProcessEngine processEngine, final int numberOfInstances) {
91 | return startInvoiceProcess(processEngine, numberOfInstances, false);
92 | }
93 |
94 | public static HashMap startInvoiceProcess(ProcessEngine processEngine, final int numberOfInstances, boolean addRandomTimeInterval) {
95 | RepositoryService repositoryService = processEngine.getRepositoryService();
96 | RuntimeService runtimeService = processEngine.getRuntimeService();
97 | TaskService taskService = processEngine.getTaskService();
98 |
99 | Deployment deployment = repositoryService.createDeployment().addClasspathResource("invoice.bpmn").deploy();
100 | Assert.assertNotNull(repositoryService.createDeploymentQuery().deploymentId(deployment.getId()).singleResult());
101 |
102 | LOGGER.info("Creating " + numberOfInstances + " instances of 'invoice.bpmn' process.");
103 |
104 |
105 | HashMap variablesByProcessIds = new HashMap(numberOfInstances);
106 |
107 | for (int i = 0; i < numberOfInstances; i++) {
108 | if (addRandomTimeInterval) {
109 | ClockUtil.setCurrentTime(new Date(ClockUtil.getCurrentTime().getTime() + getRandomLong()));
110 | }
111 |
112 | HashMap variables = new HashMap();
113 | variables.put(TestDataGenerator.getRandomString(), TestDataGenerator.getRandomString());
114 | variables.put("long", TestDataGenerator.getRandomLong());
115 | variables.put("double", TestDataGenerator.getRandomDouble());
116 |
117 | ProcessInstance pi = runtimeService.startProcessInstanceByKey("invoice", variables);
118 | Assert.assertNotNull(pi);
119 |
120 | List tasks = taskService.createTaskQuery().processInstanceId(pi.getId()).list();
121 |
122 | assertEquals(1, tasks.size());
123 | assertEquals("assignApprover", tasks.get(0).getTaskDefinitionKey());
124 |
125 | variables.clear();
126 | String approver = TestDataGenerator.getRandomString();
127 | variables.put("approver", approver);
128 | taskService.complete(tasks.get(0).getId(), variables);
129 |
130 | tasks = taskService.createTaskQuery().processInstanceId(pi.getId()).list();
131 |
132 | assertEquals(1, tasks.size());
133 | assertEquals("approveInvoice", tasks.get(0).getTaskDefinitionKey());
134 | assertEquals(approver, tasks.get(0).getAssignee());
135 |
136 | variables.clear();
137 | variables.put("approved", Boolean.TRUE);
138 | taskService.complete(tasks.get(0).getId(), variables);
139 |
140 | tasks = taskService.createTaskQuery().processInstanceId(pi.getId()).list();
141 |
142 | // retrieve all variables
143 | List variableInstances = runtimeService.createVariableInstanceQuery().processInstanceIdIn(pi.getProcessInstanceId()).list();
144 | variablesByProcessIds.put(pi.getProcessInstanceId(), new ProcessDataContainer(pi.getProcessInstanceId(), pi.getBusinessKey(), variableInstances));
145 |
146 | assertEquals(1, tasks.size());
147 | assertEquals("prepareBankTransfer", tasks.get(0).getTaskDefinitionKey());
148 | taskService.complete(tasks.get(0).getId());
149 |
150 | ProcessEngineAssert.assertProcessEnded(processEngine, pi.getId());
151 | }
152 |
153 | LOGGER.info("Created " + numberOfInstances + " instances of 'invoice.bpmn' process.");
154 |
155 | return variablesByProcessIds;
156 | }
157 |
158 | private TestDataGenerator() {}
159 | }
160 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 | 4.0.0
22 |
23 |
24 | org.camunda.bpm.extension.elasticsearch
25 | camunda-bpm-elasticsearch
26 | 1.0.0-SNAPSHOT
27 |
28 |
29 | elasticsearch-engine-integration
30 | camunda bpm - elasticsearch - integration
31 |
32 |
33 | ${project.build.directory}/es/log
34 | ${project.build.directory}/es/data
35 | ${project.build.directory}/es/work
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | com.github.tlrx
55 | elasticsearch-test
56 | test
57 |
58 |
59 |
60 | org.camunda.bpm.extension.elasticsearch
61 | elasticsearch-commons
62 | ${project.version}
63 |
64 |
65 |
66 | org.camunda.bpm
67 | camunda-engine
68 | provided
69 |
70 |
71 |
72 | com.fasterxml.uuid
73 | java-uuid-generator
74 | provided
75 |
76 |
77 |
78 | org.camunda.bpm.jboss
79 | camunda-jboss-subsystem
80 | provided
81 |
82 |
83 |
84 | org.codehaus.groovy
85 | groovy-all
86 | 2.3.2
87 | test
88 |
89 |
90 |
91 |
92 | com.h2database
93 | h2
94 | test
95 |
96 |
97 |
98 | junit
99 | junit
100 | test
101 |
102 |
103 |
104 | org.hamcrest
105 | hamcrest-all
106 | test
107 |
108 |
109 |
110 | org.apache.activemq
111 | activemq-all
112 | provided
113 |
114 |
115 |
116 |
117 | org.springframework.integration
118 | spring-integration-jms
119 | provided
120 |
121 |
122 |
123 |
124 |
125 |
126 | src/test/resources
127 | true
128 |
129 |
130 |
131 |
132 |
133 | org.apache.maven.plugins
134 | maven-surefire-plugin
135 |
136 | true
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 | remote-es
145 |
146 | ${project.basedir}/elasticsearch/logs
147 | ${project.basedir}/elasticsearch/data
148 | ${project.basedir}/elasticsearch/work
149 |
150 |
151 |
152 |
153 | org.apache.maven.plugins
154 | maven-surefire-plugin
155 |
156 |
157 | **/cfg/**
158 |
159 |
160 |
161 |
162 | maven-antrun-plugin
163 | 1.4
164 |
165 |
166 | process-test-classes
167 |
168 | run
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | org.camunda
9 | camunda-release-parent
10 | 2.5
11 |
12 |
13 |
14 | org.camunda.bpm.extension.elasticsearch
15 | camunda-bpm-elasticsearch
16 | 1.0.0-SNAPSHOT
17 | pom
18 |
19 | camunda bpm - elasticsearch
20 |
21 |
22 | 1.4.4
23 | 1.2.1
24 |
25 | 4.9.0
26 | 2.4.1
27 | 17.0
28 |
29 | 7.2.0
30 | 5.9.0
31 |
32 | 4.11
33 |
34 | 3.1.2.RELEASE
35 | 2.2.5.RELEASE
36 |
37 | 1.7
38 | UTF-8
39 |
40 |
41 |
42 | elasticsearch-cockpit-plugin
43 | elasticsearch-engine-integration
44 | elasticsearch-jboss-module
45 | elasticsearch-lib-module
46 | elasticsearch-commons
47 |
48 |
49 |
50 |
51 |
52 | com.github.tlrx
53 | elasticsearch-test
54 | ${version.elasticsearch-test}
55 | test
56 |
57 |
58 |
59 | org.elasticsearch
60 | elasticsearch
61 | ${version.elasticsearch}
62 |
63 |
64 |
65 | org.hamcrest
66 | hamcrest-all
67 | 1.3
68 | test
69 |
70 |
71 |
72 | org.elasticsearch
73 | elasticsearch
74 | ${version.elasticsearch}
75 | test-jar
76 | test
77 |
78 |
79 |
80 | org.camunda.bpm
81 | camunda-engine
82 | ${version.camunda-bpm}
83 |
84 |
85 | com.fasterxml.uuid
86 | java-uuid-generator
87 | 3.1.2
88 |
89 |
90 | org.camunda.bpm.jboss
91 | camunda-jboss-subsystem
92 | ${version.camunda-bpm}
93 |
94 |
95 | com.h2database
96 | h2
97 | 1.3.168
98 |
99 |
100 |
101 |
102 | com.fasterxml.jackson.core
103 | jackson-core
104 | ${version.jackson}
105 |
106 |
107 | com.fasterxml.jackson.core
108 | jackson-databind
109 | ${version.jackson}
110 |
111 |
112 |
113 | junit
114 | junit
115 | ${version.junit}
116 | test
117 |
118 |
119 |
120 |
121 | org.apache.activemq
122 | activemq-all
123 | ${version.activemq}
124 |
125 |
126 |
127 |
128 | org.springframework.integration
129 | spring-integration-jms
130 | ${version.spring-it}
131 |
132 |
133 |
134 |
135 | org.springframework
136 | spring-core
137 | ${version.spring}
138 |
139 |
140 | org.springframework
141 | spring-context
142 | ${version.spring}
143 |
144 |
145 | org.springframework
146 | spring-context-support
147 | ${version.spring}
148 |
149 |
150 | org.springframework
151 | spring-beans
152 | ${version.spring}
153 |
154 |
155 | org.springframework
156 | spring-aspects
157 | ${version.spring}
158 |
159 |
160 | org.springframework
161 | spring-test
162 | ${version.spring}
163 | test
164 |
165 |
166 |
167 | com.google.guava
168 | guava
169 | ${version.guava}
170 |
171 |
172 |
173 |
174 |
175 |
176 | The Apache Software License, Version 2.0
177 | http://www.apache.org/licenses/LICENSE-2.0.txt
178 |
179 |
180 |
181 |
182 |
183 | Christian Lipphardt
184 | christian.lipphardt@camunda.com
185 |
186 |
187 |
188 |
189 |
190 | camunda-bpm-nexus
191 | https://app.camunda.com/nexus/content/groups/public/
192 |
193 | true
194 |
195 |
196 | true
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 | org.apache.maven.plugins
205 | maven-compiler-plugin
206 | 3.1
207 |
208 | ${version.java}
209 | ${version.java}
210 |
211 |
212 |
213 |
214 |
215 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/test/java/org/camunda/bpm/elasticsearch/AbstractElasticSearchTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch;
18 |
19 | import com.fasterxml.jackson.databind.ObjectMapper;
20 | import com.github.tlrx.elasticsearch.test.annotations.ElasticsearchAdminClient;
21 | import com.github.tlrx.elasticsearch.test.annotations.ElasticsearchClient;
22 | import com.github.tlrx.elasticsearch.test.annotations.ElasticsearchNode;
23 | import com.github.tlrx.elasticsearch.test.support.junit.runners.ElasticsearchRunner;
24 | import org.camunda.bpm.elasticsearch.util.ElasticSearchHelper;
25 | import org.camunda.bpm.engine.impl.ProcessEngineImpl;
26 | import org.camunda.bpm.engine.impl.cfg.ProcessEnginePlugin;
27 | import org.camunda.bpm.engine.test.ProcessEngineRule;
28 | import org.elasticsearch.action.ShardOperationFailedException;
29 | import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
30 | import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
31 | import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
32 | import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
33 | import org.elasticsearch.action.admin.indices.flush.FlushResponse;
34 | import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
35 | import org.elasticsearch.client.AdminClient;
36 | import org.elasticsearch.client.Client;
37 | import org.elasticsearch.client.Requests;
38 | import org.elasticsearch.cluster.metadata.IndexMetaData;
39 | import org.elasticsearch.cluster.metadata.MappingMetaData;
40 | import org.elasticsearch.common.collect.ImmutableOpenMap;
41 | import org.elasticsearch.common.hppc.cursors.ObjectCursor;
42 | import org.elasticsearch.node.Node;
43 | import org.elasticsearch.rest.RestStatus;
44 | import org.junit.Before;
45 | import org.junit.Rule;
46 | import org.junit.runner.RunWith;
47 |
48 | import java.io.IOException;
49 | import java.util.List;
50 | import java.util.Map;
51 | import java.util.logging.Logger;
52 |
53 | import static org.elasticsearch.client.Requests.clusterStateRequest;
54 | import static org.hamcrest.core.IsEqual.equalTo;
55 | import static org.junit.Assert.assertThat;
56 |
57 | @RunWith(ElasticsearchRunner.class)
58 | public abstract class AbstractElasticSearchTest {
59 |
60 | protected Logger logger = Logger.getLogger(AbstractElasticSearchTest.class.getName());
61 |
62 | protected ObjectMapper mapper = new ObjectMapper();
63 |
64 | @ElasticsearchNode(configFile = "config/elasticsearch-test.yml")
65 | protected Node elasticsearchNode;
66 | @ElasticsearchClient
67 | protected Client client;
68 | @ElasticsearchAdminClient
69 | protected AdminClient adminClient;
70 |
71 | @Rule
72 | public ProcessEngineRule processEngineRule = new ProcessEngineRule();
73 |
74 | @Before
75 | public void initialize() {
76 | ElasticSearchHistoryPluginConfiguration historyPluginConfiguration = ElasticSearchHistoryPluginConfiguration.readConfigurationFromClasspath();
77 | ElasticSearchHelper.checkIndex(client, historyPluginConfiguration.getIndex());
78 | ElasticSearchHelper.checkTypeAndMapping(client, historyPluginConfiguration.getIndex(), historyPluginConfiguration.getType());
79 |
80 | List processEnginePlugins = ((ProcessEngineImpl) processEngineRule.getProcessEngine())
81 | .getProcessEngineConfiguration()
82 | .getProcessEnginePlugins();
83 | for (ProcessEnginePlugin processEnginePlugin : processEnginePlugins) {
84 | if (processEnginePlugin instanceof ElasticSearchHistoryPlugin) {
85 | ElasticSearchHistoryPlugin plugin = (ElasticSearchHistoryPlugin) processEnginePlugin;
86 | plugin.setElasticSearchClient(client);
87 | }
88 | }
89 | }
90 |
91 | protected void showMappings(String... indices) throws IOException {
92 | ClusterStateResponse clusterStateResponse = adminClient.cluster()
93 | .state(clusterStateRequest()
94 | .blocks(true)
95 | .nodes(true)
96 | .indices(indices))
97 | .actionGet();
98 |
99 | for (IndexMetaData indexMetaData : clusterStateResponse.getState().getMetaData()) {
100 | printMapping(indexMetaData.getMappings());
101 | }
102 | }
103 |
104 | protected void printMapping(ImmutableOpenMap mappedMetaData) {
105 | for (ObjectCursor metaDataEntry : mappedMetaData.values()) {
106 | try {
107 | Map sourceAsMap = metaDataEntry.value.getSourceAsMap();
108 | logger.info(mapper.writeValueAsString(sourceAsMap));
109 | } catch (IOException e) {
110 | // nop
111 | }
112 | }
113 | }
114 |
115 | /**
116 | * Waits for relocations and refreshes all indices in the cluster.
117 | *
118 | * @see #waitForRelocation()
119 | */
120 | protected final RefreshResponse refresh() {
121 | waitForRelocation();
122 | // TODO RANDOMIZE with flush?
123 | RefreshResponse actionGet = adminClient.indices().prepareRefresh().execute().actionGet();
124 | // assertNoFailures(actionGet);
125 | return actionGet;
126 | }
127 |
128 | /**
129 | * Flushes and refreshes all indices in the cluster
130 | */
131 | protected final void flushAndRefresh() {
132 | flush(true);
133 | refresh();
134 | }
135 |
136 | /**
137 | * Flushes all indices in the cluster
138 | */
139 | protected final FlushResponse flush() {
140 | return flush(true);
141 | }
142 |
143 | private FlushResponse flush(boolean ignoreNotAllowed) {
144 | waitForRelocation();
145 | FlushResponse actionGet = adminClient.indices().prepareFlush().setForce(true).setFull(true).execute().actionGet();
146 | if (ignoreNotAllowed) {
147 | for (ShardOperationFailedException failure : actionGet.getShardFailures()) {
148 | // assertThat("unexpected flush failure " + failure.reason(), failure.status(), equalTo(RestStatus.SERVICE_UNAVAILABLE));
149 | }
150 | } else {
151 | // assertNoFailures(actionGet);
152 | }
153 | return actionGet;
154 | }
155 |
156 | /**
157 | * Waits for all relocating shards to become active using the cluster health API.
158 | */
159 | public ClusterHealthStatus waitForRelocation() {
160 | return waitForRelocation(null);
161 | }
162 |
163 | /**
164 | * Waits for all relocating shards to become active and the cluster has reached the given health status
165 | * using the cluster health API.
166 | */
167 | public ClusterHealthStatus waitForRelocation(ClusterHealthStatus status) {
168 | ClusterHealthRequest request = Requests.clusterHealthRequest().waitForRelocatingShards(0);
169 | if (status != null) {
170 | request.waitForStatus(status);
171 | }
172 | ClusterHealthResponse actionGet = adminClient.cluster()
173 | .health(request).actionGet();
174 | if (actionGet.isTimedOut()) {
175 | // logger.info("waitForRelocation timed out (status={}), cluster state:\n{}\n{}", status, adminClient.cluster().prepareState().get().getState().prettyPrint(), adminClient.cluster().preparePendingClusterTasks().get().prettyPrint());
176 | assertThat("timed out waiting for relocation", actionGet.isTimedOut(), equalTo(false));
177 | }
178 | if (status != null) {
179 | assertThat(actionGet.getStatus(), equalTo(status));
180 | }
181 | return actionGet.getStatus();
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/elasticsearch-engine-integration/src/main/java/org/camunda/bpm/elasticsearch/index/ElasticSearchDefaultIndexStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 - Christian Lipphardt and camunda services GmbH
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.camunda.bpm.elasticsearch.index;
18 |
19 | import com.fasterxml.jackson.core.JsonProcessingException;
20 | import org.camunda.bpm.elasticsearch.entity.ElasticSearchProcessInstanceHistoryEntity;
21 | import org.camunda.bpm.elasticsearch.util.ElasticSearchHelper;
22 | import org.camunda.bpm.engine.impl.history.event.HistoricActivityInstanceEventEntity;
23 | import org.camunda.bpm.engine.impl.history.event.HistoricProcessInstanceEventEntity;
24 | import org.camunda.bpm.engine.impl.history.event.HistoricTaskInstanceEventEntity;
25 | import org.camunda.bpm.engine.impl.history.event.HistoricVariableUpdateEventEntity;
26 | import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
27 | import org.elasticsearch.action.update.UpdateRequestBuilder;
28 | import org.elasticsearch.action.update.UpdateResponse;
29 | import org.elasticsearch.common.unit.TimeValue;
30 | import org.elasticsearch.script.ScriptService;
31 |
32 | import java.io.IOException;
33 | import java.util.HashMap;
34 | import java.util.List;
35 | import java.util.Map;
36 | import java.util.logging.Level;
37 | import java.util.logging.Logger;
38 |
39 | public class ElasticSearchDefaultIndexStrategy extends ElasticSearchIndexStrategy {
40 |
41 | protected static final Logger LOGGER = Logger.getLogger(ElasticSearchDefaultIndexStrategy.class.getName());
42 |
43 | protected static final String ES_INDEX_UPDATE_SCRIPT =
44 | "if (isActivityInstanceEvent) { if (ctx._source.containsKey(\"activities\")) { ctx._source.activities += value } else { ctx._source.activities = value } };" +
45 | "if (isTaskInstanceEvent) { if (ctx._source.containsKey(\"tasks\")) { ctx._source.tasks += value } else { ctx._source.tasks = value } };" +
46 | "if (isVariableUpdateEvent) { if (ctx._source.containsKey(\"variables\")) { ctx._source.variables += value } else { ctx._source.variables = value } };";
47 | protected static final int WAIT_FOR_RESPONSE = 5;
48 |
49 | public void executeRequest(List historyEvents) {
50 | for (HistoryEvent historyEvent : historyEvents) {
51 | executeRequest(historyEvent);
52 | }
53 | }
54 |
55 | public void executeRequest(HistoryEvent historyEvent) {
56 | try {
57 | if (filterEvents(historyEvent)) {
58 | return;
59 | }
60 |
61 | UpdateRequestBuilder updateRequestBuilder = prepareUpdateRequest(historyEvent);
62 |
63 | if (LOGGER.isLoggable(Level.FINE)) {
64 | LOGGER.fine(ElasticSearchHelper.convertRequestToJson(updateRequestBuilder.request()));
65 | }
66 |
67 | UpdateResponse updateResponse;
68 | if (WAIT_FOR_RESPONSE > 0) {
69 | updateResponse = updateRequestBuilder.get(TimeValue.timeValueSeconds(WAIT_FOR_RESPONSE));
70 | } else {
71 | updateResponse = updateRequestBuilder.get();
72 | }
73 |
74 | if (LOGGER.isLoggable(Level.FINE)) {
75 | LOGGER.fine("[" + updateResponse.getIndex() +
76 | "][" + updateResponse.getType() +
77 | "][update] process instance with id '" + updateResponse.getId() + "'");
78 | LOGGER.log(Level.FINE, "Source: " + updateResponse.getGetResult().sourceAsString());
79 | }
80 | } catch (IOException e) {
81 | LOGGER.log(Level.SEVERE, e.getMessage(), e);
82 | }
83 | }
84 |
85 | protected boolean filterEvents(HistoryEvent historyEvent) {
86 | if (historyEvent instanceof HistoricProcessInstanceEventEntity ||
87 | historyEvent instanceof HistoricActivityInstanceEventEntity ||
88 | historyEvent instanceof HistoricTaskInstanceEventEntity ||
89 | historyEvent instanceof HistoricVariableUpdateEventEntity) {
90 | return false;
91 | }
92 | return true;
93 | }
94 |
95 | protected UpdateRequestBuilder prepareUpdateRequest(HistoryEvent historyEvent) throws IOException {
96 | UpdateRequestBuilder updateRequestBuilder = esClient.prepareUpdate()
97 | .setIndex(dispatcher.getDispatchTargetIndex(historyEvent))
98 | .setId(historyEvent.getProcessInstanceId());
99 |
100 | String dispatchTargetType = dispatcher.getDispatchTargetType(historyEvent);
101 | if (dispatchTargetType != null && !dispatchTargetType.isEmpty()) {
102 | updateRequestBuilder.setType(dispatchTargetType);
103 | }
104 |
105 | if (historyEvent instanceof HistoricProcessInstanceEventEntity) {
106 | prepareHistoricProcessInstanceEventUpdate(historyEvent, updateRequestBuilder);
107 | } else if (historyEvent instanceof HistoricActivityInstanceEventEntity ||
108 | historyEvent instanceof HistoricTaskInstanceEventEntity ||
109 | historyEvent instanceof HistoricVariableUpdateEventEntity) {
110 | updateRequestBuilder = prepareOtherHistoricEventsUpdateRequest(historyEvent, updateRequestBuilder);
111 | } else {
112 | // unknown event - insert...
113 | throw new IllegalArgumentException("Unknown event detected: '" + historyEvent + "'");
114 | // LOGGER.warning("Unknown event detected: '" + historyEvent + "'");
115 | }
116 |
117 | if (LOGGER.isLoggable(Level.FINE)) {
118 | updateRequestBuilder.setFields("_source");
119 | }
120 |
121 | return updateRequestBuilder;
122 | }
123 |
124 | protected UpdateRequestBuilder prepareHistoricProcessInstanceEventUpdate(HistoryEvent historyEvent, UpdateRequestBuilder updateRequestBuilder) throws JsonProcessingException {
125 | ElasticSearchProcessInstanceHistoryEntity elasticSearchProcessInstanceHistoryEntity =
126 | ElasticSearchProcessInstanceHistoryEntity.createFromHistoryEvent(historyEvent);
127 |
128 | String event = transformer.transformToJson(elasticSearchProcessInstanceHistoryEntity);
129 |
130 | updateRequestBuilder.setDoc(event).setDocAsUpsert(true);
131 |
132 | return updateRequestBuilder;
133 | }
134 |
135 | protected UpdateRequestBuilder prepareOtherHistoricEventsUpdateRequest(HistoryEvent historyEvent, UpdateRequestBuilder updateRequestBuilder) throws IOException {
136 | HashMap scriptParams = new HashMap();
137 |
138 | if (historyEvent instanceof HistoricActivityInstanceEventEntity) {
139 | scriptParams.put("isActivityInstanceEvent", true);
140 | scriptParams.put("isTaskInstanceEvent", false);
141 | scriptParams.put("isVariableUpdateEvent", false);
142 | } else if (historyEvent instanceof HistoricTaskInstanceEventEntity) {
143 | scriptParams.put("isActivityInstanceEvent", false);
144 | scriptParams.put("isTaskInstanceEvent", true);
145 | scriptParams.put("isVariableUpdateEvent", false);
146 | } else {
147 | scriptParams.put("isActivityInstanceEvent", false);
148 | scriptParams.put("isTaskInstanceEvent", false);
149 | scriptParams.put("isVariableUpdateEvent", true);
150 | }
151 |
152 | String eventJson = transformer.transformToJson(historyEvent);
153 | // needed otherwise the resulting json is not an array/list and the update script throws an error
154 | List> events = transformer.transformJsonToList("[" + eventJson + "]");
155 | scriptParams.put("value", events);
156 |
157 | updateRequestBuilder.setScript(ES_INDEX_UPDATE_SCRIPT, ScriptService.ScriptType.INLINE)
158 | .setScriptParams(scriptParams);
159 |
160 | return updateRequestBuilder;
161 | }
162 |
163 | }
164 |
--------------------------------------------------------------------------------
/docs/userguide.md:
--------------------------------------------------------------------------------
1 | # Camunda BPM Elasticsearch User Guide
2 |
3 | ## Overview
4 |
5 | The Camunda BPM Elasticsearch extension is a community extension which allows you to use
6 | Elasticsearch as data store for Camunda history and audit data.
7 |
8 | ### Giving it a quick Try
9 |
10 | If you want to have a quick look at this project, you can start a running instance of Camunda BPM
11 | with Elasticsearch in the following way:
12 |
13 | * Cone the project using git: `git clone https://github.com/camunda/camunda-bpm-elasticsearch.git`
14 | * Build the project using maven: `mvn clean install -DskipTests`
15 | * Start the camunda Webapp from the commandline:
16 |
17 | ```bash
18 | cd elasticsearch-cockpit-plugin
19 | mvn clean jetty:run -Pdevelop
20 | ```
21 | * go to [http://localhost:8080/camunda](http://localhost:8080/camunda)
22 | * Login with `demo:demo`
23 |
24 | In Cockpit you will see a couple of plugins which allow you to search through the process engine
25 | data using Elasticsearch.
26 |
27 | ### How it works
28 |
29 | The Camunda process engine produces a continuous event stream of auditing data (Read Userguide:
30 | [History][userguide:history_event_stream]). This event stream contains events about process instances
31 | being started, tasks being completed, variables being modified and so forth (check the Camunda BPM
32 | Userguide for a complete list of supported events).
33 | The Camunda Elasticsearch Extension implements the `HistoryEventHandler` SPI and stores all history
34 | events in Elasticsearch. Once the history data is stored in Elasticsearch, it can be queried in a
35 | flexible way.
36 |
37 | ### Components
38 |
39 | The Camunda Elasticsearch Extension is composed of two modules: a process engine plugin and a
40 | Cockpit plugin.
41 |
42 | #### The Process Engine Plugin
43 |
44 | The process engine plugin implements the `HistoryEventHandler` SPI and stores camunda history events
45 | in Elasticsearch.
46 |
47 | #### The Cockpit Plugin
48 |
49 | The Cockpit plugin enhances Camunda BPM Cockpit and provides different view for accessing the
50 | History Data stored in Elasticsearch.
51 |
52 | ##### The Quick Search View
53 |
54 | The Quick Seach View is a simple seach bar which allows full text search on all stored events.
55 |
56 | ![Quick Search Screenshot][quick-search-screenshot]
57 |
58 | ##### The Activity Monitoring View
59 |
60 | The Activity Monitoring View shows an histogram with the number of started and completed process
61 | instances over time.
62 |
63 | ## Installation & Configuration
64 |
65 | ### Supported Camunda BPM Version
66 |
67 | Currently camunda BPM 7.2.0 is required.
68 |
69 | ### Installing the Process Engine Integration
70 |
71 | The process engine integration needs to be added as [Process Engine Plugin][process-engine-plugin]
72 | to Camunda Process Engine. The setup depends on whether you use a *shared* or an *embedded* process
73 | engine.
74 |
75 | #### Installation with Embedded Process Engine
76 |
77 | In order to use the Elasticsearch Module with an embedded process engine you need to add it to the
78 | process engine classpath and configure the process engine to use the plugin.
79 |
80 | ##### Adding the Elasticsearch plugin to the classpath
81 |
82 | If your application uses Apache Maven, you need to add the following dependencies to your
83 | application:
84 |
85 | ```xml
86 |
87 |
88 | org.camunda.bpm.extension.elasticsearch
89 | elasticsearch-engine-integration
90 | 1.0.0-SNAPSHOT
91 |
92 |
93 | org.elasticsearch
94 | elasticsearch
95 | 1.3.1
96 |
97 |
98 | ```
99 |
100 | ##### Spring based Configuration
101 |
102 | If you bootstrap your process engine using the Spring Framework, you can configure the Elasticsearch plugin using Spring:
103 |
104 | ```xml
105 |
106 | ...
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | ```
117 |
118 | #### Installation with Shared Process Engine
119 |
120 | If you use a shared process engine, you need to add camunda Elasticsearch JAR files and all
121 | required Elasticsearch libraries to the process engine classpath and configure the plugin in
122 | `bpm-platform.xml` or `standalone.xml` (JBoss).
123 |
124 | ##### Installation in JBoss Application Server Distribution
125 |
126 | In order to install the Elasticsearch extension on JBoss Application Server with Camunda BPM
127 | subsystem, the following steps are necessary:
128 |
129 | * Download a pre-packaged distribution from [camunda.org](http://camunda.org/download/).
130 | * Unpack the Camunda distribution into a folder of your choice.
131 | * Clone the [camunda-bpm-elasticsearch][repository] repository using Git.
132 | * Build the repository by typing `mvn clean install`
133 | * Copy the modules from `elasticsearch-jboss-module/target/modules/` to the modules folder of your
134 | jboss distribution.
135 | * Inside the modules folder of your JBoss Application Server, edit the file
136 | `modules/org/camunda/bpm/jboss/camunda-jboss-subsystem/main/module.xm` and add a
137 | module dependency to the elasticsearch module:
138 |
139 | ```xml
140 |
141 | ...
142 |
143 | ...
144 |
145 |
146 |
147 |
148 | ```
149 |
150 | * Edit the JBoss `standalone.xml` file and activate the Elasticsearch plugin:
151 |
152 | ```xml
153 |
154 |
155 |
156 | ...
157 |
158 |
159 | org.camunda.bpm.elasticsearch.ElasticSearchHistoryPlugin
160 |
161 |
162 |
163 |
164 |
165 | ```
166 |
167 | ##### Installation in Tomcat Distribution
168 |
169 | In order to install the Elasticsearch process engine plugin in the Camunda Tomcat distribution, the
170 | following steps are necessary:
171 |
172 | * Download a pre-packaged distribution from [camunda.org](http://camunda.org/download/).
173 | * Unpack the camunda distribution into a folder of your choice.
174 | * Clone the [camunda-bpm-elasticsearch][repository] repository using Git.
175 | * Build the repository by typing `mvn clean install`
176 | * copy the libraries `elasticsearch-lib-module/target/lib/` to the apache tomcat `lib/` folder (the
177 | same folder in which the `camunda-engine.jar` file is located.).
178 | * Inside the apache tomcat configuration folder, edit the file named `bpm-platform.xml`:
179 |
180 | ```xml
181 |
182 | ...
183 |
184 |
185 | org.camunda.bpm.elasticsearch.ElasticSearchHistoryPlugin
186 |
187 |
188 |
189 | ```
190 |
191 | ### Installing the Cockpit Plugin
192 |
193 | > TODO: this section needs to be improved
194 |
195 | In order to install the cockpit plugin you need to proceed as follows:
196 |
197 | * Build the cockpit plugin by cloning the repository and building it with maven
198 | * Copy the plugin jar file to the WEB-INF/lib folder of the camunda Webapplication
199 |
200 | [userguide:history_event_stream]: http://docs.camunda.org/latest/guides/user-guide/#process-engine-history-and-audit-event-log
201 | [quick-search-screenshot]: img/quick-search.png
202 | [process-engine-plugin]: http://docs.camunda.org/latest/guides/user-guide/#process-engine-process-engine-plugins
203 | [repository]: https://github.com/camunda/camunda-bpm-elasticsearch
204 |
--------------------------------------------------------------------------------
/elasticsearch-cockpit-plugin/src/main/resources/plugin-webapp/dashboards/app/lib/nv.d3.min.css:
--------------------------------------------------------------------------------
1 | .chartWrap{margin:0;padding:0;overflow:hidden}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nvtooltip{position:absolute;background-color:rgba(255,255,255,1);padding:1px;border:1px solid rgba(0,0,0,.2);z-index:10000;font-family:Arial;font-size:13px;text-align:left;pointer-events:none;white-space:nowrap;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 250ms linear;-moz-transition:opacity 250ms linear;-webkit-transition:opacity 250ms linear;transition-delay:250ms;-moz-transition-delay:250ms;-webkit-transition-delay:250ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{margin:0;padding:4px 14px;line-height:18px;font-weight:400;background-color:rgba(247,247,247,.75);text-align:center;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.nvtooltip p{margin:0;padding:5px 14px;text-align:center}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key{font-weight:400}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{width:8px;height:8px;vertical-align:middle}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{position:absolute;pointer-events:none}svg{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:block;width:100%;height:100%}svg text{font:400 12px Arial}svg .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .disabled circle{fill-opacity:0}.nvd3 .nv-axis{pointer-events:none}.nvd3 .nv-axis path{fill:none;stroke:#000;stroke-opacity:.75;shape-rendering:crispEdges}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{fill:none;stroke:#e5e5e5;shape-rendering:crispEdges}.nvd3 .nv-axis .zero line,.nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-bars .negative rect{zfill:brown}.nvd3 .nv-bars rect{zfill:#4682b4;fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:rgba(0,0,0,0)}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect,.nvd3 .nv-discretebar .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:rgba(0,0,0,0)}.nvd3.nv-pie path{stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label{pointer-events:none}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups path.nv-line{fill:none;stroke-width:1.5px}.nvd3 .nv-groups path.nv-line.nv-thin-line{stroke-width:1px}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3 .nv-line.hover path{stroke-width:6px}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-scatter .nv-groups .nv-point.hover,.nvd3 .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}.nvd3 .nv-distribution{pointer-events:none}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:4px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3.nv-historicalStockChart .nv-axis .nv-axislabel{font-weight:700}.nvd3.nv-historicalStockChart .nv-dragTarget{fill-opacity:0;stroke:none;cursor:move}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-indentedtree .name{margin-left:5px}.nvd3.nv-indentedtree .clickable{color:#08C;cursor:pointer}.nvd3.nv-indentedtree span.clickable:hover{color:#005580;text-decoration:underline}.nvd3.nv-indentedtree .nv-childrenCount{display:inline-block;margin-left:5px}.nvd3.nv-indentedtree .nv-treeicon{cursor:pointer}.nvd3.nv-indentedtree .nv-treeicon.nv-folded{cursor:pointer}.nvd3 .background path{fill:none;stroke:#ccc;stroke-opacity:.4;shape-rendering:crispEdges}.nvd3 .foreground path{fill:none;stroke:#4682b4;stroke-opacity:.7}.nvd3 .brush .extent{fill-opacity:.3;stroke:#fff;shape-rendering:crispEdges}.nvd3 .axis line,.axis path{fill:none;stroke:#000;shape-rendering:crispEdges}.nvd3 .axis text{text-shadow:0 1px 0 #fff}.nvd3 .nv-interactiveGuideLine{pointer-events:none}.nvd3 line.nv-guideline{stroke:#ccc}
--------------------------------------------------------------------------------