diff --git a/doc/fr/development_tutorial.md b/doc/fr/development_tutorial.md index 27bcfc6a635d9fbeceb728dede7ad9629b18c5f2..2cccc2296acd6a0be3f07a0ecf279fc0a84499eb 100644 --- a/doc/fr/development_tutorial.md +++ b/doc/fr/development_tutorial.md @@ -286,7 +286,7 @@ Ouvrir votre IDE, et ouvrir le projet Duniter4j. Dans le répertoire `duniter4j-elasticsearch/src/main/java`, cherchez et répérez dans le code : -- les controlleurs REST : package `org.duniter.elasticsearch.action` +- les controlleurs REST : package `org.duniter.elasticsearch.rest` - les services d'indexation : package `org.duniter.elasticsearch.service`. * Il existe un service d'indexation par type de stockage. Par exemple : `BlockchainService`, `UserService`, etc. diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Constants.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Constants.java index ff2ee8c223377fddab75f03100c7c2bc9e1b115f..1df3da70caa447790e09943d22cce224f1a94be9 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Constants.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Constants.java @@ -28,6 +28,8 @@ package org.duniter.core.client.model.bma; public interface Constants { interface Regex { - String CURRENCY_NAME = "[a-zA-Z_-]"; + String USER_ID = "[A-Za-z0-9_-]+"; + String CURRENCY_NAME = "[A-Za-z0-9_-]"; + String PUBKEY = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}"; } } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/elasticsearch/Event.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/elasticsearch/Event.java index d535ad58ac20900c119e114a00f4adea169c33a4..ffb926ef1667ab97320e004160c565a38b2fa812 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/elasticsearch/Event.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/elasticsearch/Event.java @@ -1,5 +1,27 @@ package org.duniter.core.client.model.elasticsearch; +/* + * #%L + * Duniter4j :: Core Client API + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + import org.nuiton.i18n.I18n; import java.util.Locale; diff --git a/duniter4j-core-shared/src/main/java/org/duniter/core/service/MailService.java b/duniter4j-core-shared/src/main/java/org/duniter/core/service/MailService.java index 3bb0859189f10e3be065383d02050baf2420866f..64975b25ce5240bdece733be974e4a995e0f3977 100644 --- a/duniter4j-core-shared/src/main/java/org/duniter/core/service/MailService.java +++ b/duniter4j-core-shared/src/main/java/org/duniter/core/service/MailService.java @@ -1,5 +1,27 @@ package org.duniter.core.service; +/* + * #%L + * Duniter4j :: Core Shared + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + import org.duniter.core.beans.Bean; import org.duniter.core.exception.TechnicalException; diff --git a/duniter4j-core-shared/src/main/java/org/duniter/core/service/MailServiceImpl.java b/duniter4j-core-shared/src/main/java/org/duniter/core/service/MailServiceImpl.java index d217524284f599a30280685c129820f15139a4f9..ec7bb898b1ffd51c2e62049835e3fe7ad6257931 100644 --- a/duniter4j-core-shared/src/main/java/org/duniter/core/service/MailServiceImpl.java +++ b/duniter4j-core-shared/src/main/java/org/duniter/core/service/MailServiceImpl.java @@ -1,5 +1,27 @@ package org.duniter.core.service; +/* + * #%L + * Duniter4j :: Core Shared + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + import org.duniter.core.exception.TechnicalException; import org.duniter.core.util.StringUtils; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/cli/action/HelpCliAction.java b/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/cli/action/HelpCliAction.java deleted file mode 100644 index 5fd18e0be55780c9e0aa94c360046f299aecf043..0000000000000000000000000000000000000000 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/cli/action/HelpCliAction.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.duniter.elasticsearch.cli.action; - -/* - * #%L - * SIH-Adagio :: Shared - * $Id:$ - * $HeadURL:$ - * %% - * Copyright (C) 2012 - 2014 Ifremer - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -public class HelpCliAction { - - public void show() { - StringBuilder sb = new StringBuilder(); - - sb.append("Usage: duniter4j-elaticsearch.<sh|bat> <commands> [options]\n\n") - .append("Commands:\n\n") - .append(" start Start elastic search node\n") - .append(" index Index blocks from BMA Node\n") - .append(" reset-data Reset all indexed data (blocks and records)\n") - .append(" reset-data-blocks Reset only indexed blocks (from uCoin node's)\n") - .append(" reset-data-records Reset only indexed records\n") - .append("\n") - .append("\n") - .append("Options:\n\n") - .append(" --help Output usage information\n") - .append(" -h --host <user> uCoin node host (with Basic Merkled API)\n") - .append(" -p --port <pwd> uCoin node port (with Basic Merkled API)\n") - .append("\n") - .append(" -esh --es-host <user> ElasticSearch node host\n") - .append(" -esp --es-port <pwd> ElasticSearch node port\n"); - - System.out.println(sb.toString()); - } -} diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/cli/action/IndexerCliAction.java b/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/cli/action/IndexerCliAction.java deleted file mode 100644 index 5220383c2343d76ebfad5c27917241646449a6dd..0000000000000000000000000000000000000000 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/cli/action/IndexerCliAction.java +++ /dev/null @@ -1,187 +0,0 @@ -package org.duniter.elasticsearch.cli.action; - -/* - * #%L - * SIH-Adagio :: Shared - * $Id:$ - * $HeadURL:$ - * %% - * Copyright (C) 2012 - 2014 Ifremer - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class IndexerCliAction { - /* Logger */ - private static final Logger log = LoggerFactory.getLogger(IndexerCliAction.class); - - /* - public void indexBlocksFromNode() { - - final boolean async = ServiceLocator.instance().getElasticSearchService().isNodeInstance(); - - Runnable runnable = new Runnable() { - @Override - public void run() { - PluginSettings config = PluginSettings.instance(); - final Peer peer = checkConfigAndGetPeer(config); - final BlockchainService blockIndexerService = ServiceLocator.instance().getBlockIndexerService(); - - // Will create the blockchain if not exist - blockIndexerService.indexLastBlocks(peer); - - if (async) { - ServiceLocator.instance().getBlockchainRemoteService().addNewBlockListener(peer, new WebsocketClientEndpoint.MessageHandler() { - @Override - public void handleMessage(String message) { - String currencyName = GsonUtils.getValueFromJSONAsString(message, "blockchain"); - *///blockIndexerService.indexLastBlockFromJson(peer, message, true /*refresh*/, true /*wait*/); - //blockIndexerService.indexCurrentBlockAsJson(currencyName, message, true /*wait*/); - /* } - }); - } - } - }; - - // Async execution - if (async) { - ServiceLocator.instance().getExecutorService().execute(runnable); - } - - // Synchrone execution - else { - runnable.run(); - } - } - - public void resetAllData() { - resetAllCurrencies(); - resetDataBlocks(); - resetMarketRecords(); - resetRegistry(); - } - - public void resetAllCurrencies() { - CurrencyRegistryService currencyIndexerService = ServiceLocator.instance().getRegistryCurrencyIndexerService(); - currencyIndexerService.deleteAllCurrencies(); - } - - public void resetDataBlocks() { - BlockchainRemoteService blockchainService = ServiceLocator.instance().getBlockchainRemoteService(); - BlockchainService indexerService = ServiceLocator.instance().getBlockIndexerService(); - PluginSettings config = PluginSettings.instance(); - Peer peer = checkConfigAndGetPeer(config); - - try { - // Get the blockchain name from node - BlockchainParameters parameter = blockchainService.getParameters(peer); - if (parameter == null) { - log.error(String.format("Could not connect to node [%s:%s]", - config.getNodeBmaHost(), config.getNodeBmaPort())); - return; - } - String currencyName = parameter.getCurrency(); - - log.info(String.format("Reset data for index [%s]", currencyName)); - - // Delete then create index on blockchain - boolean indexExists = indexerService.existsIndex(currencyName); - if (indexExists) { - indexerService.deleteIndex(currencyName); - indexerService.createIndex(currencyName); - } - - - log.info(String.format("Successfully reset data for index [%s]", currencyName)); - } catch(Exception e) { - log.error("Error during reset data: " + e.getMessage(), e); - } - } - - public void resetMarketRecords() { - RecordMarketService recordIndexerService = ServiceLocator.instance().getMarketRecordIndexerService(); - CategoryMarketService categoryIndexerService = ServiceLocator.instance().getMarketCategoryIndexerService(); - - try { - // Delete then create index on records - boolean indexExists = recordIndexerService.existsIndex(); - if (indexExists) { - recordIndexerService.deleteIndex(); - } - log.info(String.format("Successfully reset market records")); - - categoryIndexerService.createIndex(); - categoryIndexerService.initCategories(); - log.info(String.format("Successfully re-initialized market categories data")); - - } catch(Exception e) { - log.error("Error during reset market records: " + e.getMessage(), e); - } - } - - public void resetRegistry() { - RecordRegistryService recordIndexerService = ServiceLocator.instance().getRegistryRecordIndexerService(); - CategoryRegistryService categoryIndexerService = ServiceLocator.instance().getRegistryCategoryIndexerService(); - CitiesRegistryService citiesIndexerService = ServiceLocator.instance().getRegistryCitiesIndexerService(); - - try { - // Delete then create index on records - if (recordIndexerService.existsIndex()) { - recordIndexerService.deleteIndex(); - } - recordIndexerService.createIndex(); - log.info(String.format("Successfully reset registry records")); - - - if (categoryIndexerService.existsIndex()) { - categoryIndexerService.deleteIndex(); - } - categoryIndexerService.createIndex(); - categoryIndexerService.initCategories(); - log.info(String.format("Successfully re-initialized registry categories")); - - if (citiesIndexerService.existsIndex()) { - citiesIndexerService.deleteIndex(); - } - citiesIndexerService.initCities(); - log.info(String.format("Successfully re-initialized registry cities")); - - } catch(Exception e) { - log.error("Error during reset registry records: " + e.getMessage(), e); - } - }*/ - - /* -- internal methods -- */ - - /*protected Peer checkConfigAndGetPeer(PluginSettings config) { - if (StringUtils.isBlank(config.getNodeBmaHost())) { - log.error("ERROR: node host is required"); - System.exit(-1); - return null; - } - if (config.getNodeBmaPort() <= 0) { - log.error("ERROR: node port is required"); - System.exit(-1); - return null; - } - - Peer peer = new Peer(config.getNodeBmaHost(), config.getNodeBmaPort()); - return peer; - }*/ -} diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/event/EventCodes.java b/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/event/EventCodes.java deleted file mode 100644 index f790e04670df91961cc3c46d7d839193b4d9e91a..0000000000000000000000000000000000000000 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/event/EventCodes.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.duniter.elasticsearch.service.event; - -/** - * Created by blavenie on 29/11/16. - */ -public enum EventCodes { - - NODE_STARTED -} diff --git a/duniter4j-elasticsearch/pom.xml b/duniter4j-es-assembly/pom.xml similarity index 68% rename from duniter4j-elasticsearch/pom.xml rename to duniter4j-es-assembly/pom.xml index b4d5c8d8e4b57d477c025bd158bd2fc15df1e649..b3acfffe1a74e7a0e6a6cf5ff43d396a2fc7ff4a 100644 --- a/duniter4j-elasticsearch/pom.xml +++ b/duniter4j-es-assembly/pom.xml @@ -8,163 +8,31 @@ </parent> <groupId>org.duniter</groupId> - <artifactId>duniter4j-elasticsearch</artifactId> - <packaging>jar</packaging> - <name>Duniter4j :: ElasticSearch Plugin</name> + <artifactId>duniter4j-es-assembly</artifactId> + <packaging>pom</packaging> + <name>Duniter4j :: ElasticSearch Assembly</name> <properties> <!-- bundle configuration --> - <bundlePrefix>duniter4j-elasticsearch-${project.version}</bundlePrefix> + <bundlePrefix>duniter4j-es-${project.version}</bundlePrefix> <!-- i18n configuration --> - <i18n.bundleOutputName>duniter4j-elasticsearch-i18n</i18n.bundleOutputName> + <i18n.bundleOutputName>duniter4j-es-i18n</i18n.bundleOutputName> <i18n.generateCsvFile>true</i18n.generateCsvFile> <i18n.bundleCsvFile> ${maven.gen.dir}/resources/META-INF/${i18n.bundleOutputName}.csv </i18n.bundleCsvFile> <config.i18nBundleName>${i18n.bundleOutputName}</config.i18nBundleName> - - <duniter4j-elasticsearch.config>${project.basedir}/src/test/resources/duniter4j-elasticsearch-test.properties</duniter4j-elasticsearch.config> - <assembly.skip>false</assembly.skip> </properties> - <dependencies> - <dependency> - <groupId>org.duniter</groupId> - <artifactId>duniter4j-core-client</artifactId> - <version>${project.version}</version> - <exclusions> - <exclusion> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> - </exclusion> - </exclusions> - </dependency> - <!-- LOGGING DEPENDENCIES - SLF4J --> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-api</artifactId> - </dependency> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-log4j12</artifactId> - <optional>true</optional> - </dependency> - <dependency> - <groupId>log4j</groupId> - <artifactId>log4j</artifactId> - <optional>true</optional> - <scope>runtime</scope> - </dependency> - - <!-- Elastic Search --> - <dependency> - <groupId>org.elasticsearch</groupId> - <artifactId>elasticsearch</artifactId> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-databind</artifactId> - </dependency> - - <!-- JNA (need for OS shutdown hook) --> - <dependency> - <groupId>net.java.dev.jna</groupId> - <artifactId>jna</artifactId> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>net.java.dev.jna</groupId> - <artifactId>jna-platform</artifactId> - <exclusions> - <exclusion> - <groupId>net.java.dev.jna</groupId> - <artifactId>jna</artifactId> - </exclusion> - </exclusions> - </dependency> - -<!-- - <dependency> - <groupId>com.github.spullara.mustache.java</groupId> - <artifactId>compiler</artifactId> - <version>0.8.13</version> - <scope>compile</scope> - </dependency> ---> - - <dependency> - <groupId>org.glassfish.tyrus</groupId> - <artifactId>tyrus-server</artifactId> - </dependency> - <dependency> - <groupId>org.glassfish.tyrus</groupId> - <artifactId>tyrus-container-grizzly-server</artifactId> - </dependency> - - <!-- Unit test --> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - <build> - <resources> - <resource> - <directory>src/main/filtered-resources</directory> - <filtering>true</filtering> - <includes> - <include>*.config</include> - <include>**/*.properties</include> - </includes> - </resource> - <resource> - <directory>src/main/resources</directory> - <filtering>false</filtering> - </resource> - </resources> <plugins> - <plugin> - <groupId>org.nuiton.i18n</groupId> - <artifactId>i18n-maven-plugin</artifactId> - - <executions> - <execution> - <id>scan-sources</id> - <configuration> - <entries> - <entry> - <specificGoal>parserValidation</specificGoal> - <basedir>${maven.src.dir}/main/java/</basedir> - <includes> - <param>**/**-validation.xml</param> - </includes> - </entry> - </entries> - </configuration> - <goals> - <goal>parserJava</goal> - <goal>parserValidation</goal> - <goal>gen</goal> - </goals> - </execution> - <execution> - <id>make-bundle</id> - <goals> - <goal>bundle</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> <artifactId>maven-dependency-plugin</artifactId> <executions> + <!-- unpack ES --> <execution> <id>unpack-elasticsearch</id> <goals> @@ -185,6 +53,8 @@ <skip>${assembly.skip}</skip> </configuration> </execution> + + <!-- unpack attachment plugin --> <execution> <id>unpack-mapper-attachments-plugin</id> <goals> @@ -205,30 +75,78 @@ <skip>${assembly.skip}</skip> </configuration> </execution> - </executions> - </plugin> - <plugin> - <artifactId>maven-assembly-plugin</artifactId> - <executions> + <!-- unpack ES core plugin --> <execution> - <id>assembly-plugin</id> - <phase>package</phase> + <id>unpack-es-core-plugin</id> <goals> - <goal>single</goal> + <goal>unpack</goal> </goals> + <phase>prepare-package</phase> <configuration> - <attach>true</attach> - <appendAssemblyId>false</appendAssemblyId> - <finalName>${bundlePrefix}</finalName> - <descriptors> - <descriptor> - ${basedir}/src/main/assembly/plugin.xml - </descriptor> - </descriptors> - <skipAssembly>${assembly.skip}</skipAssembly> + <artifactItems> + <artifactItem> + <groupId>org.duniter</groupId> + <artifactId>duniter4j-es-core</artifactId> + <version>${project.version}</version> + <type>zip</type> + </artifactItem> + </artifactItems> + <outputDirectory>${project.build.directory}/elasticsearch-${elasticsearch.version}/plugins/duniter4j-es-core</outputDirectory> + <silent>true</silent> + <skip>${assembly.skip}</skip> + </configuration> + </execution> + + <!-- unpack ES user plugin --> + <execution> + <id>unpack-es-user-plugin</id> + <goals> + <goal>unpack</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.duniter</groupId> + <artifactId>duniter4j-es-user</artifactId> + <version>${project.version}</version> + <type>zip</type> + </artifactItem> + </artifactItems> + <outputDirectory>${project.build.directory}/elasticsearch-${elasticsearch.version}/plugins/duniter4j-es-user</outputDirectory> + <silent>true</silent> + <skip>${assembly.skip}</skip> + </configuration> + </execution> + + <!-- unpack ES gchange plugin --> + <execution> + <id>unpack-es-gchange-plugin</id> + <goals> + <goal>unpack</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.duniter</groupId> + <artifactId>duniter4j-es-gchange</artifactId> + <version>${project.version}</version> + <type>zip</type> + </artifactItem> + </artifactItems> + <outputDirectory>${project.build.directory}/elasticsearch-${elasticsearch.version}/plugins/duniter4j-es-gchange</outputDirectory> + <silent>true</silent> + <skip>${assembly.skip}</skip> </configuration> </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> <execution> <id>assembly-standalone</id> <phase>package</phase> @@ -291,10 +209,14 @@ <!-- reuse standalone files --> <then> <delete failonerror="false"> - <fileset dir="${run.es.home}/plugins/${project.artifactId}" includes="${project.artifactId}-*.jar" /> + <fileset dir="${run.es.home}/plugins/${project.groupId}" includes="duniter4j-*.jar" /> </delete> - <copy todir="${run.es.home}/plugins/${project.artifactId}" overwrite="true"> - <fileset dir="${project.build.directory}" includes="*.jar"> + <copy todir="${run.es.home}/plugins/${project.groupId}" overwrite="true"> + <fileset dir="../duniter4j-es-core/target" includes="duniter4j-*${project.version}.jar"> + </fileset> + <fileset dir="../duniter4j-es-user/target" includes="duniter4j-*${project.version}.jar"> + </fileset> + <fileset dir="../duniter4j-es-gchange/target" includes="duniter4j-*${project.version}.jar"> </fileset> </copy> </then> diff --git a/duniter4j-elasticsearch/src/main/assembly/config/elasticsearch.yml b/duniter4j-es-assembly/src/main/assembly/config/elasticsearch.yml similarity index 99% rename from duniter4j-elasticsearch/src/main/assembly/config/elasticsearch.yml rename to duniter4j-es-assembly/src/main/assembly/config/elasticsearch.yml index bfde540b2c396897a130de459f70d8a50646490b..b7eec87e9594b461d3321849282c0960b7472524 100644 --- a/duniter4j-elasticsearch/src/main/assembly/config/elasticsearch.yml +++ b/duniter4j-es-assembly/src/main/assembly/config/elasticsearch.yml @@ -100,7 +100,7 @@ http.cors.enabled: true # # Require explicit names when deleting indices: # -# action.destructive_requires_name: true +# rest.destructive_requires_name: true security.manager.enabled: false diff --git a/duniter4j-elasticsearch/src/main/assembly/config/logging.yml b/duniter4j-es-assembly/src/main/assembly/config/logging.yml similarity index 98% rename from duniter4j-elasticsearch/src/main/assembly/config/logging.yml rename to duniter4j-es-assembly/src/main/assembly/config/logging.yml index 2fe5b777407e59b7c2faae4cbbd61a94d93017b9..82d2ed08061ae5b56335db6048aeb3906777b52b 100644 --- a/duniter4j-elasticsearch/src/main/assembly/config/logging.yml +++ b/duniter4j-es-assembly/src/main/assembly/config/logging.yml @@ -2,7 +2,7 @@ es.logger.level: INFO rootLogger: ${es.logger.level}, console, file logger: - # log action execution errors for easier debugging + # log rest execution errors for easier debugging action: DEBUG # deprecation logging, turn to DEBUG to see them diff --git a/duniter4j-elasticsearch/src/main/assembly/standalone.xml b/duniter4j-es-assembly/src/main/assembly/standalone.xml similarity index 63% rename from duniter4j-elasticsearch/src/main/assembly/standalone.xml rename to duniter4j-es-assembly/src/main/assembly/standalone.xml index 7ad07ec1bd98813a96be85b91d7519e4190280e5..e9d6a6ee5e808c7602ef3e4b3b8a3c788fc55cbe 100644 --- a/duniter4j-elasticsearch/src/main/assembly/standalone.xml +++ b/duniter4j-es-assembly/src/main/assembly/standalone.xml @@ -62,51 +62,9 @@ <include>logging.yml</include> </includes> </fileSet> - - <fileSet> - <directory>target</directory> - <outputDirectory>plugins/${project.artifactId}</outputDirectory> - <includes> - <include>${project.build.finalName}.${project.packaging}</include> - </includes> - </fileSet> - - <fileSet> - <directory>target/classes</directory> - <outputDirectory>plugins/${project.artifactId}</outputDirectory> - <includes> - <include>plugin-descriptor.properties</include> - <include>plugin-security.policy</include> - </includes> - </fileSet> - - <fileSet> - <outputDirectory>plugins/${project.artifactId}</outputDirectory> - <includes> - <include>LICENSE</include> - </includes> - </fileSet> </fileSets> <dependencySets> - <dependencySet> - <outputDirectory>plugins/${project.artifactId}</outputDirectory> - <useProjectArtifact>true</useProjectArtifact> - <useTransitiveFiltering>true</useTransitiveFiltering> - <excludes> - <exclude>org.elasticsearch:elasticsearch</exclude> - <exclude>net.java.dev.jna:jna</exclude> - <exclude>com.fasterxml.jackson.core:jackson-core</exclude> - <exclude>log4j:log4j</exclude> - <exclude>com.google.guava:guava</exclude> - <!-- lib to include in elasticsearch/libs/ --> - <exclude>javax.websocket:javax.websocket-api</exclude> - <exclude>org.glassfish.tyrus:tyrus-client</exclude> - <exclude>org.glassfish.tyrus:tyrus-container-grizzly-client</exclude> - </excludes> - <fileMode>0555</fileMode> - </dependencySet> - <dependencySet> <outputDirectory>lib</outputDirectory> <useProjectArtifact>true</useProjectArtifact> diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostIndexAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostIndexAction.java similarity index 97% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostIndexAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostIndexAction.java index 9b2eb4d617d9568754829863afac029469d5ac4d..5f715ec31a9529a99e1c1133148b9e2068673de0 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostIndexAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostIndexAction.java @@ -23,9 +23,9 @@ package org.duniter.elasticsearch.action; */ import org.duniter.core.exception.BusinessException; -import org.duniter.elasticsearch.action.security.RestSecurityController; -import org.duniter.elasticsearch.exception.DuniterElasticsearchException; import org.duniter.elasticsearch.rest.XContentThrowableRestResponse; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.exception.DuniterElasticsearchException; import org.elasticsearch.client.Client; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.ESLoggerFactory; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostUpdateAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostUpdateAction.java similarity index 97% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostUpdateAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostUpdateAction.java index ee02158ab7289362434c6229722c95e1e5f2ae96..c56a0a23aef039d0b2fffcaeee94a93e1143c378 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostUpdateAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/AbstractRestPostUpdateAction.java @@ -23,9 +23,9 @@ package org.duniter.elasticsearch.action; */ import org.duniter.core.exception.BusinessException; -import org.duniter.elasticsearch.action.security.RestSecurityController; -import org.duniter.elasticsearch.exception.DuniterElasticsearchException; import org.duniter.elasticsearch.rest.XContentThrowableRestResponse; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.exception.DuniterElasticsearchException; import org.elasticsearch.client.Client; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.ESLoggerFactory; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/RestModule.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/RestModule.java similarity index 87% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/RestModule.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/RestModule.java index 6c68949b37aca1b2e3e7ea5009e5043103286375..a67d6f2a078292396379f7f74c30149b763b50b8 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/RestModule.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/RestModule.java @@ -22,16 +22,16 @@ package org.duniter.elasticsearch.action; * #L% */ -import org.duniter.elasticsearch.action.currency.RestCurrencyIndexAction; -import org.duniter.elasticsearch.action.history.RestHistoryDeleteIndexAction; +import org.duniter.elasticsearch.rest.currency.RestCurrencyIndexAction; +import org.duniter.elasticsearch.rest.history.RestHistoryDeleteIndexAction; import org.duniter.elasticsearch.action.market.*; import org.duniter.elasticsearch.action.message.RestMessageInboxIndexAction; import org.duniter.elasticsearch.action.message.RestMessageOutboxIndexAction; import org.duniter.elasticsearch.action.registry.*; -import org.duniter.elasticsearch.action.security.RestSecurityAuthAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; -import org.duniter.elasticsearch.action.security.RestSecurityFilter; -import org.duniter.elasticsearch.action.security.RestSecurityGetChallengeAction; +import org.duniter.elasticsearch.rest.security.RestSecurityAuthAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.rest.security.RestSecurityFilter; +import org.duniter.elasticsearch.rest.security.RestSecurityGetChallengeAction; import org.duniter.elasticsearch.action.user.RestUserProfileIndexAction; import org.duniter.elasticsearch.action.user.RestUserProfileUpdateAction; import org.duniter.elasticsearch.action.user.RestUserSettingsIndexAction; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/history/RestHistoryDeleteIndexAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/history/RestHistoryDeleteIndexAction.java similarity index 88% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/history/RestHistoryDeleteIndexAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/history/RestHistoryDeleteIndexAction.java index 79936a4ec38c891918e004007a28ecf14876514d..0778b8d60ee0938c7320562848deec35c58e9ea9 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/history/RestHistoryDeleteIndexAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/history/RestHistoryDeleteIndexAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.history; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostIndexAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.HistoryService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; @@ -34,7 +34,7 @@ import org.elasticsearch.rest.RestController; public class RestHistoryDeleteIndexAction extends AbstractRestPostIndexAction { - private static final ESLogger log = ESLoggerFactory.getLogger(RestHistoryDeleteIndexAction.class.getName()); + private static final ESLogger log = ESLoggerFactory.getLogger(org.duniter.elasticsearch.rest.history.RestHistoryDeleteIndexAction.class.getName()); @Inject public RestHistoryDeleteIndexAction(Settings settings, RestController controller, Client client, diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCategoryAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCategoryAction.java similarity index 94% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCategoryAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCategoryAction.java index 6ece11506906aceae208f37a428b02c1c59e1653..7bf4f6ae423b0a3896c0900ab1914ff6a6e12a19 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCategoryAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCategoryAction.java @@ -22,7 +22,7 @@ package org.duniter.elasticsearch.action.market; * #L% */ -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.MarketService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.rest.RestRequest; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentIndexAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentIndexAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentIndexAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentIndexAction.java index 08452ec88a89538970eacb90600fd9488fc80987..37b278a0ac678b036d88d19db4eaacd5ab5112bd 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentIndexAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentIndexAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.market; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostIndexAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.MarketService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentUpdateAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentUpdateAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentUpdateAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentUpdateAction.java index b1840100e7eb7bf4edcf747bf0240e849f0a58a2..a11d15ab94ece445162e8af7aa31b99ea28df8e6 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentUpdateAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketCommentUpdateAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.market; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostUpdateAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.MarketService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordIndexAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordIndexAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordIndexAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordIndexAction.java index 2d2b8d56fb30e3c649e9fdd43cf6fe36d2a32f2c..1f7cfc68dd69eb97c7453cfeac80af4bceee66b2 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordIndexAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordIndexAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.market; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostIndexAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.MarketService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordUpdateAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordUpdateAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordUpdateAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordUpdateAction.java index 9b1b99da434718d59a2a9b7ee496f563c94cb595..7b1cd758b089c8a287e32ac233c6b3b3ae760dbf 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordUpdateAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/market/RestMarketRecordUpdateAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.market; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostUpdateAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.MarketService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/message/RestMessageInboxIndexAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/message/RestMessageInboxIndexAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/message/RestMessageInboxIndexAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/message/RestMessageInboxIndexAction.java index d6c5bdfd75b95bebc24b2f2d10b39c214ff61f26..1ab8b7d53c57ae106d5812a918cbc5c710b9eadf 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/message/RestMessageInboxIndexAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/message/RestMessageInboxIndexAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.message; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostIndexAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.MessageService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/message/RestMessageOutboxIndexAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/message/RestMessageOutboxIndexAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/message/RestMessageOutboxIndexAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/message/RestMessageOutboxIndexAction.java index 70e08b0ab147e44d2aa98a12b89d8c18c35c395c..5fc0c8ba24eb47502266301c9eb9fe3056b32fea 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/message/RestMessageOutboxIndexAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/message/RestMessageOutboxIndexAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.message; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostIndexAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.MessageService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCategoryAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCategoryAction.java similarity index 94% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCategoryAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCategoryAction.java index 7bfdf238cce3091dcf99e734fb7d0f5bc8adfb9a..db635061101d389b439931bb9fba650d3f95d5a3 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCategoryAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCategoryAction.java @@ -22,7 +22,7 @@ package org.duniter.elasticsearch.action.registry; * #L% */ -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.RegistryService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.rest.RestRequest; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCommentIndexAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCommentIndexAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCommentIndexAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCommentIndexAction.java index 1c112ce448084527ee943ea9341949b8ca1eefc7..b1fb59693d0355602abec4487eaf7298d9c8c096 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCommentIndexAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryCommentIndexAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.registry; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostIndexAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.RegistryService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordIndexAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordIndexAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordIndexAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordIndexAction.java index 1d3c576891bcc6bdfae0bfb02927c252a689c5e2..98d4fce014f567781d28a86a5276d5712af7c7d7 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordIndexAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordIndexAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.registry; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostIndexAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.RegistryService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordUpdateAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordUpdateAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordUpdateAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordUpdateAction.java index dcfba5eb166a5cf06b7eba171a6a76d02385af3f..3ea0b8a01ce461d0945eec000f96d1168872ebd7 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordUpdateAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestRegistryRecordUpdateAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.registry; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostUpdateAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.RegistryService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestregistryCommentUpdateAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestregistryCommentUpdateAction.java similarity index 88% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestregistryCommentUpdateAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestregistryCommentUpdateAction.java index 6137eacbe420cbbde9077dd9b5d746ec9cb706df..329a5ae700849ad89611b93517b8a6f9f24cfd29 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/registry/RestregistryCommentUpdateAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/registry/RestregistryCommentUpdateAction.java @@ -22,9 +22,8 @@ package org.duniter.elasticsearch.action.registry; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostUpdateAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; -import org.duniter.elasticsearch.service.MarketService; +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.RegistryService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileIndexAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileIndexAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileIndexAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileIndexAction.java index 9e738f86a865c7d0585096db543cdf1c1819adc0..535fb070daf65705a69e5f5cb3e4ed612ac1bd84 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileIndexAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileIndexAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.user; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostIndexAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.UserService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileUpdateAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileUpdateAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileUpdateAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileUpdateAction.java index e6d60417be959af40b0fe323cb749525cd6ba6f3..c287a82eea4b60477088c3069e9b8738839178af 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileUpdateAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserProfileUpdateAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.user; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostUpdateAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.UserService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsIndexAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsIndexAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsIndexAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsIndexAction.java index cb9f7c75b72285368386ad1904ca43aff500226a..1948f583fe07f125725e9d2ef80b7343cf10958d 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsIndexAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsIndexAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.user; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostIndexAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.UserService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsUpdateAction.java b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsUpdateAction.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsUpdateAction.java rename to duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsUpdateAction.java index 3cf36bae5b7b636c4836e543a841fcadf6ca6a6d..b7b3bd717f67ef8cb2517f7824ceafa7a060c889 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsUpdateAction.java +++ b/duniter4j-es-assembly/src/main/java/org/duniter/elasticsearch/action/user/RestUserSettingsUpdateAction.java @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.action.user; * #L% */ -import org.duniter.elasticsearch.action.AbstractRestPostUpdateAction; -import org.duniter.elasticsearch.action.security.RestSecurityController; +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.UserService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/test/es-home/config/elasticsearch.yml b/duniter4j-es-assembly/src/test/es-home/config/elasticsearch.yml similarity index 98% rename from duniter4j-elasticsearch/src/test/es-home/config/elasticsearch.yml rename to duniter4j-es-assembly/src/test/es-home/config/elasticsearch.yml index fc10df3236a31e354731c5a630787aec4fecc1c4..828ddbd04543ef810f380ec8e0dbf229e6c2c48c 100644 --- a/duniter4j-elasticsearch/src/test/es-home/config/elasticsearch.yml +++ b/duniter4j-es-assembly/src/test/es-home/config/elasticsearch.yml @@ -100,7 +100,7 @@ http.cors.enabled: true # # Require explicit names when deleting indices: # -# action.destructive_requires_name: true +# rest.destructive_requires_name: true security.manager.enabled: false @@ -173,3 +173,6 @@ duniter.mail.admin: blavenie@EIS-DEV # Mail subject prefix # #duniter.mail.subject.prefix: [Duniter4j ES] + +duniter.changes.listenSource: */block +duniter.ws.port: 9400 diff --git a/duniter4j-elasticsearch/src/test/es-home/config/logging.yml b/duniter4j-es-assembly/src/test/es-home/config/logging.yml similarity index 98% rename from duniter4j-elasticsearch/src/test/es-home/config/logging.yml rename to duniter4j-es-assembly/src/test/es-home/config/logging.yml index 63c95362ee33c175973e0c3d2d82dd076b5d400e..15cfa3e195cb46a62c7536f118d1684acfcc2ecf 100644 --- a/duniter4j-elasticsearch/src/test/es-home/config/logging.yml +++ b/duniter4j-es-assembly/src/test/es-home/config/logging.yml @@ -2,7 +2,7 @@ es.logger.level: INFO rootLogger: ${es.logger.level}, console, file logger: - # log action execution errors for easier debugging + # log rest execution errors for easier debugging action: DEBUG # deprecation logging, turn to DEBUG to see them diff --git a/duniter4j-elasticsearch/LICENSE b/duniter4j-es-core/LICENSE similarity index 100% rename from duniter4j-elasticsearch/LICENSE rename to duniter4j-es-core/LICENSE diff --git a/duniter4j-es-core/pom.xml b/duniter4j-es-core/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..06cf545266c6bd9584ddb7c98df458c383ab5154 --- /dev/null +++ b/duniter4j-es-core/pom.xml @@ -0,0 +1,175 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.duniter</groupId> + <artifactId>duniter4j</artifactId> + <version>0.3.5-SNAPSHOT</version> + </parent> + + <groupId>org.duniter</groupId> + <artifactId>duniter4j-es-core</artifactId> + <packaging>jar</packaging> + <name>Duniter4j :: ElasticSearch Core plugin</name> + + <properties> + <!-- i18n configuration --> + <i18n.bundleOutputName>duniter4j-es-core-i18n</i18n.bundleOutputName> + <i18n.generateCsvFile>true</i18n.generateCsvFile> + <i18n.bundleCsvFile> + ${maven.gen.dir}/resources/META-INF/${i18n.bundleOutputName}.csv + </i18n.bundleCsvFile> + <config.i18nBundleName>${i18n.bundleOutputName}</config.i18nBundleName> + + </properties> + + <dependencies> + <dependency> + <groupId>org.duniter</groupId> + <artifactId>duniter4j-core-client</artifactId> + <version>${project.version}</version> + <exclusions> + <exclusion> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </exclusion> + </exclusions> + </dependency> + <!-- LOGGING DEPENDENCIES - SLF4J --> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <optional>true</optional> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <optional>true</optional> + <scope>runtime</scope> + </dependency> + + <!-- Elastic Search --> + <dependency> + <groupId>org.elasticsearch</groupId> + <artifactId>elasticsearch</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + + <!-- JNA (need for OS shutdown hook) --> + <dependency> + <groupId>net.java.dev.jna</groupId> + <artifactId>jna</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>net.java.dev.jna</groupId> + <artifactId>jna-platform</artifactId> + <exclusions> + <exclusion> + <groupId>net.java.dev.jna</groupId> + <artifactId>jna</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.glassfish.tyrus</groupId> + <artifactId>tyrus-server</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.tyrus</groupId> + <artifactId>tyrus-container-grizzly-server</artifactId> + </dependency> + + <!-- Unit test --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <resources> + <resource> + <directory>src/main/filtered-resources</directory> + <filtering>true</filtering> + <includes> + <include>*.config</include> + <include>**/*.properties</include> + </includes> + </resource> + <resource> + <directory>src/main/resources</directory> + <filtering>false</filtering> + </resource> + </resources> + + <plugins> + <plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>i18n-maven-plugin</artifactId> + + <executions> + <execution> + <id>scan-sources</id> + <configuration> + <entries> + <entry> + <specificGoal>parserValidation</specificGoal> + <basedir>${maven.src.dir}/main/java/</basedir> + <includes> + <param>**/**-validation.xml</param> + </includes> + </entry> + </entries> + </configuration> + <goals> + <goal>parserJava</goal> + <goal>parserValidation</goal> + <goal>gen</goal> + </goals> + </execution> + <execution> + <id>make-bundle</id> + <goals> + <goal>bundle</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>assembly-plugin</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + <configuration> + <attach>true</attach> + <appendAssemblyId>false</appendAssemblyId> + <finalName>${project.artifactId}-${project.version}</finalName> + <descriptors> + <descriptor> + ${basedir}/src/main/assembly/plugin.xml + </descriptor> + </descriptors> + <skipAssembly>${assembly.skip}</skipAssembly> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/duniter4j-elasticsearch/src/license/THIRD-PARTY.properties b/duniter4j-es-core/src/license/THIRD-PARTY.properties similarity index 100% rename from duniter4j-elasticsearch/src/license/THIRD-PARTY.properties rename to duniter4j-es-core/src/license/THIRD-PARTY.properties diff --git a/duniter4j-elasticsearch/src/main/assembly/plugin.xml b/duniter4j-es-core/src/main/assembly/plugin.xml similarity index 100% rename from duniter4j-elasticsearch/src/main/assembly/plugin.xml rename to duniter4j-es-core/src/main/assembly/plugin.xml diff --git a/duniter4j-elasticsearch/src/main/filtered-resources/duniter4j.config b/duniter4j-es-core/src/main/filtered-resources/duniter4j.config similarity index 100% rename from duniter4j-elasticsearch/src/main/filtered-resources/duniter4j.config rename to duniter4j-es-core/src/main/filtered-resources/duniter4j.config diff --git a/duniter4j-elasticsearch/src/main/filtered-resources/log4j.properties b/duniter4j-es-core/src/main/filtered-resources/log4j.properties similarity index 100% rename from duniter4j-elasticsearch/src/main/filtered-resources/log4j.properties rename to duniter4j-es-core/src/main/filtered-resources/log4j.properties diff --git a/duniter4j-elasticsearch/src/main/filtered-resources/plugin-descriptor.properties b/duniter4j-es-core/src/main/filtered-resources/plugin-descriptor.properties similarity index 66% rename from duniter4j-elasticsearch/src/main/filtered-resources/plugin-descriptor.properties rename to duniter4j-es-core/src/main/filtered-resources/plugin-descriptor.properties index 1ab4c6e915c42745c50d8b5ebb85818f1405a600..27e01fac493896713e0c2083c527bbb975cfba80 100644 --- a/duniter4j-elasticsearch/src/main/filtered-resources/plugin-descriptor.properties +++ b/duniter4j-es-core/src/main/filtered-resources/plugin-descriptor.properties @@ -1,5 +1,5 @@ -name=duniter4j-elasticsearch -description=Plugin for Duniter node indexation +name=duniter4j-es-core +description=Plugin for Duniter version=${project.version} site=false jvm=true diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/Plugin.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/Plugin.java similarity index 91% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/Plugin.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/Plugin.java index bb8715b414e778a14fb09800fae0276e65115a18..a64334b6cd4159657b5110c034b7aabae3f3e75b 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/Plugin.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/Plugin.java @@ -23,11 +23,11 @@ package org.duniter.elasticsearch; */ import com.google.common.collect.Lists; -import org.duniter.elasticsearch.action.RestModule; -import org.duniter.elasticsearch.node.DuniterNode; +import org.duniter.elasticsearch.rest.RestModule; import org.duniter.elasticsearch.security.SecurityModule; import org.duniter.elasticsearch.service.ServiceModule; import org.duniter.elasticsearch.threadpool.ThreadPool; +import org.duniter.elasticsearch.websocket.WebsocketModule; import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Module; @@ -65,10 +65,11 @@ public class Plugin extends org.elasticsearch.plugins.Plugin { return modules; } modules.add(new SecurityModule()); + + modules.add(new WebsocketModule()); modules.add(new RestModule()); + modules.add(new ServiceModule()); - // TODO : must be tested inside full release assembly - //modules.add(new ChangesModule()); return modules; } @@ -81,7 +82,7 @@ public class Plugin extends org.elasticsearch.plugins.Plugin { } components.add(PluginSettings.class); components.add(ThreadPool.class); - components.add(DuniterNode.class); + components.add(PluginInit.class); return components; } diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginInit.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginInit.java new file mode 100644 index 0000000000000000000000000000000000000000..110b3a0dabb3934ddd9ee0047baa832d09edd913 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginInit.java @@ -0,0 +1,132 @@ +package org.duniter.elasticsearch; + +/* + * #%L + * Duniter4j :: ElasticSearch Plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.core.client.model.elasticsearch.Currency; +import org.duniter.core.client.model.local.Peer; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.service.BlockchainService; +import org.duniter.elasticsearch.service.CurrencyService; +import org.duniter.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.health.ClusterHealthStatus; +import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.inject.Injector; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestRequest; + +/** + * Created by blavenie on 17/06/16. + */ +public class PluginInit extends AbstractLifecycleComponent<PluginInit> { + + private final PluginSettings pluginSettings; + private final ThreadPool threadPool; + private final Injector injector; + private final static ESLogger logger = Loggers.getLogger("duniter.core"); + private final Client client; + + @Inject + public PluginInit(Client client, Settings settings, PluginSettings pluginSettings, ThreadPool threadPool, final Injector injector) { + super(settings); + this.pluginSettings = pluginSettings; + this.threadPool = threadPool; + this.injector = injector; + this.client = client; + } + + @Override + protected void doStart() { + threadPool.scheduleOnClusterHealthStatus(() -> { + createIndices(); + + // Waiting cluster back to GREEN or YELLOW state, before synchronize + threadPool.scheduleOnClusterHealthStatus(() -> { + synchronize(); + }, ClusterHealthStatus.YELLOW, ClusterHealthStatus.GREEN); + }, ClusterHealthStatus.YELLOW, ClusterHealthStatus.GREEN); + } + + @Override + protected void doStop() { + + } + + @Override + protected void doClose() { + + } + + protected void createIndices() { + + boolean reloadIndices = pluginSettings.reloadIndices(); + + if (reloadIndices) { + if (logger.isInfoEnabled()) { + logger.info("Reloading all Duniter core indices..."); + } + + injector.getInstance(CurrencyService.class) + .deleteIndex() + .createIndexIfNotExists(); + + if (logger.isInfoEnabled()) { + logger.info("Reloading all Duniter indices... [OK]"); + } + } + else { + if (logger.isInfoEnabled()) { + logger.info("Checking Duniter core indices..."); + } + + injector.getInstance(CurrencyService.class).createIndexIfNotExists(); + + if (logger.isInfoEnabled()) { + logger.info("Checking Duniter core indices... [OK]"); + } + } + } + + protected void synchronize() { + if (pluginSettings.enableBlockchainSync()) { + + Peer peer = pluginSettings.checkAndGetPeer(); + + // Index (or refresh) node's currency + Currency currency = injector.getInstance(CurrencyService.class).indexCurrencyFromPeer(peer, true); + + // Add access to currency index + injector.getInstance(RestSecurityController.class).allowIndexType(RestRequest.Method.GET, + currency.getCurrencyName(), + BlockchainService.BLOCK_TYPE); + + // Index blocks (and listen if new block appear) + injector.getInstance(BlockchainService.class) + .indexLastBlocks(peer) + .listenAndIndexNewBlock(peer); + } + } +} diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/PluginSettings.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java similarity index 97% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/PluginSettings.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java index 265473a9daec1ccd72ca42c4a87bbb489b9b880a..5fe14728a5452d817f73001d2c62160c37e4891a 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/PluginSettings.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java @@ -60,7 +60,7 @@ import static org.nuiton.i18n.I18n.t; */ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> { - private Settings settings; + protected final Settings settings; /** * Delegate application config. @@ -269,6 +269,14 @@ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> { return settings.get("duniter.mail.subject.prefix", "[Duniter4j ES]"); } + public int getWebSocketPort() { + return settings.getAsInt("duniter.ws.port", 9200); + } + + public String getWebSocketHost() { + return settings.get("network.host", "locahost"); + } + /* protected methods */ protected void initI18n() throws IOException { @@ -301,6 +309,6 @@ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> { } protected String getI18nBundleName() { - return "duniter4j-elasticsearch-i18n"; + return "duniter4j-es-core-i18n"; } } diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/AccessDeniedException.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/AccessDeniedException.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/AccessDeniedException.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/AccessDeniedException.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/DuniterElasticsearchException.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/DuniterElasticsearchException.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/DuniterElasticsearchException.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/DuniterElasticsearchException.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/DuplicateIndexIdException.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/DuplicateIndexIdException.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/DuplicateIndexIdException.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/DuplicateIndexIdException.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/InvalidFormatException.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/InvalidFormatException.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/InvalidFormatException.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/InvalidFormatException.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/InvalidSignatureException.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/InvalidSignatureException.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/InvalidSignatureException.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/InvalidSignatureException.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/NodeConfigException.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/NodeConfigException.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/NodeConfigException.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/NodeConfigException.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/NotFoundException.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/NotFoundException.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/exception/NotFoundException.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/NotFoundException.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/model/Currency.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/model/Currency.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/model/Currency.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/model/Currency.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/model/SearchResult.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/model/SearchResult.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/model/SearchResult.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/model/SearchResult.java diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/AbstractRestPostIndexAction.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/AbstractRestPostIndexAction.java new file mode 100644 index 0000000000000000000000000000000000000000..c2a0f7fd4480d26f3d7f07a48bbfb0ef1bf9b558 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/AbstractRestPostIndexAction.java @@ -0,0 +1,83 @@ +package org.duniter.elasticsearch.rest; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.core.exception.BusinessException; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.exception.DuniterElasticsearchException; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.ESLoggerFactory; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.*; + +import static org.elasticsearch.rest.RestRequest.Method.GET; +import static org.elasticsearch.rest.RestRequest.Method.POST; +import static org.elasticsearch.rest.RestStatus.OK; + +public abstract class AbstractRestPostIndexAction extends BaseRestHandler { + + private static ESLogger log = null; + + private final JsonIndexer indexer; + + + public AbstractRestPostIndexAction(Settings settings, RestController controller, Client client, + RestSecurityController securityController, + String indexName, + String typeName, + JsonIndexer indexer) { + super(settings, controller, client); + controller.registerHandler(POST, + String.format("/%s/%s", indexName, typeName), + this); + securityController.allowIndexType(POST, indexName, typeName); + securityController.allowIndexType(GET, indexName, typeName); + log = ESLoggerFactory.getLogger(String.format("[%s]", indexName)); + this.indexer = indexer; + } + + @Override + protected void handleRequest(final RestRequest request, RestChannel restChannel, Client client) throws Exception { + + try { + String id = indexer.handleJson(request.content().toUtf8()); + restChannel.sendResponse(new BytesRestResponse(OK, id)); + } + catch(DuniterElasticsearchException | BusinessException e) { + log.error(e.getMessage(), e); + restChannel.sendResponse(new XContentThrowableRestResponse(request, e)); + } + catch(Exception e) { + log.error(e.getMessage(), e); + } + } + + + public interface JsonIndexer { + String handleJson(String json) throws DuniterElasticsearchException, BusinessException; + } + + + +} \ No newline at end of file diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/AbstractRestPostUpdateAction.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/AbstractRestPostUpdateAction.java new file mode 100644 index 0000000000000000000000000000000000000000..ede1eb5e089554e7b753d757cde0930305c8b76d --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/AbstractRestPostUpdateAction.java @@ -0,0 +1,82 @@ +package org.duniter.elasticsearch.rest; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.core.exception.BusinessException; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.exception.DuniterElasticsearchException; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.ESLoggerFactory; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.*; + +import static org.elasticsearch.rest.RestRequest.Method.POST; +import static org.elasticsearch.rest.RestStatus.OK; + +public abstract class AbstractRestPostUpdateAction extends BaseRestHandler { + + private static ESLogger log = null; + + private final JsonUpdater updater; + + + public AbstractRestPostUpdateAction(Settings settings, RestController controller, Client client, + RestSecurityController securityController, + String indexName, + String typeName, + JsonUpdater updater) { + super(settings, controller, client); + controller.registerHandler(POST, + String.format("/%s/%s/{id}/_update", indexName, typeName), + this); + securityController.allowIndexType(POST, indexName, typeName); + log = ESLoggerFactory.getLogger(String.format("[%s]", indexName)); + this.updater = updater; + } + + @Override + protected void handleRequest(final RestRequest request, RestChannel restChannel, Client client) throws Exception { + String id = request.param("id"); + + try { + updater.handleJson(request.content().toUtf8(), id); + restChannel.sendResponse(new BytesRestResponse(OK, id)); + } + catch(DuniterElasticsearchException | BusinessException e) { + log.error(e.getMessage(), e); + restChannel.sendResponse(new XContentThrowableRestResponse(request, e)); + } + catch(Exception e) { + log.error(e.getMessage(), e); + } + } + + + public interface JsonUpdater { + void handleJson(String json, String id) throws DuniterElasticsearchException, BusinessException; + } + + + +} \ No newline at end of file diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/RestModule.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/RestModule.java new file mode 100644 index 0000000000000000000000000000000000000000..68e8542ce6d8b2d58014bfab2f076b7b2e3ed7bc --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/RestModule.java @@ -0,0 +1,47 @@ +package org.duniter.elasticsearch.rest; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.currency.RestCurrencyIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityAuthAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.rest.security.RestSecurityFilter; +import org.duniter.elasticsearch.rest.security.RestSecurityGetChallengeAction; +import org.elasticsearch.common.inject.AbstractModule; +import org.elasticsearch.common.inject.Module; + +public class RestModule extends AbstractModule implements Module { + + @Override protected void configure() { + + // Currency + bind(RestCurrencyIndexAction.class).asEagerSingleton(); + + // Authentication & Security + bind(RestSecurityGetChallengeAction.class).asEagerSingleton(); + bind(RestSecurityAuthAction.class).asEagerSingleton(); + bind(RestSecurityFilter.class).asEagerSingleton(); + bind(RestSecurityController.class).asEagerSingleton(); + + } +} \ No newline at end of file diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/rest/RestXContentBuilder.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/RestXContentBuilder.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/rest/RestXContentBuilder.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/RestXContentBuilder.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/rest/XContentRestResponse.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/XContentRestResponse.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/rest/XContentRestResponse.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/XContentRestResponse.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/rest/XContentThrowableRestResponse.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/XContentThrowableRestResponse.java similarity index 95% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/rest/XContentThrowableRestResponse.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/XContentThrowableRestResponse.java index e78ac86d987ee2ffe6a986d9645f5fdfbfc02bb3..4f498f4040c007324b37408de064afad0a8c8ea7 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/rest/XContentThrowableRestResponse.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/XContentThrowableRestResponse.java @@ -31,7 +31,6 @@ import org.elasticsearch.rest.RestStatus; import java.io.IOException; import static org.elasticsearch.ExceptionsHelper.detailedMessage; -import static org.duniter.elasticsearch.rest.RestXContentBuilder.restContentBuilder; public class XContentThrowableRestResponse extends XContentRestResponse { @@ -44,7 +43,7 @@ public class XContentThrowableRestResponse extends XContentRestResponse { } private static XContentBuilder convert(RestRequest request, RestStatus status, Throwable t) throws IOException { - XContentBuilder builder = restContentBuilder(request).startObject() + XContentBuilder builder = RestXContentBuilder.restContentBuilder(request).startObject() .field("error", detailedMessage(t)) .field("status", status.getStatus()); if (t != null && request.paramAsBoolean("error_trace", false)) { diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/currency/RestCurrencyIndexAction.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/currency/RestCurrencyIndexAction.java similarity index 94% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/currency/RestCurrencyIndexAction.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/currency/RestCurrencyIndexAction.java index dea0ce5a284a49585b387695ac1710d66c946395..100df9ec37dc2acd44cdaf980ab314231dec8b52 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/currency/RestCurrencyIndexAction.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/currency/RestCurrencyIndexAction.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.action.currency; +package org.duniter.elasticsearch.rest.currency; /* * #%L @@ -30,7 +30,7 @@ import org.elasticsearch.rest.*; import static org.elasticsearch.rest.RestStatus.OK; /** - * A action to post a request to process a new currency/peer. + * A rest to post a request to process a new currency/peer. * * TODO : * - add security, to allow only request from admin (check signature against settings keyring) diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityAuthAction.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityAuthAction.java similarity index 98% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityAuthAction.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityAuthAction.java index 945e8dce38775f58a37b8100e9f5d891ad7cdd52..b62408e8ac76c878fc2fff7205f9498834cac25f 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityAuthAction.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityAuthAction.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.action.security; +package org.duniter.elasticsearch.rest.security; /* * #%L diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityController.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityController.java similarity index 98% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityController.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityController.java index 4c5dc3c4f9f9c816b9e983d0956164df66437660..b52fb2a56e1e498c5e950742dbbfa1306dd9e8b7 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityController.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityController.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.action.security; +package org.duniter.elasticsearch.rest.security; /* * #%L diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityFilter.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityFilter.java similarity index 97% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityFilter.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityFilter.java index 189a999b477498efecd28760eecf15a419915c1f..9b6557d0486e8344a118dcd4abb755c0ac7a917f 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityFilter.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityFilter.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.action.security; +package org.duniter.elasticsearch.rest.security; /* * #%L diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityGetChallengeAction.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityGetChallengeAction.java similarity index 97% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityGetChallengeAction.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityGetChallengeAction.java index 416b75ffac1d0601ee2a71a6341efa98e42467cc..9b27b92a95e39ba4b9bf4baacf33be58bae52bb4 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/action/security/RestSecurityGetChallengeAction.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/security/RestSecurityGetChallengeAction.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.action.security; +package org.duniter.elasticsearch.rest.security; /* * #%L diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/security/SecurityModule.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/security/SecurityModule.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/security/SecurityModule.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/security/SecurityModule.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/security/challenge/ChallengeMessageStore.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/security/challenge/ChallengeMessageStore.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/security/challenge/ChallengeMessageStore.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/security/challenge/ChallengeMessageStore.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/security/token/SecurityTokenStore.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/security/token/SecurityTokenStore.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/security/token/SecurityTokenStore.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/security/token/SecurityTokenStore.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/AbstractService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java similarity index 99% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/AbstractService.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java index f7cc988290f8642466b068a0b6c38b3971257b1b..89bc70ad9b1fedecded6ba0787b47a853f428a4a 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/AbstractService.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java @@ -30,7 +30,6 @@ import com.google.common.collect.ImmutableSet; import com.google.gson.JsonSyntaxException; import org.duniter.core.beans.Bean; import org.duniter.core.client.model.elasticsearch.Record; -import org.duniter.core.client.service.exception.HttpBadRequestException; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; import org.duniter.core.util.StringUtils; @@ -77,16 +76,6 @@ public abstract class AbstractService implements Bean { protected final int retryCount; protected final int retryWaitDuration; - public AbstractService(String loggerName, Client client, PluginSettings pluginSettings, CryptoService cryptoService) { - this.logger = Loggers.getLogger(loggerName); - this.client = client; - this.pluginSettings = pluginSettings; - this.cryptoService = cryptoService; - this.objectMapper = new ObjectMapper(); - this.retryCount = pluginSettings.getNodeRetryCount(); - this.retryWaitDuration = pluginSettings.getNodeRetryWaitDuration(); - } - public AbstractService(String loggerName, Client client, PluginSettings pluginSettings) { this(loggerName, client, pluginSettings, null); } @@ -99,6 +88,16 @@ public abstract class AbstractService implements Bean { this("duniter", client, pluginSettings, cryptoService); } + public AbstractService(String loggerName, Client client, PluginSettings pluginSettings, CryptoService cryptoService) { + this.logger = Loggers.getLogger(loggerName); + this.client = client; + this.pluginSettings = pluginSettings; + this.cryptoService = cryptoService; + this.objectMapper = new ObjectMapper(); + this.retryCount = pluginSettings.getNodeRetryCount(); + this.retryWaitDuration = pluginSettings.getNodeRetryWaitDuration(); + } + /* -- protected methods -- */ protected boolean existsIndex(String indexes) { @@ -124,6 +123,7 @@ public abstract class AbstractService implements Bean { JsonNode actualObj = readAndVerifyIssuerSignature(json); String issuer = getIssuer(actualObj); + if (logger.isDebugEnabled()) { logger.debug(String.format("Indexing a %s from issuer [%s]", type, issuer.substring(0, 8))); } @@ -132,7 +132,8 @@ public abstract class AbstractService implements Bean { .setSource(json) .setRefresh(false) .execute().actionGet(); - return response.getId(); + String id = response.getId(); + return id; } protected void checkIssuerAndUpdateDocumentFromJson(String index, String type, String json, String id) { diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/synchro/SynchroService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractSynchroService.java similarity index 78% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/synchro/SynchroService.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractSynchroService.java index b95cb1ac609280725e8fd195f68e81ac0ef5a72b..57c9561f6f0469f972f9958b6fbab971d988b7ad 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/synchro/SynchroService.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractSynchroService.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.service.synchro; +package org.duniter.elasticsearch.service; /* * #%L @@ -35,7 +35,8 @@ import org.duniter.core.service.CryptoService; import org.duniter.core.util.StringUtils; import org.duniter.elasticsearch.PluginSettings; import org.duniter.elasticsearch.exception.InvalidFormatException; -import org.duniter.elasticsearch.service.*; +import org.duniter.elasticsearch.service.AbstractService; +import org.duniter.elasticsearch.service.ServiceLocator; import org.duniter.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.action.bulk.BulkItemResponse; import org.elasticsearch.action.bulk.BulkRequestBuilder; @@ -57,66 +58,32 @@ import java.util.Set; /** * Created by blavenie on 27/10/16. */ -public class SynchroService extends AbstractService { +public abstract class AbstractSynchroService extends AbstractService { protected HttpService httpService; @Inject - public SynchroService(Client client, PluginSettings settings, CryptoService cryptoService, - ThreadPool threadPool, final ServiceLocator serviceLocator) { + public AbstractSynchroService(Client client, PluginSettings settings, CryptoService cryptoService, + ThreadPool threadPool, final ServiceLocator serviceLocator) { super("duniter.network.p2p", client, settings,cryptoService); threadPool.scheduleOnStarted(() -> { httpService = serviceLocator.getHttpService(); }); } - public void synchronize() { - logger.info("Synchronizing data..."); + /* -- protected methods -- */ + protected Peer getPeerFromAPI(String filterApiName) { // TODO : get peers from currency - use peering BMA API, and select peers with ESA (ES API) Peer peer = new Peer(pluginSettings.getDataSyncHost(), pluginSettings.getDataSyncPort()); - - synchronize(peer); - } - - public void synchronize(Peer peer) { - - long sinceTime = 0; // ToDO: get last sync time from somewhere ? (e.g. a specific index) - - logger.info(String.format("[%s] Synchronizing data since %s...", peer.toString(), sinceTime)); - - importMarketChanges(peer, sinceTime); - importRegistryChanges(peer, sinceTime); - importUserChanges(peer, sinceTime); - importMessageChanges(peer, sinceTime); - - logger.info(String.format("[%s] Synchronizing data since %s [OK]", peer.toString(), sinceTime)); - } - - public void importMarketChanges(Peer peer, long sinceTime) { - importChanges(peer, MarketService.INDEX, MarketService.RECORD_TYPE, sinceTime); - importChanges(peer, MarketService.INDEX, MarketService.RECORD_COMMENT_TYPE, sinceTime); - } - - public void importRegistryChanges(Peer peer, long sinceTime) { - importChanges(peer, RegistryService.INDEX, RegistryService.RECORD_TYPE, sinceTime); - importChanges(peer, RegistryService.INDEX, RegistryService.RECORD_COMMENT_TYPE, sinceTime); - } - - public void importUserChanges(Peer peer, long sinceTime) { - importChanges(peer, UserService.INDEX, UserService.PROFILE_TYPE, sinceTime); - importChanges(peer, UserService.INDEX, UserService.SETTINGS_TYPE, sinceTime); - } - - public void importMessageChanges(Peer peer, long sinceTime) { - importChanges(peer, MessageService.INDEX, MessageService.RECORD_TYPE, sinceTime); + return peer; } - public void importChanges(Peer peer, String index, String type, long sinceTime) { + protected void importChanges(Peer peer, String index, String type, long sinceTime) { importChanges(peer, index, type, Record.PROPERTY_ISSUER, Record.PROPERTY_TIME, sinceTime); } - public void importChanges(Peer peer, String index, String type, String issuerFieldName, String versionFieldName, long sinceTime) { + protected void importChanges(Peer peer, String index, String type, String issuerFieldName, String versionFieldName, long sinceTime) { // Create the search query BytesStreamOutput bos; @@ -125,28 +92,28 @@ public class SynchroService extends AbstractService { XContentBuilder builder = new XContentBuilder(JsonXContent.jsonXContent, bos); builder.startObject() .startObject("query") - // bool.should - .startObject("bool") - .startObject("should") - // time > sinceDate - .startObject("range") - .startObject("time") - .field("gte", sinceTime) - .endObject() - .endObject() - .endObject() - // currency + // bool.should + .startObject("bool") + .startObject("should") + // time > sinceDate + .startObject("range") + .startObject("time") + .field("gte", sinceTime) + .endObject() + .endObject() + .endObject() + // currency /*.startObject("filter") .startObject("term") .field("currency", "sou") // todo, filter on configured currency only .endObject() .endObject()*/ - .endObject() + .endObject() // end: query .endObject() .field("from", 0) // todo .field("size", 100) // todo - .endObject(); + .endObject(); builder.flush(); } catch(IOException e) { @@ -237,7 +204,7 @@ public class SynchroService extends AbstractService { logger.trace(String.format("[%s] [%s/%s] update _id=%s\n%s", peer, hitIndex, hitType, id, json)); } bulkRequest.add(client.prepareIndex(hitIndex, hitType, id) - .setSource(json.getBytes())); + .setSource(json.getBytes())); } } @@ -283,8 +250,4 @@ public class SynchroService extends AbstractService { IOUtils.closeQuietly(response); } } - - /* -- protected methods -- */ - - } diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java similarity index 99% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java index 0786e89ee1b92c00f4d74abecc3d72a7c15e633c..77fee625b794822dca61a20966abad991721f725 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java @@ -36,7 +36,6 @@ import org.duniter.core.client.model.local.Peer; import org.duniter.core.client.service.bma.BlockchainRemoteService; import org.duniter.core.client.service.bma.NetworkRemoteService; import org.duniter.core.client.service.exception.BlockNotFoundException; -import org.duniter.core.client.service.exception.HttpBadRequestException; import org.duniter.core.client.service.exception.JsonSyntaxException; import org.duniter.core.exception.TechnicalException; import org.duniter.core.model.NullProgressionModel; @@ -65,7 +64,6 @@ import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHitField; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.metrics.max.Max; @@ -89,7 +87,7 @@ public class BlockchainService extends AbstractService { private final ProgressionModel nullProgressionModel = new NullProgressionModel(); private BlockchainRemoteService blockchainRemoteService; - private RegistryService registryService; + private CurrencyService currencyService; private ThreadPool threadPool; private JsonAttributeParser blockNumberParser = new JsonAttributeParser("number"); @@ -111,8 +109,8 @@ public class BlockchainService extends AbstractService { } @Inject - public void setRegistryService(RegistryService registryService) { - this.registryService = registryService; + public void setCurrencyService(CurrencyService currencyService) { + this.currencyService = currencyService; } public BlockchainService listenAndIndexNewBlock(Peer peer){ @@ -148,8 +146,8 @@ public class BlockchainService extends AbstractService { logger.info(I18n.t("duniter4j.blockIndexerService.indexLastBlocks.task", currencyName, peer)); // Create index blockchain if need - if (!registryService.isCurrencyExists(currencyName)) { - registryService.indexCurrencyFromPeer(peer); + if (!currencyService.isCurrencyExists(currencyName)) { + currencyService.indexCurrencyFromPeer(peer); } // Check if index exists diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/RegistryService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/CurrencyService.java similarity index 58% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/RegistryService.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/CurrencyService.java index 8d8da25177cc2d0a1c2da66810fb41d396977ede..0cd8989c80137616204dc1696ea8a4b642bbfdd4 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/RegistryService.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/CurrencyService.java @@ -41,13 +41,11 @@ import org.duniter.core.service.CryptoService; import org.duniter.core.util.ObjectUtils; import org.duniter.core.util.StringUtils; import org.duniter.elasticsearch.PluginSettings; -import org.duniter.elasticsearch.action.security.RestSecurityController; import org.duniter.elasticsearch.exception.AccessDeniedException; import org.duniter.elasticsearch.exception.DuplicateIndexIdException; import org.duniter.elasticsearch.exception.InvalidSignatureException; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; @@ -60,7 +58,6 @@ import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHitField; -import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.List; @@ -69,24 +66,20 @@ import java.util.Objects; /** * Created by Benoit on 30/03/2015. */ -public class RegistryService extends AbstractService { +public class CurrencyService extends AbstractService { - public static final String INDEX = "registry"; - public static final String RECORD_TYPE = "record"; - public static final String RECORD_CATEGORY_TYPE = "category"; - public static final String RECORD_COMMENT_TYPE = "comment"; - public static final String CURRENCY_TYPE = "currency"; - private static final String CATEGORIES_BULK_CLASSPATH_FILE = "registry-categories-bulk-insert.json"; + public static final String INDEX = "currency"; + public static final String CURRENCY_TYPE = "record"; private final Gson gson; private BlockchainRemoteService blockchainRemoteService; @Inject - public RegistryService(Client client, + public CurrencyService(Client client, PluginSettings settings, CryptoService cryptoService, BlockchainRemoteService blockchainRemoteService) { - super("gchange." + INDEX, client, settings, cryptoService); + super("duniter." + INDEX, client, settings, cryptoService); this.gson = GsonUtils.newBuilder().create(); this.blockchainRemoteService = blockchainRemoteService; } @@ -94,12 +87,10 @@ public class RegistryService extends AbstractService { /** * Create index need for blockchain registry, if need */ - public RegistryService createIndexIfNotExists() { + public CurrencyService createIndexIfNotExists() { try { if (!existsIndex(INDEX)) { createIndex(); - - fillRecordCategories(); } } catch(JsonProcessingException e) { @@ -112,7 +103,7 @@ public class RegistryService extends AbstractService { * Create index for registry * @throws JsonProcessingException */ - public RegistryService createIndex() throws JsonProcessingException { + public CurrencyService createIndex() throws JsonProcessingException { logger.info(String.format("Creating index [%s]", INDEX)); CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(INDEX); @@ -123,15 +114,12 @@ public class RegistryService extends AbstractService { .build(); createIndexRequestBuilder.setSettings(indexSettings); createIndexRequestBuilder.addMapping(CURRENCY_TYPE, createCurrencyType()); - createIndexRequestBuilder.addMapping(RECORD_CATEGORY_TYPE, createRecordCategoryType()); - createIndexRequestBuilder.addMapping(RECORD_TYPE, createRecordType()); - createIndexRequestBuilder.addMapping(RECORD_COMMENT_TYPE, createRecordCommentType(INDEX, RECORD_COMMENT_TYPE)); createIndexRequestBuilder.execute().actionGet(); return this; } - public RegistryService deleteIndex() { + public CurrencyService deleteIndex() { deleteIndexIfExists(INDEX); return this; } @@ -140,50 +128,11 @@ public class RegistryService extends AbstractService { return super.existsIndex(INDEX); } - public RegistryService fillRecordCategories() { - if (logger.isDebugEnabled()) { - logger.debug(String.format("[%s/%s] Fill data", INDEX, RECORD_CATEGORY_TYPE)); - } - - // Insert categories - bulkFromClasspathFile(CATEGORIES_BULK_CLASSPATH_FILE, INDEX, RECORD_CATEGORY_TYPE, - // Add order attribute - new AddSequenceAttributeHandler("order", "\\{.*\"name\".*\\}", 1)); - - return this; - } - public boolean isCurrencyExists(String currencyName) { String pubkey = getSenderPubkeyByCurrencyId(currencyName); return !StringUtils.isEmpty(pubkey); } - public String indexRecordFromJson(String json) { - return checkIssuerAndIndexDocumentFromJson(INDEX, RECORD_TYPE, json); - } - - public void updateRecordFromJson(String json, String id) { - checkIssuerAndUpdateDocumentFromJson(INDEX, RECORD_TYPE, json, id); - } - - public String indexCommentFromJson(String json) { - return checkIssuerAndIndexDocumentFromJson(INDEX, RECORD_COMMENT_TYPE, json); - } - - public void updateCommentFromJson(String json, String id) { - checkIssuerAndUpdateDocumentFromJson(INDEX, RECORD_COMMENT_TYPE, json, id); - } - - public void insertRecordFromBulkFile(File bulkFile) { - - if (logger.isDebugEnabled()) { - logger.debug("Inserting records from file"); - } - - // Insert cities - bulkFromFile(bulkFile, INDEX, RECORD_TYPE); - } - /** * Retrieve the blockchain data, from peer * @@ -369,195 +318,6 @@ public class RegistryService extends AbstractService { /* -- Internal methods -- */ - - public XContentBuilder createRecordType() { - String stringAnalyzer = pluginSettings.getDefaultStringAnalyzer(); - - try { - XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(RECORD_TYPE) - .startObject("properties") - - // title - .startObject("title") - .field("type", "string") - .field("analyzer", stringAnalyzer) - .endObject() - - // description - .startObject("description") - .field("type", "string") - .field("analyzer", stringAnalyzer) - .endObject() - - // creationTime - .startObject("creationTime") - .field("type", "integer") - .endObject() - - // time - .startObject("time") - .field("type", "integer") - .endObject() - - // issuer - .startObject("issuer") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // pubkey - .startObject("pubkey") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // address - .startObject("address") - .field("type", "string") - .field("analyzer", stringAnalyzer) - .endObject() - - // city - .startObject("city") - .field("type", "string") - .endObject() - - // geoPoint - .startObject("geoPoint") - .field("type", "geo_point") - .endObject() - - // thumbnail - .startObject("thumbnail") - .field("type", "attachment") - .startObject("fields") // src - .startObject("content") // title - .field("index", "no") - .endObject() - .startObject("title") // title - .field("type", "string") - .field("store", "no") - .endObject() - .startObject("author") // title - .field("store", "no") - .endObject() - .startObject("content_type") // title - .field("store", "yes") - .endObject() - .endObject() - .endObject() - - // pictures - .startObject("pictures") - .field("type", "nested") - .field("dynamic", "false") - .startObject("properties") - .startObject("file") // file - .field("type", "attachment") - .startObject("fields") - .startObject("content") // content - .field("index", "no") - .endObject() - .startObject("title") // title - .field("type", "string") - .field("store", "yes") - .field("analyzer", stringAnalyzer) - .endObject() - .startObject("author") // author - .field("type", "string") - .field("store", "no") - .endObject() - .startObject("content_type") // content_type - .field("store", "yes") - .endObject() - .endObject() - .endObject() - .endObject() - .endObject() - - // picturesCount - .startObject("picturesCount") - .field("type", "integer") - .endObject() - - // category - .startObject("category") - .field("type", "nested") - .field("dynamic", "false") - .startObject("properties") - .startObject("id") // id - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - .startObject("parent") // parent - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - .startObject("name") // name - .field("type", "string") - .field("analyzer", stringAnalyzer) - .endObject() - .endObject() - .endObject() - - // tags - .startObject("tags") - .field("type", "completion") - .field("search_analyzer", "simple") - .field("analyzer", "simple") - .field("preserve_separators", "false") - .endObject() - - .endObject() - .endObject().endObject(); - - return mapping; - } - catch(IOException ioe) { - throw new TechnicalException(String.format("Error while getting mapping for index [%s/%s]: %s", INDEX, RECORD_TYPE, ioe.getMessage()), ioe); - } - } - - public XContentBuilder createRecordCategoryType() { - try { - XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(RECORD_CATEGORY_TYPE) - .startObject("properties") - - // name - .startObject("name") - .field("type", "string") - .field("analyzer", pluginSettings.getDefaultStringAnalyzer()) - .endObject() - - // description - /*.startObject("description") - .field("type", "string") - .endObject()*/ - - // parent - .startObject("parent") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // tags - /*.startObject("tags") - .field("type", "completion") - .field("search_analyzer", "simple") - .field("analyzer", "simple") - .field("preserve_separators", "false") - .endObject()*/ - - .endObject() - .endObject().endObject(); - - return mapping; - } - catch(IOException ioe) { - throw new TechnicalException(String.format("Error while getting mapping for index [%s/%s]: %s", INDEX, RECORD_CATEGORY_TYPE, ioe.getMessage()), ioe); - } - } - public XContentBuilder createCurrencyType() { try { XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(CURRENCY_TYPE) @@ -591,30 +351,6 @@ public class RegistryService extends AbstractService { } } - /** - * - * @param jsonCategory - * @return the product id - */ - public String indexCategoryFromJson(String jsonCategory) { - if (logger.isDebugEnabled()) { - logger.debug("Indexing a category"); - } - - // Preparing indexBlocksFromNode - IndexRequestBuilder indexRequest = client.prepareIndex(INDEX, RECORD_CATEGORY_TYPE) - .setSource(jsonCategory); - - // Execute indexBlocksFromNode - IndexResponse response = indexRequest - .setRefresh(false) - .execute().actionGet(); - - return response.getId(); - } - - - /** * Retrieve a blockchain from its name * @param currencyId diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java similarity index 97% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java index d2b283fdd496814605ca1b1fa9990c45b6c08e64..98d46240d912a81aee7fcd89f905d6cf9d45032f 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java @@ -59,10 +59,10 @@ public class ServiceLocator private static BeanFactory beanFactory = null; @Inject - public ServiceLocator(Injector injector) { + public ServiceLocator() { super(); if (logger.isDebugEnabled()) { - logger.debug("Starting Duniter4j client ServiceLocator..."); + logger.debug("Starting Duniter4j ServiceLocator..."); } setBeanFactory(getOrCreateBeanFactory()); diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/ServiceModule.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceModule.java similarity index 70% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/ServiceModule.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceModule.java index 00890a8cfadc92ca62fb24747a88fe2dfda8f2be..9612444ee283c20f76bf5e3f4c609ff6c48533fa 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/ServiceModule.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceModule.java @@ -34,9 +34,9 @@ import org.duniter.core.client.service.bma.WotRemoteService; import org.duniter.core.client.service.local.CurrencyService; import org.duniter.core.client.service.local.PeerService; import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.PluginInit; import org.duniter.elasticsearch.PluginSettings; -import org.duniter.elasticsearch.service.event.EventService; -import org.duniter.elasticsearch.service.synchro.SynchroService; +import org.duniter.elasticsearch.service.changes.ChangeService; import org.elasticsearch.common.inject.AbstractModule; import org.elasticsearch.common.inject.Module; @@ -45,19 +45,13 @@ public class ServiceModule extends AbstractModule implements Module { @Override protected void configure() { bind(ServiceLocator.class).asEagerSingleton(); - // ES common service + // common services bind(PluginSettings.class).asEagerSingleton(); - bind(EventService.class).asEagerSingleton(); + bind(PluginInit.class).asEagerSingleton(); + bind(ChangeService.class).asEagerSingleton(); - // ES indexation services - bind(RegistryService.class).asEagerSingleton(); - bind(MarketService.class).asEagerSingleton(); + // indexation services bind(BlockchainService.class).asEagerSingleton(); - bind(MessageService.class).asEagerSingleton(); - bind(HistoryService.class).asEagerSingleton(); - - // ES Synchro services - bind(SynchroService.class).asEagerSingleton(); // Duniter Client API beans bindWithLocator(BlockchainRemoteService.class); @@ -74,19 +68,6 @@ public class ServiceModule extends AbstractModule implements Module { // Duniter Shared API beans bindWithLocator(CryptoService.class); bindWithLocator(org.duniter.core.service.MailService.class); - -/* - bindWithLocator(BlockchainRemoteServiceImpl.class); - bindWithLocator(NetworkRemoteServiceImpl.class); - bindWithLocator(WotRemoteServiceImpl.class); - bindWithLocator(TransactionRemoteServiceImpl.class); - bindWithLocator(Ed25519CryptoServiceImpl.class); - bindWithLocator(PeerServiceImpl.class); - bindWithLocator(CurrencyServiceImpl.class); - bindWithLocator(HttpServiceImpl.class); - bindWithLocator(MemoryCurrencyDaoImpl.class); - bindWithLocator(MemoryPeerDaoImpl.class); - bindWithLocator(DataContext.class);*/ } /* protected methods */ diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangeEvent.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeEvent.java similarity index 88% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangeEvent.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeEvent.java index f1dde61423642fa6ee1ec65076b3fea5a9555cdc..99d6fd7a0963fb770138e103d48e9bb3dc6661cc 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangeEvent.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeEvent.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.websocket; +package org.duniter.elasticsearch.service.changes; /* * #%L @@ -43,6 +43,7 @@ import org.joda.time.DateTime; public class ChangeEvent { private final String id; + private final String index; private final String type; private final DateTime timestamp; private final Operation operation; @@ -53,8 +54,9 @@ public class ChangeEvent { INDEX,CREATE,DELETE } - public ChangeEvent(String id, String type, DateTime timestamp, Operation operation, long version, BytesReference source) { + public ChangeEvent(String index, String type, String id, DateTime timestamp, Operation operation, long version, BytesReference source) { this.id = id; + this.index = index; this.type = type; this.timestamp = timestamp; this.operation = operation; @@ -74,6 +76,10 @@ public class ChangeEvent { return timestamp; } + public String getIndex() { + return index; + } + public String getType() { return type; } @@ -85,4 +91,6 @@ public class ChangeEvent { public BytesReference getSource() { return source; } + + } diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeListener.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeListener.java new file mode 100644 index 0000000000000000000000000000000000000000..af8ba0918419f329a85acb46592e8756c245dbec --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeListener.java @@ -0,0 +1,6 @@ +package org.duniter.elasticsearch.service.changes; + +public interface ChangeListener { + String getId(); + void onChanges(String message); +} \ No newline at end of file diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangeRegister.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeService.java similarity index 75% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangeRegister.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeService.java index 4fbeba359826c4ba80d63cc5e24cd28c52ae9035..56487c3846fcf2a609be0120b5ed919039faeded 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangeRegister.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeService.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.websocket; +package org.duniter.elasticsearch.service.changes; /* * #%L @@ -50,63 +50,32 @@ import org.elasticsearch.index.indexing.IndexingOperationListener; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.indices.IndicesLifecycle; import org.elasticsearch.indices.IndicesService; -import org.glassfish.tyrus.server.Server; import org.joda.time.DateTime; -import javax.websocket.DeploymentException; import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -public class ChangeRegister { +public class ChangeService { - private static final String SETTING_PRIMARY_SHARD_ONLY = "changes.primaryShardOnly"; - private static final String SETTING_PORT = "changes.port"; - private static final String SETTING_LISTEN_SOURCE = "changes.listenSource"; + private static final String SETTING_PRIMARY_SHARD_ONLY = "duniter.changes.primaryShardOnly"; + private static final String SETTING_LISTEN_SOURCE = "duniter.changes.listenSource"; - private final ESLogger log = Loggers.getLogger(ChangeRegister.class); + private final ESLogger log = Loggers.getLogger(ChangeService.class); - private static final Map<String, WebSocketServerEndPoint> LISTENERS = new HashMap<String, WebSocketServerEndPoint>(); + private static final Map<String, ChangeListener> LISTENERS = new HashMap<>(); @Inject - public ChangeRegister(final Settings settings, IndicesService indicesService) { + public ChangeService(final Settings settings, IndicesService indicesService) { final boolean allShards = !settings.getAsBoolean(SETTING_PRIMARY_SHARD_ONLY, Boolean.FALSE); - final int port = settings.getAsInt(SETTING_PORT, 9400); final String[] sourcesStr = settings.getAsArray(SETTING_LISTEN_SOURCE, new String[]{"*"}); final Set<ChangeSource> sources = new HashSet<>(); for(String sourceStr : sourcesStr) { sources.add(new ChangeSource(sourceStr)); } - final Server server = new Server("localhost", port, "/ws", null, WebSocketServerEndPoint.class) ; - - try { - log.info("Starting WebSocketServerEndPoint server"); - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Object run() { - try { - // Tyrus tries to load the server code using reflection. In Elasticsearch 2.x Java - // security manager is used which breaks the reflection code as it can't find the class. - // This is a workaround for that - Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - server.start(); - return null; - } catch (DeploymentException e) { - throw new RuntimeException("Failed to start server", e); - } - } - }); - log.info("WebSocketServerEndPoint server started"); - } catch (Exception e) { - log.error("Failed to start WebSocketServerEndPoint server",e); - throw new RuntimeException(e); - } - indicesService.indicesLifecycle().addListener(new IndicesLifecycle.Listener() { @Override public void afterIndexShardStarted(IndexShard indexShard) { @@ -117,8 +86,9 @@ public class ChangeRegister { @Override public void postCreate(Engine.Create create) { ChangeEvent change=new ChangeEvent( - create.id(), + indexName, create.type(), + create.id(), new DateTime(), ChangeEvent.Operation.CREATE, create.version(), @@ -131,8 +101,9 @@ public class ChangeRegister { @Override public void postDelete(Engine.Delete delete) { ChangeEvent change=new ChangeEvent( - delete.id(), + indexName, delete.type(), + delete.id(), new DateTime(), ChangeEvent.Operation.DELETE, delete.version(), @@ -146,8 +117,9 @@ public class ChangeRegister { public void postIndex(Engine.Index index) { ChangeEvent change=new ChangeEvent( - index.id(), + indexName, index.type(), + index.id(), new DateTime(), ChangeEvent.Operation.INDEX, index.version(), @@ -205,17 +177,15 @@ public class ChangeRegister { } builder.endObject(); - - message = builder.string(); } catch (IOException e) { log.error("Failed to write JSON", e); return; } - for (WebSocketServerEndPoint listener : LISTENERS.values()) { + for (ChangeListener listener : LISTENERS.values()) { try { - listener.sendMessage(message); + listener.onChanges(message); } catch (Exception e) { log.error("Failed to send message", e); } @@ -230,11 +200,13 @@ public class ChangeRegister { }); } - public static void registerListener(WebSocketServerEndPoint webSocket) { - LISTENERS.put(webSocket.getId(), webSocket); + public static void registerListener(ChangeListener listener) { + LISTENERS.put(listener.getId(), listener); } - public static void unregisterListener(WebSocketServerEndPoint webSocket) { - LISTENERS.remove(webSocket.getId()); + public static void unregisterListener(ChangeListener listener) { + LISTENERS.remove(listener.getId()); } + + } diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangeSource.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeSource.java similarity index 97% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangeSource.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeSource.java index 9cf7a1793d2e7cdc051e12e550ebdf162849590a..6f79a5076ffaa77d7dee8a0211c3bad1aabd76b7 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangeSource.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeSource.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.websocket; +package org.duniter.elasticsearch.service.changes; /* * #%L diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeUtils.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..cfe6a16c9e79010ffbd69ca061402b36b08bf5a7 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/changes/ChangeUtils.java @@ -0,0 +1,65 @@ +package org.duniter.elasticsearch.service.changes; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.JsonSyntaxException; +import org.duniter.core.exception.TechnicalException; +import org.duniter.elasticsearch.exception.InvalidFormatException; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.joda.time.DateTime; + +import java.io.IOException; + +/** + * Created by blavenie on 30/11/16. + */ +public class ChangeUtils { + + public static ChangeEvent fromJson(ObjectMapper objectMapper, String json) { + try { + JsonNode actualObj = objectMapper.readTree(json); + String index = actualObj.get("_index").asText(); + String type = actualObj.get("_type").asText(); + String id = actualObj.get("_id").asText(); + DateTime timestamp = new DateTime(actualObj.get("_timestamp").asLong()); + ChangeEvent.Operation operation = ChangeEvent.Operation.valueOf(actualObj.get("_operation").asText()); + long version = actualObj.get("_version").asLong(); + + JsonNode sourceNode = actualObj.get("_source"); + BytesReference source = null; + if (sourceNode != null) { + // TODO : fill bytes reference from source + //source = sourceNode. + } + + ChangeEvent event = new ChangeEvent(index, type, id, timestamp, operation, version, source); + return event; + } catch (IOException | JsonSyntaxException e) { + throw new InvalidFormatException("Invalid record JSON: " + e.getMessage(), e); + } + } + + public static String toJson(ChangeEvent change) { + try { + XContentBuilder builder = new XContentBuilder(JsonXContent.jsonXContent, new BytesStreamOutput()); + builder.startObject() + .field("_index", change.getIndex()) + .field("_type", change.getType()) + .field("_id", change.getId()) + .field("_timestamp", change.getTimestamp()) + .field("_version", change.getVersion()) + .field("_operation", change.getOperation().toString()); + if (change.getSource() != null) { + builder.rawField("_source", change.getSource()); + } + builder.endObject(); + + return builder.string(); + } catch (IOException e) { + throw new TechnicalException("Error while generating JSON from change event", e); + } + } +} diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/threadpool/ThreadPool.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/threadpool/ThreadPool.java similarity index 94% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/threadpool/ThreadPool.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/threadpool/ThreadPool.java index b024cbc0d1e1dffe5ff41e47597f77044f1785d1..85944904ffe00ef687f6f7c1c1ab20da972f185e 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/threadpool/ThreadPool.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/threadpool/ThreadPool.java @@ -98,9 +98,9 @@ public class ThreadPool extends AbstractLifecycleComponent<ThreadPool> { } /** - * Schedules an action when node is started (all services and modules ready) + * Schedules an rest when node is started (all services and modules ready) * - * @param job the action to execute when node started + * @param job the rest to execute when node started * @return a ScheduledFuture who's get will return when the task is complete and throw an exception if it is canceled */ public void scheduleOnStarted(Runnable job) { @@ -109,9 +109,9 @@ public class ThreadPool extends AbstractLifecycleComponent<ThreadPool> { } /** - * Schedules an action when cluster is ready + * Schedules an rest when cluster is ready * - * @param job the action to execute + * @param job the rest to execute * @param expectedStatus expected health status, to run the job * @return a ScheduledFuture who's get will return when the task is complete and throw an exception if it is canceled */ @@ -127,9 +127,9 @@ public class ThreadPool extends AbstractLifecycleComponent<ThreadPool> { } /** - * Schedules an action that runs on the scheduler thread, after a delay. + * Schedules an rest that runs on the scheduler thread, after a delay. * - * @param command the action to take + * @param command the rest to take * @param interval the delay interval * @return a ScheduledFuture who's get will return when the task is complete and throw an exception if it is canceled */ @@ -138,9 +138,9 @@ public class ThreadPool extends AbstractLifecycleComponent<ThreadPool> { } /** - * Schedules a periodic action that always runs on the scheduler thread. + * Schedules a periodic rest that always runs on the scheduler thread. * - * @param command the action to take + * @param command the rest to take * @param interval the delay interval * @return a ScheduledFuture who's get will return when the task is complete and throw an exception if it is canceled */ diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/Desktop.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/Desktop.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/Desktop.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/Desktop.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/DesktopPower.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/DesktopPower.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/DesktopPower.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/DesktopPower.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/WindowsPower.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/WindowsPower.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/WindowsPower.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/WindowsPower.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/handle/CWPSSTRUCT.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/handle/CWPSSTRUCT.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/handle/CWPSSTRUCT.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/handle/CWPSSTRUCT.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/handle/HANDLER_ROUTINE.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/handle/HANDLER_ROUTINE.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/handle/HANDLER_ROUTINE.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/handle/HANDLER_ROUTINE.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/handle/WNDPROC.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/handle/WNDPROC.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/handle/WNDPROC.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/handle/WNDPROC.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/libs/Kernel32Ex.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/libs/Kernel32Ex.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/libs/Kernel32Ex.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/libs/Kernel32Ex.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/wrap/GetLastErrorException.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/wrap/GetLastErrorException.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/wrap/GetLastErrorException.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/wrap/GetLastErrorException.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/wrap/WNDCLASSEXWrap.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/wrap/WNDCLASSEXWrap.java similarity index 100% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/util/os/win/wrap/WNDCLASSEXWrap.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/util/os/win/wrap/WNDCLASSEXWrap.java diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/WebSocketServerEndPoint.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketChangeEndPoint.java similarity index 84% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/WebSocketServerEndPoint.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketChangeEndPoint.java index eb6b2e90d64ec6b28f6c19a21defdd6695d46d32..23e983af34e1c6a1d2e1088d11a2c4bf1fc9bdb7 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/WebSocketServerEndPoint.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketChangeEndPoint.java @@ -38,6 +38,8 @@ package org.duniter.elasticsearch.websocket; limitations under the License. */ +import org.duniter.elasticsearch.service.changes.ChangeListener; +import org.duniter.elasticsearch.service.changes.ChangeService; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.Loggers; @@ -45,22 +47,24 @@ import javax.websocket.*; import javax.websocket.server.ServerEndpoint; @ServerEndpoint(value = "/_changes") -public class WebSocketServerEndPoint { +public class WebsocketChangeEndPoint implements ChangeListener{ - private final ESLogger log = Loggers.getLogger(WebSocketServerEndPoint.class); + private final ESLogger log = Loggers.getLogger(WebsocketChangeEndPoint.class); private Session session; @OnOpen public void onOpen(Session session) { log.info("Connected ... " + session.getId()); this.session = session; - ChangeRegister.registerListener(this); + ChangeService.registerListener(this); } - public void sendMessage(String message) { + @Override + public void onChanges(String message) { session.getAsyncRemote().sendText(message); } + @Override public String getId() { return session == null ? null : session.getId(); } @@ -73,7 +77,7 @@ public class WebSocketServerEndPoint { @OnClose public void onClose(CloseReason reason) { log.info("Closing websocket: "+reason); - ChangeRegister.unregisterListener(this); + ChangeService.unregisterListener(this); this.session = null; } diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangesModule.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketModule.java similarity index 87% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangesModule.java rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketModule.java index 4e02183bf3d7aaf70151db38a5e713b0c9567255..e3472356762aa94f1385fd79c357c252116addb2 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/websocket/ChangesModule.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketModule.java @@ -42,12 +42,12 @@ import org.elasticsearch.common.inject.AbstractModule; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.Loggers; -public class ChangesModule extends AbstractModule { - private final ESLogger log = Loggers.getLogger(ChangesModule.class); +public class WebsocketModule extends AbstractModule { + private final ESLogger log = Loggers.getLogger(WebsocketModule.class); @Override protected void configure() { - log.debug("Binding Changes Module"); - bind(ChangeRegister.class).asEagerSingleton(); + log.debug("Binding websocket Module"); + bind(WebsocketServer.class).asEagerSingleton(); } } diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketServer.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketServer.java new file mode 100644 index 0000000000000000000000000000000000000000..f3a44ae8c07495699c3fe34ac374b307e3cf4bfb --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketServer.java @@ -0,0 +1,86 @@ +package org.duniter.elasticsearch.websocket; + +/* + * #%L + * Duniter4j :: ElasticSearch Plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +/* + Copyright 2015 ForgeRock AS + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import org.duniter.elasticsearch.PluginSettings; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.Loggers; +import org.glassfish.tyrus.server.Server; + +import javax.websocket.DeploymentException; +import java.security.AccessController; +import java.security.PrivilegedAction; + +public class WebsocketServer { + + private final ESLogger log = Loggers.getLogger(WebsocketServer.class); + + @Inject + public WebsocketServer(final PluginSettings pluginSettings) { + final String host = pluginSettings.getWebSocketHost(); + final int port = pluginSettings.getWebSocketPort(); + + final Server server = new Server(host, port, "/ws", null, WebsocketChangeEndPoint.class) ; + + try { + log.info("Starting websocket server"); + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + try { + // Tyrus tries to load the server code using reflection. In Elasticsearch 2.x Java + // security manager is used which breaks the reflection code as it can't find the class. + // This is a workaround for that + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + server.start(); + return null; + } catch (DeploymentException e) { + throw new RuntimeException("Failed to start server", e); + } + } + }); + log.info("Websocket server started"); + } catch (Exception e) { + log.error("Failed to start Websocket server",e); + throw new RuntimeException(e); + } + } + +} diff --git a/duniter4j-elasticsearch/src/main/resources/META-INF/services/javax.websocket.ContainerProvider b/duniter4j-es-core/src/main/resources/META-INF/services/javax.websocket.ContainerProvider similarity index 100% rename from duniter4j-elasticsearch/src/main/resources/META-INF/services/javax.websocket.ContainerProvider rename to duniter4j-es-core/src/main/resources/META-INF/services/javax.websocket.ContainerProvider diff --git a/duniter4j-elasticsearch/src/main/resources/META-INF/services/org.duniter.core.beans.Bean b/duniter4j-es-core/src/main/resources/META-INF/services/org.duniter.core.beans.Bean similarity index 100% rename from duniter4j-elasticsearch/src/main/resources/META-INF/services/org.duniter.core.beans.Bean rename to duniter4j-es-core/src/main/resources/META-INF/services/org.duniter.core.beans.Bean diff --git a/duniter4j-elasticsearch/src/main/resources/META-INF/services/org.nuiton.config.ApplicationConfigProvider b/duniter4j-es-core/src/main/resources/META-INF/services/org.nuiton.config.ApplicationConfigProvider similarity index 100% rename from duniter4j-elasticsearch/src/main/resources/META-INF/services/org.nuiton.config.ApplicationConfigProvider rename to duniter4j-es-core/src/main/resources/META-INF/services/org.nuiton.config.ApplicationConfigProvider diff --git a/duniter4j-elasticsearch/src/main/resources/i18n/duniter4j-elasticsearch_en_GB.properties b/duniter4j-es-core/src/main/resources/i18n/duniter4j-es-core_en_GB.properties similarity index 92% rename from duniter4j-elasticsearch/src/main/resources/i18n/duniter4j-elasticsearch_en_GB.properties rename to duniter4j-es-core/src/main/resources/i18n/duniter4j-es-core_en_GB.properties index d5a57ab3a335453416ed273e845cb91391aec15c..a6067b359c9b1b3288467e75f3b34faaa2ce093d 100644 --- a/duniter4j-elasticsearch/src/main/resources/i18n/duniter4j-elasticsearch_en_GB.properties +++ b/duniter4j-es-core/src/main/resources/i18n/duniter4j-es-core_en_GB.properties @@ -34,10 +34,6 @@ duniter4j.config.option.tasks.queueCapacity.description= duniter4j.config.option.temp.directory.description= duniter4j.config.option.version.description= duniter4j.config.parse.error= -duniter4j.event.NODE_STARTED=Node started on cluster Duniter4j ES [%s] -duniter4j.event.subject.ERROR=[%s] Error message -duniter4j.event.subject.INFO=[%s] Information message -duniter4j.event.subject.WARN=[%s] Warning message duniter4j.executor.task.waitingExecution= duniter4j.job.stopped= duniter4j.job.stopping= diff --git a/duniter4j-elasticsearch/src/main/resources/i18n/duniter4j-elasticsearch_fr_FR.properties b/duniter4j-es-core/src/main/resources/i18n/duniter4j-es-core_fr_FR.properties similarity index 91% rename from duniter4j-elasticsearch/src/main/resources/i18n/duniter4j-elasticsearch_fr_FR.properties rename to duniter4j-es-core/src/main/resources/i18n/duniter4j-es-core_fr_FR.properties index 5c2ac7bf0f54f978cc5aa836d456b53f56dd835b..7e5877795b675d15bc9d49e6fd0be56f4cb548c5 100644 --- a/duniter4j-elasticsearch/src/main/resources/i18n/duniter4j-elasticsearch_fr_FR.properties +++ b/duniter4j-es-core/src/main/resources/i18n/duniter4j-es-core_fr_FR.properties @@ -1,4 +1,4 @@ -duniter4j-elasticsearch.config= +duniter4j-es-core.config= duniter4j.blockIndexerService.detectFork.invalidBlock=[%s] [%s] Detecting fork\: block \#%s -> new hash [%s] duniter4j.blockIndexerService.detectFork.invalidBlockchain=[%s] [%s] Peer has another blockchain (no common blocks \!). Skipping block \#%s - hash [%s]. duniter4j.blockIndexerService.detectFork.remoteBlockNotFound=[%s] [%s] Unable to get block \#%s from peer\: %s @@ -34,10 +34,6 @@ duniter4j.config.option.tasks.queueCapacity.description= duniter4j.config.option.temp.directory.description= duniter4j.config.option.version.description= duniter4j.config.parse.error= -duniter4j.event.NODE_STARTED=Noeud démarré sur le cluster Duniter4j ES [%s] -duniter4j.event.subject.ERROR=%s Message d'erreur -duniter4j.event.subject.INFO=%s Message d'information -duniter4j.event.subject.WARN=%s Message d'avertissement duniter4j.executor.task.waitingExecution= duniter4j.job.stopped= duniter4j.job.stopping= diff --git a/duniter4j-elasticsearch/src/main/resources/market-categories-bulk-insert.json b/duniter4j-es-core/src/main/resources/market-categories-bulk-insert.json similarity index 100% rename from duniter4j-elasticsearch/src/main/resources/market-categories-bulk-insert.json rename to duniter4j-es-core/src/main/resources/market-categories-bulk-insert.json diff --git a/duniter4j-elasticsearch/src/main/resources/plugin-security.policy b/duniter4j-es-core/src/main/resources/plugin-security.policy similarity index 73% rename from duniter4j-elasticsearch/src/main/resources/plugin-security.policy rename to duniter4j-es-core/src/main/resources/plugin-security.policy index 08f3659df8624bcdf38e613ce8920e539f3d49cb..3cc974ff17c10b2a5fcf00c39c9feda28b665210 100644 --- a/duniter4j-elasticsearch/src/main/resources/plugin-security.policy +++ b/duniter4j-es-core/src/main/resources/plugin-security.policy @@ -1,4 +1,4 @@ -grant codeBase "file:${es.path.home}/plugins/duniter4j-elasticsearch/"{ +grant codeBase "file:${es.path.home}/plugins/duniter4j-es-core/"{ permission java.io.FilePermission "/etc/ld.so.conf", "read"; permission java.io.FilePermission "/etc/ld.so.conf.d/*.conf", "read"; permission java.io.FilePermission "/usr/local/lib/*", "read"; diff --git a/duniter4j-elasticsearch/src/main/resources/registry-categories-bulk-insert.json b/duniter4j-es-core/src/main/resources/registry-categories-bulk-insert.json similarity index 100% rename from duniter4j-elasticsearch/src/main/resources/registry-categories-bulk-insert.json rename to duniter4j-es-core/src/main/resources/registry-categories-bulk-insert.json diff --git a/duniter4j-es-core/src/test/es-home/config/elasticsearch.yml b/duniter4j-es-core/src/test/es-home/config/elasticsearch.yml new file mode 100644 index 0000000000000000000000000000000000000000..828ddbd04543ef810f380ec8e0dbf229e6c2c48c --- /dev/null +++ b/duniter4j-es-core/src/test/es-home/config/elasticsearch.yml @@ -0,0 +1,178 @@ +# ======================== Elasticsearch Configuration ========================= +# +# NOTE: Elasticsearch comes with reasonable defaults for most settings. +# Before you set out to tweak and tune the configuration, make sure you +# understand what are you trying to accomplish and the consequences. +# +# The primary way of configuring a node is via this file. This template lists +# the most important settings you may want to configure for a production cluster. +# +# Please see the documentation for further information on configuration options: +# <http://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration.html> +# +# ---------------------------------- Cluster ----------------------------------- +# +# Use a descriptive name for your cluster: +# +# cluster.name: my-application +cluster.name: duniter4j-elasticsearch-TEST +# +# ------------------------------------ Node ------------------------------------ +# +# Use a descriptive name for the node: +# +# node.name: node-1 +# +# Add custom attributes to the node: +# +# node.rack: r1 +# +# ----------------------------------- Paths ------------------------------------ +# +# Path to directory where to store the data (separate multiple locations by comma): +# +# path.data: /path/to/data +# +# Path to log files: +# +# path.logs: /path/to/logs +# +# ----------------------------------- Memory ----------------------------------- +# +# Lock the memory on startup: +# +# bootstrap.mlockall: true +# +# Make sure that the `ES_HEAP_SIZE` environment variable is set to about half the memory +# available on the system and that the owner of the process is allowed to use this limit. +# +# Elasticsearch performs poorly when the system is swapping the memory. +# +# ---------------------------------- Network ----------------------------------- +# +# Set the bind address to a specific IP (IPv4 or IPv6): +# +# network.host: 192.168.233.1 +# +# Set a custom port for HTTP: +# +# http.port: 9200-9300 + +http.cors.allow-origin: "/.*/" +http.cors.enabled: true + +# Internal transport layer +# +# transport.tcp.port: 9210-9220 +# +# For more information, see the documentation at: +# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-network.html> +# +# --------------------------------- Discovery ---------------------------------- +# +# Pass an initial list of hosts to perform discovery when new node is started: +# The default list of hosts is ["127.0.0.1", "[::1]"] +# +# discovery.zen.ping.unicast.hosts: ["host1", "host2"] +#discovery.zen.ping.unicast.hosts: ["127.0.0.1", ""] +# +# Prevent the "split brain" by configuring the majority of nodes (total number of nodes / 2 + 1): +# +# discovery.zen.minimum_master_nodes: 3 +# +# For more information, see the documentation at: +# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-discovery.html> +# +# ---------------------------------- Gateway ----------------------------------- +# +# Block initial recovery after a full cluster restart until N nodes are started: +# +# gateway.recover_after_nodes: 3 +# +# For more information, see the documentation at: +# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-gateway.html> +# +# ---------------------------------- Various ----------------------------------- +# +# Disable starting multiple nodes on a single system: +# +# node.max_local_storage_nodes: 1 +# +# Require explicit names when deleting indices: +# +# rest.destructive_requires_name: true + +security.manager.enabled: false + +# +# ---------------------------------- Duniter4j --------------------------------- +# +# Disbale duniter4j plugin +# +# duniter.enabled: false +# +# Reset and reload all Duniter4j data at startup - DO SET to true in production +# +# duniter.indices.reload: true +# +# Default string analyzer +# +duniter.string.analyzer: french +# +# Enabling node blockchain synchronization +# +duniter.blockchain.sync.enable: false +# +# Duniter node to synchronize +# +duniter.host: cgeek.fr +duniter.port: 9330 +# +# ---------------------------------- Duniter4j security ------------------------- +# +duniter.keyring.salt: abc +duniter.keyring.password: def + +# Enable security, to disable HTTP access to the default ES admin API +# +duniter.security.enable: false +# +# Security token prefix (default: 'duniter-') +# +# duniter.auth.token.prefix: duniter- +# +# Token validity duration, in seconds (default: 600) +# +# duniter.auth.tokenValidityDuration: 3600 # = 1hour +# +# ---------------------------------- Duniter4j P2P sync ------------------------- +# +# Should synchronize data using P2P +# +duniter.data.sync.enable: false +#duniter.data.sync.host: data.duniter.fr +#duniter.data.sync.port: 80 + +# ---------------------------------- Duniter4j SMTP server ------------------------- +# +# SMTP server configuration (host and port) +# +#duniter.mail.smtp.host: localhost +#duniter.mail.smtp.port: 25 +# +# Mail 'from' address +# +#duniter.mail.from: no-reply@domain.com +duniter.mail.from: root@EIS-DEV +# +# Mail: admin address +# +#duniter.mail.admin: user@domain.com +duniter.mail.admin: blavenie@EIS-DEV +# +# Mail subject prefix +# +#duniter.mail.subject.prefix: [Duniter4j ES] + +duniter.changes.listenSource: */block +duniter.ws.port: 9400 diff --git a/duniter4j-es-core/src/test/es-home/config/logging.yml b/duniter4j-es-core/src/test/es-home/config/logging.yml new file mode 100644 index 0000000000000000000000000000000000000000..15cfa3e195cb46a62c7536f118d1684acfcc2ecf --- /dev/null +++ b/duniter4j-es-core/src/test/es-home/config/logging.yml @@ -0,0 +1,97 @@ +# you can override this using by setting a system property, for example -Des.logger.level=DEBUG +es.logger.level: INFO +rootLogger: ${es.logger.level}, console, file +logger: + # log rest execution errors for easier debugging + action: DEBUG + + # deprecation logging, turn to DEBUG to see them + deprecation: INFO, deprecation_log_file + + # reduce the logging for aws, too much is logged under the default INFO + com.amazonaws: WARN + # aws will try to do some sketchy JMX stuff, but its not needed. + com.amazonaws.jmx.SdkMBeanRegistrySupport: ERROR + com.amazonaws.metrics.AwsSdkMetrics: ERROR + + org.apache.http: INFO + + org.duniter: INFO + + org.duniter.elasticsearch: DEBUG + + duniter : DEBUG + duniter.network.p2p: TRACE + + security: DEBUG + + org.nuiton.i18n: WARN + org.nuiton.config: WARN + + # gateway + #gateway: DEBUG + #index.gateway: DEBUG + + # peer shard recovery + #indices.recovery: DEBUG + + # discovery + #discovery: TRACE + + index.search.slowlog: TRACE, index_search_slow_log_file + index.indexing.slowlog: TRACE, index_indexing_slow_log_file + +additivity: + index.search.slowlog: false + index.indexing.slowlog: false + deprecation: false + +appender: + console: + type: console + layout: + type: consolePattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %.10000m%n" + + # Use the following log4j-extras RollingFileAppender to enable gzip compression of log files. + # For more information see https://logging.apache.org/log4j/extras/apidocs/org/apache/log4j/rolling/RollingFileAppender.html + #file: + #type: extrasRollingFile + #file: ${path.logs}/${cluster.name}.log + #rollingPolicy: timeBased + #rollingPolicy.FileNamePattern: ${path.logs}/${cluster.name}.log.%d{yyyy-MM-dd}.gz + #layout: + #type: pattern + #conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + deprecation_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_deprecation.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + index_search_slow_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_index_search_slowlog.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + index_indexing_slow_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_index_indexing_slowlog.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" diff --git a/duniter4j-elasticsearch/src/test/java/org/duniter/elasticsearch/TestFixtures.java b/duniter4j-es-core/src/test/java/org/duniter/elasticsearch/TestFixtures.java similarity index 100% rename from duniter4j-elasticsearch/src/test/java/org/duniter/elasticsearch/TestFixtures.java rename to duniter4j-es-core/src/test/java/org/duniter/elasticsearch/TestFixtures.java diff --git a/duniter4j-elasticsearch/src/test/java/org/duniter/elasticsearch/TestResource.java b/duniter4j-es-core/src/test/java/org/duniter/elasticsearch/TestResource.java similarity index 100% rename from duniter4j-elasticsearch/src/test/java/org/duniter/elasticsearch/TestResource.java rename to duniter4j-es-core/src/test/java/org/duniter/elasticsearch/TestResource.java diff --git a/duniter4j-elasticsearch/src/test/java/org/duniter/elasticsearch/service/BlockchainServiceTest.java b/duniter4j-es-core/src/test/java/org/duniter/elasticsearch/service/BlockchainServiceTest.java similarity index 100% rename from duniter4j-elasticsearch/src/test/java/org/duniter/elasticsearch/service/BlockchainServiceTest.java rename to duniter4j-es-core/src/test/java/org/duniter/elasticsearch/service/BlockchainServiceTest.java diff --git a/duniter4j-elasticsearch/src/test/resources/META-INF/services/org.duniter.core.beans.Bean b/duniter4j-es-core/src/test/resources/META-INF/services/org.duniter.core.beans.Bean similarity index 100% rename from duniter4j-elasticsearch/src/test/resources/META-INF/services/org.duniter.core.beans.Bean rename to duniter4j-es-core/src/test/resources/META-INF/services/org.duniter.core.beans.Bean diff --git a/duniter4j-elasticsearch/src/test/resources/curl_test.sh b/duniter4j-es-core/src/test/resources/curl_test.sh similarity index 100% rename from duniter4j-elasticsearch/src/test/resources/curl_test.sh rename to duniter4j-es-core/src/test/resources/curl_test.sh diff --git a/duniter4j-elasticsearch/src/test/resources/duniter4j-elasticsearch-localhost-node.properties b/duniter4j-es-core/src/test/resources/duniter4j-elasticsearch-localhost-node.properties similarity index 100% rename from duniter4j-elasticsearch/src/test/resources/duniter4j-elasticsearch-localhost-node.properties rename to duniter4j-es-core/src/test/resources/duniter4j-elasticsearch-localhost-node.properties diff --git a/duniter4j-elasticsearch/src/test/resources/duniter4j-elasticsearch-test.properties b/duniter4j-es-core/src/test/resources/duniter4j-elasticsearch-test.properties similarity index 100% rename from duniter4j-elasticsearch/src/test/resources/duniter4j-elasticsearch-test.properties rename to duniter4j-es-core/src/test/resources/duniter4j-elasticsearch-test.properties diff --git a/duniter4j-elasticsearch/src/test/resources/log4j.properties b/duniter4j-es-core/src/test/resources/log4j.properties similarity index 100% rename from duniter4j-elasticsearch/src/test/resources/log4j.properties rename to duniter4j-es-core/src/test/resources/log4j.properties diff --git a/duniter4j-elasticsearch/src/test/resources/registry-test-records.json b/duniter4j-es-core/src/test/resources/registry-test-records.json similarity index 100% rename from duniter4j-elasticsearch/src/test/resources/registry-test-records.json rename to duniter4j-es-core/src/test/resources/registry-test-records.json diff --git a/duniter4j-es-gchange/pom.xml b/duniter4j-es-gchange/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..a76429f4b6d8d8b9ab396ae7525e3cf85e6e8b9a --- /dev/null +++ b/duniter4j-es-gchange/pom.xml @@ -0,0 +1,129 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.duniter</groupId> + <artifactId>duniter4j</artifactId> + <version>0.3.5-SNAPSHOT</version> + </parent> + + <groupId>org.duniter</groupId> + <artifactId>duniter4j-es-gchange</artifactId> + <packaging>jar</packaging> + <name>Duniter4j :: ElasticSearch GChange plugin</name> + + <properties> + + <!-- i18n configuration --> + <i18n.bundleOutputName>duniter4j-es-gchange-i18n</i18n.bundleOutputName> + <i18n.generateCsvFile>true</i18n.generateCsvFile> + <i18n.bundleCsvFile> + ${maven.gen.dir}/resources/META-INF/${i18n.bundleOutputName}.csv + </i18n.bundleCsvFile> + <config.i18nBundleName>${i18n.bundleOutputName}</config.i18nBundleName> + </properties> + + <dependencies> + <dependency> + <groupId>org.duniter</groupId> + <artifactId>duniter4j-es-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.duniter</groupId> + <artifactId>duniter4j-es-user</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + + <!-- Elastic Search --> + <dependency> + <groupId>org.elasticsearch</groupId> + <artifactId>elasticsearch</artifactId> + <scope>provided</scope> + </dependency> + + <!-- Unit test --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <resources> + <resource> + <directory>src/main/filtered-resources</directory> + <filtering>true</filtering> + <includes> + <include>*.config</include> + <include>**/*.properties</include> + </includes> + </resource> + <resource> + <directory>src/main/resources</directory> + <filtering>false</filtering> + </resource> + </resources> + + <plugins> + <plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>i18n-maven-plugin</artifactId> + + <executions> + <execution> + <id>scan-sources</id> + <configuration> + <entries> + <entry> + <specificGoal>parserValidation</specificGoal> + <basedir>${maven.src.dir}/main/java/</basedir> + <includes> + <param>**/**-validation.xml</param> + </includes> + </entry> + </entries> + </configuration> + <goals> + <goal>parserJava</goal> + <goal>parserValidation</goal> + <goal>gen</goal> + </goals> + </execution> + <execution> + <id>make-bundle</id> + <goals> + <goal>bundle</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>assembly-plugin</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + <configuration> + <attach>true</attach> + <appendAssemblyId>false</appendAssemblyId> + <finalName>${project.artifactId}-${project.version}</finalName> + <descriptors> + <descriptor> + ${basedir}/src/main/assembly/plugin.xml + </descriptor> + </descriptors> + <skipAssembly>${assembly.skip}</skipAssembly> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/duniter4j-es-gchange/src/license/THIRD-PARTY.properties b/duniter4j-es-gchange/src/license/THIRD-PARTY.properties new file mode 100644 index 0000000000000000000000000000000000000000..8610ec5bda0d6673a7c224b7fd16226cc1bcd664 --- /dev/null +++ b/duniter4j-es-gchange/src/license/THIRD-PARTY.properties @@ -0,0 +1,26 @@ +# Generated by org.codehaus.mojo.license.AddThirdPartyMojo +#------------------------------------------------------------------------------- +# Already used licenses in project : +# - ASL, version 2 +# - Apache License 2.0 +# - Apache License Version 2.0 +# - BSD License +# - CC0 1.0 Universal +# - COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 +# - Eclipse Public License 1.0 +# - General Public License (GPL) v3 +# - Indiana University Extreme! Lab Software License, vesion 1.1.1 +# - LGPL, version 2.1 +# - Lesser General Public License (LGPL) v 3.0 +# - Lesser General Public License (LPGL) +# - Lesser General Public License (LPGL) v 2.1 +# - MIT License +# - New BSD License +# - Public Domain, per Creative Commons CC0 +# - The Apache Software License, Version 2.0 +#------------------------------------------------------------------------------- +# Please fill the missing licenses for dependencies : +# +# +#Tue Jan 05 15:24:57 CET 2016 +commons-primitives--commons-primitives--1.0=The Apache Software License, Version 2.0 diff --git a/duniter4j-es-gchange/src/main/assembly/plugin.xml b/duniter4j-es-gchange/src/main/assembly/plugin.xml new file mode 100644 index 0000000000000000000000000000000000000000..004bbfa77a7e12db6bae7fc242ffbaec788f73d0 --- /dev/null +++ b/duniter4j-es-gchange/src/main/assembly/plugin.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<assembly> + <id>plugin</id> + + + <formats> + <format>zip</format> + </formats> + + <includeBaseDirectory>false</includeBaseDirectory> + + <dependencySets> + <dependencySet> + <outputDirectory>/</outputDirectory> + <useProjectArtifact>true</useProjectArtifact> + <useTransitiveFiltering>true</useTransitiveFiltering> + <excludes> + <exclude>org.duniter:duniter4j-es-core</exclude> + <exclude>org.duniter:duniter4j-es-user</exclude> + <exclude>org.elasticsearch:elasticsearch</exclude> + <exclude>net.java.dev.jna:jna</exclude> + <exclude>com.fasterxml.jackson.core:jackson-core</exclude> + <exclude>log4j:log4j</exclude> + </excludes> + </dependencySet> + </dependencySets> + + <fileSets> + <fileSet> + <includes> + <include>LICENSE</include> + </includes> + </fileSet> + + <fileSet> + <directory>target/classes</directory> + <outputDirectory/> + <includes> + <include>plugin-descriptor.properties</include> + <include>plugin-security.policy</include> + </includes> + </fileSet> + </fileSets> +</assembly> \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/filtered-resources/log4j.properties b/duniter4j-es-gchange/src/main/filtered-resources/log4j.properties new file mode 100644 index 0000000000000000000000000000000000000000..7b6667b1facc361ed8b1993869f728e2c01f1799 --- /dev/null +++ b/duniter4j-es-gchange/src/main/filtered-resources/log4j.properties @@ -0,0 +1,32 @@ + +# Global logging configuration +log4j.rootLogger=ERROR, stdout, file +#log4j.rootLogger=ERROR, stdout + +# Console output +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %5p %m%n + +# Duniter4j levels +log4j.logger.org.duniter=INFO +#log4j.logger.org.duniter.core.client=DEBUG +#log4j.logger.org.duniter.core.client.service=DEBUG +log4j.logger.org.duniter.elasticsearch=DEBUG + +# Other frameworks levels +log4j.logger.org.nuiton.util=WARN +log4j.logger.org.nuiton.config=WARN +log4j.logger.org.nuiton.converter=WARN +log4j.logger.org.nuiton.i18n=ERROR +log4j.logger.org.elasticsearch=WARN +#log4j.logger.org.elasticsearch=INFO + +log4j.appender.file=org.apache.log4j.RollingFileAppender +log4j.appender.file.file=${duniter4j.log.file} +log4j.appender.file.MaxFileSize=10MB +log4j.appender.file.MaxBackupIndex=4 + +log4j.appender.file.layout=org.apache.log4j.PatternLayout +log4j.appender.file.layout.ConversionPattern=%d{ISO8601} %5p (%c:%L) - [%t] %m%n + diff --git a/duniter4j-es-gchange/src/main/filtered-resources/plugin-descriptor.properties b/duniter4j-es-gchange/src/main/filtered-resources/plugin-descriptor.properties new file mode 100644 index 0000000000000000000000000000000000000000..821d0eeb415c988ceea9887fa59d442ae9f65dd8 --- /dev/null +++ b/duniter4j-es-gchange/src/main/filtered-resources/plugin-descriptor.properties @@ -0,0 +1,9 @@ +name=gchange +description=Plugin for Gchange API +version=${project.version} +site=false +jvm=true +classname=org.duniter.elasticsearch.gchange.Plugin +java.version=1.7 +elasticsearch.version=2.3.3 +isolated=true diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/Plugin.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/Plugin.java new file mode 100644 index 0000000000000000000000000000000000000000..c7b1067c7cdd35bfa0b8174cdde750ad5cdcd59a --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/Plugin.java @@ -0,0 +1,84 @@ +package org.duniter.elasticsearch.gchange; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import com.google.common.collect.Lists; +import org.duniter.elasticsearch.gchange.rest.RestModule; +import org.duniter.elasticsearch.gchange.service.ServiceModule; +import org.elasticsearch.common.component.LifecycleComponent; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.inject.Module; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.ESLoggerFactory; +import org.elasticsearch.common.settings.Settings; + +import java.util.Collection; + +public class Plugin extends org.elasticsearch.plugins.Plugin { + + private ESLogger log = ESLoggerFactory.getLogger(Plugin.class.getName()); + + private boolean enable; + + @Inject public Plugin(Settings settings) { + this.enable = settings.getAsBoolean("gchange.enabled", true); + } + + @Override + public String name() { + return "gchange"; + } + + @Override + public String description() { + return "ElasticSearch Gchange Plugin"; + } + + @Override + public Collection<Module> nodeModules() { + Collection<Module> modules = Lists.newArrayList(); + if (!enable) { + log.warn(description() + " has been disabled."); + return modules; + } + modules.add(new RestModule()); + modules.add(new ServiceModule()); + + return modules; + } + + @Override + public Collection<Class<? extends LifecycleComponent>> nodeServices() { + Collection<Class<? extends LifecycleComponent>> components = Lists.newArrayList(); + if (!enable) { + return components; + } + components.add(PluginSettings.class); + components.add(PluginInit.class); + return components; + } + + /* -- protected methods -- */ + + +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/PluginInit.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/PluginInit.java new file mode 100644 index 0000000000000000000000000000000000000000..020dda5d578cba7119d169fc0c43c2e039dcf3d2 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/PluginInit.java @@ -0,0 +1,134 @@ +package org.duniter.elasticsearch.gchange; + +/* + * #%L + * Duniter4j :: ElasticSearch Plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.gchange.service.MarketService; +import org.duniter.elasticsearch.gchange.service.RegistryService; +import org.duniter.elasticsearch.gchange.service.SynchroService; +import org.duniter.elasticsearch.threadpool.ThreadPool; +import org.duniter.elasticsearch.user.service.event.UserEvent; +import org.duniter.elasticsearch.user.service.event.UserEventCodes; +import org.duniter.elasticsearch.user.service.event.UserEventService; +import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.health.ClusterHealthStatus; +import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.inject.Injector; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.settings.Settings; + +/** + * Created by blavenie on 17/06/16. + */ +public class PluginInit extends AbstractLifecycleComponent<PluginInit> { + + private final PluginSettings pluginSettings; + private final ThreadPool threadPool; + private final Injector injector; + private final static ESLogger logger = Loggers.getLogger("gchange"); + private final Client client; + private final String clusterName; + + @Inject + public PluginInit(Client client, Settings settings, PluginSettings pluginSettings, ThreadPool threadPool, final Injector injector) { + super(settings); + this.pluginSettings = pluginSettings; + this.threadPool = threadPool; + this.injector = injector; + this.client = client; + this.clusterName = settings.get("cluster.name"); + } + + @Override + protected void doStart() { + threadPool.scheduleOnClusterHealthStatus(() -> { + createIndices(); + + // Waiting cluster back to GREEN or YELLOW state, before synchronize + threadPool.scheduleOnClusterHealthStatus(() -> { + synchronize(); + }, ClusterHealthStatus.YELLOW, ClusterHealthStatus.GREEN); + }, ClusterHealthStatus.YELLOW, ClusterHealthStatus.GREEN); + + // When started + threadPool.scheduleOnStarted(() -> { + // Notify admin + injector.getInstance(UserEventService.class) + .notifyAdmin(new UserEvent( + UserEvent.EventType.INFO, + UserEventCodes.NODE_STARTED.name(), + new String[]{clusterName})); + }); + } + + @Override + protected void doStop() { + + } + + @Override + protected void doClose() { + + } + + protected void createIndices() { + + boolean reloadIndices = pluginSettings.reloadIndices(); + + if (reloadIndices) { + if (logger.isInfoEnabled()) { + logger.info("Reloading all Gchange indices..."); + } + injector.getInstance(RegistryService.class) + .deleteIndex() + .createIndexIfNotExists(); + injector.getInstance(MarketService.class) + .deleteIndex() + .createIndexIfNotExists(); + + if (logger.isInfoEnabled()) { + logger.info("Reloading all Gchange indices... [OK]"); + } + } + else { + if (logger.isInfoEnabled()) { + logger.info("Checking Gchange indices..."); + } + injector.getInstance(RegistryService.class).createIndexIfNotExists(); + injector.getInstance(MarketService.class).createIndexIfNotExists(); + + if (logger.isInfoEnabled()) { + logger.info("Checking Gchange indices... [OK]"); + } + } + } + + protected void synchronize() { + + if (pluginSettings.enableDataSync()) { + // Synchronize + injector.getInstance(SynchroService.class).synchronize(); + } + } +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/PluginSettings.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/PluginSettings.java new file mode 100644 index 0000000000000000000000000000000000000000..48498ce03af983fb6b016fc05c1e43cfbf9c8b5a --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/PluginSettings.java @@ -0,0 +1,67 @@ +package org.duniter.elasticsearch.gchange; + +/* + * #%L + * UCoin Java Client :: Core API + * %% + * Copyright (C) 2014 - 2015 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + + +import com.google.common.collect.ImmutableSet; +import org.apache.commons.io.FileUtils; +import org.duniter.core.client.config.Configuration; +import org.duniter.core.client.config.ConfigurationOption; +import org.duniter.core.client.config.ConfigurationProvider; +import org.duniter.core.client.model.local.Peer; +import org.duniter.core.exception.TechnicalException; +import org.duniter.core.util.StringUtils; +import org.elasticsearch.common.component.*; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.nuiton.config.ApplicationConfig; +import org.nuiton.config.ApplicationConfigHelper; +import org.nuiton.config.ApplicationConfigProvider; +import org.nuiton.config.ArgumentsParserException; +import org.nuiton.i18n.I18n; +import org.nuiton.i18n.init.DefaultI18nInitializer; +import org.nuiton.i18n.init.UserI18nInitializer; + +import java.io.File; +import java.io.IOException; +import java.util.Locale; +import java.util.Set; + +import static org.nuiton.i18n.I18n.t; + +/** + * Access to configuration options + * @author Benoit Lavenier <benoit.lavenier@e-is.pro> + * @since 1.0 + */ +public class PluginSettings extends org.duniter.elasticsearch.user.PluginSettings { + + @Inject + public PluginSettings(org.elasticsearch.common.settings.Settings settings) { + super(settings); + } + + protected String getI18nBundleName() { + return "duniter4j-es-gchange-i18n"; + } +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/RestModule.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/RestModule.java new file mode 100644 index 0000000000000000000000000000000000000000..4f45cec511d95315fb6fb73a46d6c38e8679418f --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/RestModule.java @@ -0,0 +1,48 @@ +package org.duniter.elasticsearch.gchange.rest; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.gchange.rest.market.*; +import org.duniter.elasticsearch.gchange.rest.registry.*; +import org.elasticsearch.common.inject.AbstractModule; +import org.elasticsearch.common.inject.Module; + +public class RestModule extends AbstractModule implements Module { + + @Override protected void configure() { + + // Market + bind(RestMarketRecordIndexAction.class).asEagerSingleton(); + bind(RestMarketRecordUpdateAction.class).asEagerSingleton(); + bind(RestMarketCommentIndexAction.class).asEagerSingleton(); + bind(RestMarketCommentUpdateAction.class).asEagerSingleton(); + bind(RestMarketCategoryAction.class).asEagerSingleton(); + + // Registry + bind(RestRegistryRecordIndexAction.class).asEagerSingleton(); + bind(RestRegistryRecordUpdateAction.class).asEagerSingleton(); + bind(RestRegistryCommentIndexAction.class).asEagerSingleton(); + bind(RestregistryCommentUpdateAction.class).asEagerSingleton(); + bind(RestRegistryCategoryAction.class).asEagerSingleton(); + } +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketCategoryAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketCategoryAction.java new file mode 100644 index 0000000000000000000000000000000000000000..7aa19360824d38cc8b56850dcbe924169f8d2c36 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketCategoryAction.java @@ -0,0 +1,38 @@ +package org.duniter.elasticsearch.gchange.rest.market; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.gchange.service.MarketService; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.rest.RestRequest; + +public class RestMarketCategoryAction { + + @Inject + public RestMarketCategoryAction(RestSecurityController securityController) { + // Add security rule for category + securityController.allowIndexType(RestRequest.Method.GET, MarketService.INDEX, MarketService.RECORD_CATEGORY_TYPE); + } + +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketCommentIndexAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketCommentIndexAction.java new file mode 100644 index 0000000000000000000000000000000000000000..a65f154c946d241c15f93a7416d69fc1311cd3ae --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketCommentIndexAction.java @@ -0,0 +1,43 @@ +package org.duniter.elasticsearch.gchange.rest.market; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.gchange.service.MarketService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestMarketCommentIndexAction extends AbstractRestPostIndexAction { + + @Inject + public RestMarketCommentIndexAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, + MarketService service) { + super(settings, controller, client, securityController, + MarketService.INDEX, MarketService.RECORD_COMMENT_TYPE, + json -> service.indexCommentFromJson(json)); + } + +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketCommentUpdateAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketCommentUpdateAction.java new file mode 100644 index 0000000000000000000000000000000000000000..c10f58c8df3723f053620ddefd2a5b06dff3f559 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketCommentUpdateAction.java @@ -0,0 +1,43 @@ +package org.duniter.elasticsearch.gchange.rest.market; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.gchange.service.MarketService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestMarketCommentUpdateAction extends AbstractRestPostUpdateAction { + + @Inject + public RestMarketCommentUpdateAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, + MarketService service) { + super(settings, controller, client, securityController, + MarketService.INDEX, MarketService.RECORD_COMMENT_TYPE, + (json, id) -> service.updateCommentFromJson(json, id)); + } + +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketRecordIndexAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketRecordIndexAction.java new file mode 100644 index 0000000000000000000000000000000000000000..55f4c5fbf4b093e9442a14621f734656cc9f4fe2 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketRecordIndexAction.java @@ -0,0 +1,43 @@ +package org.duniter.elasticsearch.gchange.rest.market; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.gchange.service.MarketService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestMarketRecordIndexAction extends AbstractRestPostIndexAction { + + @Inject + public RestMarketRecordIndexAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, + MarketService service) { + super(settings, controller, client, securityController, + MarketService.INDEX, MarketService.RECORD_TYPE, + json -> service.indexRecordFromJson(json)); + } + +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketRecordUpdateAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketRecordUpdateAction.java new file mode 100644 index 0000000000000000000000000000000000000000..9761924f2ffdcfdfb21a5c65907e9a8f3af8dda4 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketRecordUpdateAction.java @@ -0,0 +1,43 @@ +package org.duniter.elasticsearch.gchange.rest.market; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.gchange.service.MarketService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestMarketRecordUpdateAction extends AbstractRestPostUpdateAction { + + @Inject + public RestMarketRecordUpdateAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, + MarketService service) { + super(settings, controller, client, securityController, + MarketService.INDEX, MarketService.RECORD_TYPE, + (json, id) -> service.updateRecordFromJson(json, id)); + } + +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryCategoryAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryCategoryAction.java new file mode 100644 index 0000000000000000000000000000000000000000..0fdb34fa17095d187e74a24269d5c3e30f36ec7c --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryCategoryAction.java @@ -0,0 +1,38 @@ +package org.duniter.elasticsearch.gchange.rest.registry; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.gchange.service.RegistryService; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.rest.RestRequest; + +public class RestRegistryCategoryAction { + + @Inject + public RestRegistryCategoryAction(RestSecurityController securityController) { + // Add security rule for category + securityController.allowIndexType(RestRequest.Method.GET, RegistryService.INDEX, RegistryService.RECORD_CATEGORY_TYPE); + } + +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryCommentIndexAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryCommentIndexAction.java new file mode 100644 index 0000000000000000000000000000000000000000..bd0047608733aca88299aa1b8a577db679aefb01 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryCommentIndexAction.java @@ -0,0 +1,43 @@ +package org.duniter.elasticsearch.gchange.rest.registry; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.gchange.service.RegistryService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestRegistryCommentIndexAction extends AbstractRestPostIndexAction { + + @Inject + public RestRegistryCommentIndexAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, + RegistryService service) { + super(settings, controller, client, securityController, + RegistryService.INDEX, RegistryService.RECORD_COMMENT_TYPE, + json -> service.indexCommentFromJson(json)); + } + +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryRecordIndexAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryRecordIndexAction.java new file mode 100644 index 0000000000000000000000000000000000000000..02c2e963b271e0ef09d21e116498284a417044dd --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryRecordIndexAction.java @@ -0,0 +1,43 @@ +package org.duniter.elasticsearch.gchange.rest.registry; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.gchange.service.RegistryService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestRegistryRecordIndexAction extends AbstractRestPostIndexAction { + + + @Inject + public RestRegistryRecordIndexAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, + RegistryService service) { + super(settings, controller, client, securityController, + RegistryService.INDEX, RegistryService.RECORD_TYPE, + json -> service.indexRecordFromJson(json)); + } +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryRecordUpdateAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryRecordUpdateAction.java new file mode 100644 index 0000000000000000000000000000000000000000..e77b942513e52bd991d5639face16fd2f1c8a2db --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryRecordUpdateAction.java @@ -0,0 +1,43 @@ +package org.duniter.elasticsearch.gchange.rest.registry; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.gchange.service.RegistryService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestRegistryRecordUpdateAction extends AbstractRestPostUpdateAction { + + @Inject + public RestRegistryRecordUpdateAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, + RegistryService service) { + super(settings, controller, client, securityController, + RegistryService.INDEX, RegistryService.RECORD_TYPE, + (json, id) -> service.updateRecordFromJson(json, id)); + } + +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestregistryCommentUpdateAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestregistryCommentUpdateAction.java new file mode 100644 index 0000000000000000000000000000000000000000..8687f463f298d5ee82b5d0da016874931c43d701 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestregistryCommentUpdateAction.java @@ -0,0 +1,43 @@ +package org.duniter.elasticsearch.gchange.rest.registry; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.gchange.service.RegistryService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestregistryCommentUpdateAction extends AbstractRestPostUpdateAction { + + @Inject + public RestregistryCommentUpdateAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, + RegistryService service) { + super(settings, controller, client, securityController, + RegistryService.INDEX, RegistryService.RECORD_COMMENT_TYPE, + (json, id) -> service.updateCommentFromJson(json, id)); + } + +} \ No newline at end of file diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/registry/CitiesRegistryService.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CitiesRegistryService.java similarity index 99% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/registry/CitiesRegistryService.java rename to duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CitiesRegistryService.java index 891791a829f951384016238925e07c2d31f78cea..e6f72a729581b50ab31594d50f03833f436b1881 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/registry/CitiesRegistryService.java +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CitiesRegistryService.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.service.registry; +package org.duniter.elasticsearch.gchange.service; /* * #%L @@ -26,13 +26,13 @@ package org.duniter.elasticsearch.service.registry; import com.fasterxml.jackson.core.JsonProcessingException; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import org.duniter.core.client.model.bma.gson.GsonUtils; import org.duniter.core.exception.TechnicalException; import org.duniter.core.util.StringUtils; -import org.duniter.elasticsearch.PluginSettings; +import org.duniter.elasticsearch.gchange.PluginSettings; import org.duniter.elasticsearch.service.AbstractService; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/MarketService.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/MarketService.java similarity index 98% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/MarketService.java rename to duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/MarketService.java index 7b1a5a11daa82fd593d6ac8a8f4cdb1870c5893b..b037fc075ec2a417c81630e310624cb2f0e7915a 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/MarketService.java +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/MarketService.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.service; +package org.duniter.elasticsearch.gchange.service; /* * #%L @@ -27,7 +27,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import org.duniter.core.client.service.bma.WotRemoteService; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; -import org.duniter.elasticsearch.PluginSettings; +import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.service.AbstractService; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/RegistryService.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/RegistryService.java new file mode 100644 index 0000000000000000000000000000000000000000..3542cd47c0b9b2dc7f7357098e6359ed7ae3f122 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/RegistryService.java @@ -0,0 +1,357 @@ +package org.duniter.elasticsearch.gchange.service; + +/* + * #%L + * UCoin Java Client :: Core API + * %% + * Copyright (C) 2014 - 2015 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.gson.Gson; +import org.duniter.core.client.model.bma.gson.GsonUtils; +import org.duniter.core.client.service.bma.BlockchainRemoteService; +import org.duniter.core.exception.TechnicalException; +import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.service.AbstractService; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; +import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; + +import java.io.IOException; + +/** + * Created by Benoit on 30/03/2015. + */ +public class RegistryService extends AbstractService { + + public static final String INDEX = "registry"; + public static final String RECORD_TYPE = "record"; + public static final String RECORD_CATEGORY_TYPE = "category"; + public static final String RECORD_COMMENT_TYPE = "comment"; + private static final String CATEGORIES_BULK_CLASSPATH_FILE = "registry-categories-bulk-insert.json"; + + private final Gson gson; + private BlockchainRemoteService blockchainRemoteService; + + @Inject + public RegistryService(Client client, + PluginSettings settings, + CryptoService cryptoService, + BlockchainRemoteService blockchainRemoteService) { + super("gchange." + INDEX, client, settings, cryptoService); + this.gson = GsonUtils.newBuilder().create(); + this.blockchainRemoteService = blockchainRemoteService; + } + + /** + * Create index need for blockchain registry, if need + */ + public RegistryService createIndexIfNotExists() { + try { + if (!existsIndex(INDEX)) { + createIndex(); + + fillRecordCategories(); + } + } + catch(JsonProcessingException e) { + throw new TechnicalException(String.format("Error while creating index [%s]", INDEX)); + } + return this; + } + + /** + * Create index for registry + * @throws JsonProcessingException + */ + public RegistryService createIndex() throws JsonProcessingException { + logger.info(String.format("Creating index [%s]", INDEX)); + + CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(INDEX); + org.elasticsearch.common.settings.Settings indexSettings = org.elasticsearch.common.settings.Settings.settingsBuilder() + .put("number_of_shards", 3) + .put("number_of_replicas", 1) + //.put("analyzer", createDefaultAnalyzer()) + .build(); + createIndexRequestBuilder.setSettings(indexSettings); + createIndexRequestBuilder.addMapping(RECORD_CATEGORY_TYPE, createRecordCategoryType()); + createIndexRequestBuilder.addMapping(RECORD_TYPE, createRecordType()); + createIndexRequestBuilder.addMapping(RECORD_COMMENT_TYPE, createRecordCommentType(INDEX, RECORD_COMMENT_TYPE)); + createIndexRequestBuilder.execute().actionGet(); + + return this; + } + + public RegistryService deleteIndex() { + deleteIndexIfExists(INDEX); + return this; + } + + public boolean existsIndex() { + return super.existsIndex(INDEX); + } + + public RegistryService fillRecordCategories() { + if (logger.isDebugEnabled()) { + logger.debug(String.format("[%s/%s] Fill data", INDEX, RECORD_CATEGORY_TYPE)); + } + + // Insert categories + bulkFromClasspathFile(CATEGORIES_BULK_CLASSPATH_FILE, INDEX, RECORD_CATEGORY_TYPE, + // Add order attribute + new AddSequenceAttributeHandler("order", "\\{.*\"name\".*\\}", 1)); + + return this; + } + + public String indexRecordFromJson(String json) { + return checkIssuerAndIndexDocumentFromJson(INDEX, RECORD_TYPE, json); + } + + public void updateRecordFromJson(String json, String id) { + checkIssuerAndUpdateDocumentFromJson(INDEX, RECORD_TYPE, json, id); + } + + public String indexCommentFromJson(String json) { + return checkIssuerAndIndexDocumentFromJson(INDEX, RECORD_COMMENT_TYPE, json); + } + + public void updateCommentFromJson(String json, String id) { + checkIssuerAndUpdateDocumentFromJson(INDEX, RECORD_COMMENT_TYPE, json, id); + } + + /* -- Internal methods -- */ + + public XContentBuilder createRecordType() { + String stringAnalyzer = pluginSettings.getDefaultStringAnalyzer(); + + try { + XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(RECORD_TYPE) + .startObject("properties") + + // title + .startObject("title") + .field("type", "string") + .field("analyzer", stringAnalyzer) + .endObject() + + // description + .startObject("description") + .field("type", "string") + .field("analyzer", stringAnalyzer) + .endObject() + + // creationTime + .startObject("creationTime") + .field("type", "integer") + .endObject() + + // time + .startObject("time") + .field("type", "integer") + .endObject() + + // issuer + .startObject("issuer") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // pubkey + .startObject("pubkey") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // address + .startObject("address") + .field("type", "string") + .field("analyzer", stringAnalyzer) + .endObject() + + // city + .startObject("city") + .field("type", "string") + .endObject() + + // geoPoint + .startObject("geoPoint") + .field("type", "geo_point") + .endObject() + + // thumbnail + .startObject("thumbnail") + .field("type", "attachment") + .startObject("fields") // src + .startObject("content") // title + .field("index", "no") + .endObject() + .startObject("title") // title + .field("type", "string") + .field("store", "no") + .endObject() + .startObject("author") // title + .field("store", "no") + .endObject() + .startObject("content_type") // title + .field("store", "yes") + .endObject() + .endObject() + .endObject() + + // pictures + .startObject("pictures") + .field("type", "nested") + .field("dynamic", "false") + .startObject("properties") + .startObject("file") // file + .field("type", "attachment") + .startObject("fields") + .startObject("content") // content + .field("index", "no") + .endObject() + .startObject("title") // title + .field("type", "string") + .field("store", "yes") + .field("analyzer", stringAnalyzer) + .endObject() + .startObject("author") // author + .field("type", "string") + .field("store", "no") + .endObject() + .startObject("content_type") // content_type + .field("store", "yes") + .endObject() + .endObject() + .endObject() + .endObject() + .endObject() + + // picturesCount + .startObject("picturesCount") + .field("type", "integer") + .endObject() + + // category + .startObject("category") + .field("type", "nested") + .field("dynamic", "false") + .startObject("properties") + .startObject("id") // id + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + .startObject("parent") // parent + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + .startObject("name") // name + .field("type", "string") + .field("analyzer", stringAnalyzer) + .endObject() + .endObject() + .endObject() + + // tags + .startObject("tags") + .field("type", "completion") + .field("search_analyzer", "simple") + .field("analyzer", "simple") + .field("preserve_separators", "false") + .endObject() + + .endObject() + .endObject().endObject(); + + return mapping; + } + catch(IOException ioe) { + throw new TechnicalException(String.format("Error while getting mapping for index [%s/%s]: %s", INDEX, RECORD_TYPE, ioe.getMessage()), ioe); + } + } + + public XContentBuilder createRecordCategoryType() { + try { + XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(RECORD_CATEGORY_TYPE) + .startObject("properties") + + // name + .startObject("name") + .field("type", "string") + .field("analyzer", pluginSettings.getDefaultStringAnalyzer()) + .endObject() + + // description + /*.startObject("description") + .field("type", "string") + .endObject()*/ + + // parent + .startObject("parent") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // tags + /*.startObject("tags") + .field("type", "completion") + .field("search_analyzer", "simple") + .field("analyzer", "simple") + .field("preserve_separators", "false") + .endObject()*/ + + .endObject() + .endObject().endObject(); + + return mapping; + } + catch(IOException ioe) { + throw new TechnicalException(String.format("Error while getting mapping for index [%s/%s]: %s", INDEX, RECORD_CATEGORY_TYPE, ioe.getMessage()), ioe); + } + } + + /** + * + * @param jsonCategory + * @return the product id + */ + public String indexCategoryFromJson(String jsonCategory) { + if (logger.isDebugEnabled()) { + logger.debug("Indexing a category"); + } + + // Preparing indexBlocksFromNode + IndexRequestBuilder indexRequest = client.prepareIndex(INDEX, RECORD_CATEGORY_TYPE) + .setSource(jsonCategory); + + // Execute indexBlocksFromNode + IndexResponse response = indexRequest + .setRefresh(false) + .execute().actionGet(); + + return response.getId(); + } + +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/ServiceModule.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/ServiceModule.java new file mode 100644 index 0000000000000000000000000000000000000000..282c53beb704d4ccc8ea8a99ae5e760b79035785 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/ServiceModule.java @@ -0,0 +1,36 @@ +package org.duniter.elasticsearch.gchange.service; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.elasticsearch.common.inject.AbstractModule; +import org.elasticsearch.common.inject.Module; + +public class ServiceModule extends AbstractModule implements Module { + + @Override protected void configure() { + bind(RegistryService.class).asEagerSingleton(); + bind(CitiesRegistryService.class).asEagerSingleton(); + bind(MarketService.class).asEagerSingleton(); + bind(SynchroService.class).asEagerSingleton(); + } +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/SynchroService.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/SynchroService.java new file mode 100644 index 0000000000000000000000000000000000000000..c91568582a394cb915dfbc28738deaf77ed84f6d --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/SynchroService.java @@ -0,0 +1,74 @@ +package org.duniter.elasticsearch.gchange.service; + +/* + * #%L + * Duniter4j :: ElasticSearch Plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.core.client.model.local.Peer; +import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.service.AbstractSynchroService; +import org.duniter.elasticsearch.service.ServiceLocator; +import org.duniter.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; + +/** + * Created by blavenie on 27/10/16. + */ +public class SynchroService extends AbstractSynchroService { + + @Inject + public SynchroService(Client client, PluginSettings settings, CryptoService cryptoService, + ThreadPool threadPool, final ServiceLocator serviceLocator) { + super(client, settings, cryptoService, threadPool, serviceLocator); + } + + public void synchronize() { + logger.info("Synchronizing data..."); + Peer peer = getPeerFromAPI("GCHANGE API"); + synchronize(peer); + } + + /* -- protected methods -- */ + + protected void synchronize(Peer peer) { + + long sinceTime = 0; // TODO: get last sync time from somewhere ? (e.g. a specific index) + + logger.info(String.format("[%s] Synchronizing gchange data since %s...", peer.toString(), sinceTime)); + + importMarketChanges(peer, sinceTime); + importRegistryChanges(peer, sinceTime); + + logger.info(String.format("[%s] Synchronizing gchange data since %s [OK]", peer.toString(), sinceTime)); + } + + protected void importMarketChanges(Peer peer, long sinceTime) { + importChanges(peer, MarketService.INDEX, MarketService.RECORD_TYPE, sinceTime); + importChanges(peer, MarketService.INDEX, MarketService.RECORD_COMMENT_TYPE, sinceTime); + } + + protected void importRegistryChanges(Peer peer, long sinceTime) { + importChanges(peer, RegistryService.INDEX, RegistryService.RECORD_TYPE, sinceTime); + importChanges(peer, RegistryService.INDEX, RegistryService.RECORD_COMMENT_TYPE, sinceTime); + } +} diff --git a/duniter4j-elasticsearch/src/main/misc/cities-fr.geoJson.txt b/duniter4j-es-gchange/src/main/misc/cities-fr.geoJson.txt similarity index 100% rename from duniter4j-elasticsearch/src/main/misc/cities-fr.geoJson.txt rename to duniter4j-es-gchange/src/main/misc/cities-fr.geoJson.txt diff --git a/duniter4j-elasticsearch/src/main/misc/index.sh b/duniter4j-es-gchange/src/main/misc/index.sh similarity index 100% rename from duniter4j-elasticsearch/src/main/misc/index.sh rename to duniter4j-es-gchange/src/main/misc/index.sh diff --git a/duniter4j-elasticsearch/src/main/misc/registry-categories-naf2008_liste_n5.ods b/duniter4j-es-gchange/src/main/misc/registry-categories-naf2008_liste_n5.ods similarity index 100% rename from duniter4j-elasticsearch/src/main/misc/registry-categories-naf2008_liste_n5.ods rename to duniter4j-es-gchange/src/main/misc/registry-categories-naf2008_liste_n5.ods diff --git a/duniter4j-es-gchange/src/main/resources/market-categories-bulk-insert.json b/duniter4j-es-gchange/src/main/resources/market-categories-bulk-insert.json new file mode 100644 index 0000000000000000000000000000000000000000..c9527a96a6f1a8d350067029670a10b4c1f2de74 --- /dev/null +++ b/duniter4j-es-gchange/src/main/resources/market-categories-bulk-insert.json @@ -0,0 +1,151 @@ +{ "index": { "_id": "cat71"}} +{ "name": "Emploi" , "parent": null} +{ "index": { "_id": "cat33"}} +{ "name": "Offres d'emploi", "parent": "cat71" } + +{ "index": { "_id": "cat1" }} +{ "name": "Véhicules" , "parent": null} +{ "index": { "_id": "cat2" }} +{ "name": "Voitures" , "parent": "cat2" } +{ "index": { "_id": "cat3" }} +{ "name": "Motos" , "parent": "cat3" } +{ "index": { "_id": "cat4" }} +{ "name": "Caravaning" , "parent": "cat3" } +{ "index": { "_id": "cat5" }} +{ "name": "Utilitaires" , "parent": "cat3" } +{ "index": { "_id": "cat6" }} +{ "name": "Equipement Auto" , "parent": "cat3" } +{ "index": { "_id": "cat44" }} +{ "name": "Equipement Moto" , "parent": "cat3" } +{ "index": { "_id": "cat50" }} +{ "name": "Equipement Caravaning" , "parent": "cat3" } +{ "index": { "_id": "cat7" }} +{ "name": "Nautisme" , "parent": "cat3" } +{ "index": { "_id": "cat51" }} +{ "name": "Equipement Nautisme" , "parent": "cat3" } + +{ "index": { "_id": "cat8" }} +{ "name": "Immobilier" , "parent": null} +{ "index": { "_id": "cat9" }} +{ "name": "Ventes immobilières" , "parent": "cat8" } +{ "index": { "_id": "cat10" }} +{ "name": "Locations" , "parent": "cat8" } +{ "index": { "_id": "cat11" }} +{ "name": "Colocations" , "parent": "cat8" } +{ "index": { "_id": "cat13" }} +{ "name": "Bureaux & Commerces" , "parent": "cat8" } + +{ "index": { "_id": "cat66" }} +{ "name": "Vacances" , "parent": null} +{ "index": { "_id": "cat12" }} +{ "name": "Locations & Gîtes" , "parent": "cat66" } +{ "index": { "_id": "cat67" }} +{ "name": "Chambres d'hôtes" , "parent": "cat66" } +{ "index": { "_id": "cat68" }} +{ "name": "Campings" , "parent": "cat66" } +{ "index": { "_id": "cat69" }} +{ "name": "Hôtels" , "parent": "cat66" } +{ "index": { "_id": "cat70" }} +{ "name": "Hébergements insolites" , "parent": "cat66" } + +{ "index": { "_id": "cat14" }} +{ "name": "Multimédia" , "parent": null} +{ "index": { "_id": "cat15" }} +{ "name": "Informatique" , "parent": "cat14" } +{ "index": { "_id": "cat43" }} +{ "name": "Consoles & Jeux vidéo" , "parent": "cat14" } +{ "index": { "_id": "cat16" }} +{ "name": "Image & Son" , "parent": "cat14" } +{ "index": { "_id": "cat17" }} +{ "name": "Téléphonie" , "parent": "cat14" } + +{ "index": { "_id": "cat18" }} +{ "name": "Maison" , "parent": null} +{ "index": { "_id": "cat19" }} +{ "name": "Ameublement" , "parent": "cat18" } +{ "index": { "_id": "cat20" }} +{ "name": "Electroménager" , "parent": "cat18" } +{ "index": { "_id": "cat45" }} +{ "name": "Arts de la table" , "parent": "cat18" } +{ "index": { "_id": "cat39" }} +{ "name": "Décoration" , "parent": "cat18" } +{ "index": { "_id": "cat46" }} +{ "name": "Linge de maison" , "parent": "cat18" } +{ "index": { "_id": "cat21" }} +{ "name": "Bricolage" , "parent": "cat18" } +{ "index": { "_id": "cat52" }} +{ "name": "Jardinage" , "parent": "cat18" } +{ "index": { "_id": "cat22" }} +{ "name": "Vêtements" , "parent": "cat18" } +{ "index": { "_id": "cat53" }} +{ "name": "Chaussures" , "parent": "cat18" } +{ "index": { "_id": "cat47" }} +{ "name": "Accessoires & Bagagerie" , "parent": "cat18" } +{ "index": { "_id": "cat42" }} +{ "name": "Montres & Bijoux" , "parent": "cat18" } +{ "index": { "_id": "cat23" }} +{ "name": "Equipement bébé" , "parent": "cat18" } +{ "index": { "_id": "cat54" }} +{ "name": "Vêtements bébé" , "parent": "cat18" } + +{ "index": { "_id": "cat24" }} +{ "name": "Loisirs" , "parent": null} +{ "index": { "_id": "cat25" }} +{ "name": "DVD / Films" , "parent": "cat24" } +{ "index": { "_id": "cat26" }} +{ "name": "CD / Musique" , "parent": "cat24" } +{ "index": { "_id": "cat27" }} +{ "name": "Livres" , "parent": "cat24" } +{ "index": { "_id": "cat28" }} +{ "name": "Animaux" , "parent": "cat24" } +{ "index": { "_id": "cat55" }} +{ "name": "Vélos" , "parent": "cat24" } +{ "index": { "_id": "cat29" }} +{ "name": "Sports & Hobbies" } +{ "index": { "_id": "cat30" }} +{ "name": "Instruments de musique" , "parent": "cat24" } +{ "index": { "_id": "cat40" }} +{ "name": "Collection" , "parent": "cat24" } +{ "index": { "_id": "cat41" }} +{ "name": "Jeux & Jouets" , "parent": "cat24" } +{ "index": { "_id": "cat48" }} +{ "name": "Vins & Gastronomie" , "parent": "cat24" } + +{ "index": { "_id": "cat56" }} +{ "name": "Matériel professionnel" , "parent": null} +{ "index": { "_id": "cat57" }} +{ "name": "Matériel Agricole" , "parent": "cat56" } +{ "index": { "_id": "cat58" }} +{ "name": "Transport - Manutention" , "parent": "cat56" } +{ "index": { "_id": "cat59" }} +{ "name": "BTP - Chantier Gros-oeuvre" , "parent": "cat56" } +{ "index": { "_id": "cat60" }} +{ "name": "Outillage - Matériaux 2nd-oeuvre" , "parent": "cat56" } +{ "index": { "_id": "cat32" }} +{ "name": "Équipements Industriels" , "parent": "cat56" } +{ "index": { "_id": "cat61" }} +{ "name": "Restauration - Hôtellerie" , "parent": "cat56" } +{ "index": { "_id": "cat62" }} +{ "name": "Fournitures de Bureau" , "parent": "cat56" } +{ "index": { "_id": "cat63" }} +{ "name": "Commerces & Marchés" , "parent": "cat56" } +{ "index": { "_id": "cat64" }} +{ "name": "Matériel Médical" , "parent": "cat56" } + +{ "index": { "_id": "cat31" }} +{ "name": "Services" , "parent": null} +{ "index": { "_id": "cat34" }} +{ "name": "Prestations de services" , "parent": "cat31" } +{ "index": { "_id": "cat35" }} +{ "name": "Billetterie" , "parent": "cat31" } +{ "index": { "_id": "cat49" }} +{ "name": "Evénements" , "parent": "cat31" } +{ "index": { "_id": "cat36" }} +{ "name": "Cours particuliers" , "parent": "cat31" } +{ "index": { "_id": "cat65" }} +{ "name": "Covoiturage" , "parent": "cat31" } +{ "index": { "_id": "cat37" }} + +{ "name": "Divers" , "parent": null} +{ "index": { "_id": "cat38" }} +{ "name": "Autres" , "parent": "cat37" } diff --git a/duniter4j-es-gchange/src/main/resources/plugin-security.policy b/duniter4j-es-gchange/src/main/resources/plugin-security.policy new file mode 100644 index 0000000000000000000000000000000000000000..3ea59400b9bd2a3d0e1b89091169fa50b60e1123 --- /dev/null +++ b/duniter4j-es-gchange/src/main/resources/plugin-security.policy @@ -0,0 +1,5 @@ +grant codeBase "file:${es.path.home}/plugins/duniter4j-es-gchange/"{ + permission java.io.FilePermission "/etc/ld.so.conf", "read"; + permission java.io.FilePermission "/etc/ld.so.conf.d/*.conf", "read"; + permission java.io.FilePermission "/usr/local/lib/*", "read"; +}; \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/resources/registry-categories-bulk-insert.json b/duniter4j-es-gchange/src/main/resources/registry-categories-bulk-insert.json new file mode 100644 index 0000000000000000000000000000000000000000..3ee70de3d3aa58dfd3ff01f1a578a2f917508d66 --- /dev/null +++ b/duniter4j-es-gchange/src/main/resources/registry-categories-bulk-insert.json @@ -0,0 +1,1506 @@ +{"index": {"_id": "A"}} +{"name": "Agriculture, sylviculture et pêche", "parent": null} +{"index": {"_id": "01.11Z"}} +{"name": "Culture de céréales (à l'exception du riz), de légumineuses et de graines oléagineuses", "parent": "A"} +{"index": {"_id": "01.12Z"}} +{"name": "Culture du riz", "parent": "A"} +{"index": {"_id": "01.13Z"}} +{"name": "Culture de légumes, de melons, de racines et de tubercules", "parent": "A"} +{"index": {"_id": "01.14Z"}} +{"name": "Culture de la canne à sucre", "parent": "A"} +{"index": {"_id": "01.15Z"}} +{"name": "Culture du tabac", "parent": "A"} +{"index": {"_id": "01.16Z"}} +{"name": "Culture de plantes à fibres", "parent": "A"} +{"index": {"_id": "01.19Z"}} +{"name": "Autres cultures non permanentes", "parent": "A"} +{"index": {"_id": "01.21Z"}} +{"name": "Culture de la vigne", "parent": "A"} +{"index": {"_id": "01.22Z"}} +{"name": "Culture de fruits tropicaux et subtropicaux", "parent": "A"} +{"index": {"_id": "01.23Z"}} +{"name": "Culture d'agrumes", "parent": "A"} +{"index": {"_id": "01.24Z"}} +{"name": "Culture de fruits à pépins et à noyau", "parent": "A"} +{"index": {"_id": "01.25Z"}} +{"name": "Culture d'autres fruits d'arbres ou d'arbustes et de fruits à coque", "parent": "A"} +{"index": {"_id": "01.26Z"}} +{"name": "Culture de fruits oléagineux", "parent": "A"} +{"index": {"_id": "01.27Z"}} +{"name": "Culture de plantes à boissons", "parent": "A"} +{"index": {"_id": "01.28Z"}} +{"name": "Culture de plantes à épices, aromatiques, médicinales et pharmaceutiques", "parent": "A"} +{"index": {"_id": "01.29Z"}} +{"name": "Autres cultures permanentes", "parent": "A"} +{"index": {"_id": "01.30Z"}} +{"name": "Reproduction de plantes", "parent": "A"} +{"index": {"_id": "01.41Z"}} +{"name": "Élevage de vaches laitières", "parent": "A"} +{"index": {"_id": "01.42Z"}} +{"name": "Élevage d'autres bovins et de buffles", "parent": "A"} +{"index": {"_id": "01.43Z"}} +{"name": "Élevage de chevaux et d'autres équidés", "parent": "A"} +{"index": {"_id": "01.44Z"}} +{"name": "Élevage de chameaux et d'autres camélidés", "parent": "A"} +{"index": {"_id": "01.45Z"}} +{"name": "Élevage d'ovins et de caprins", "parent": "A"} +{"index": {"_id": "01.46Z"}} +{"name": "Élevage de porcins", "parent": "A"} +{"index": {"_id": "01.47Z"}} +{"name": "Élevage de volailles", "parent": "A"} +{"index": {"_id": "01.49Z"}} +{"name": "Élevage d'autres animaux", "parent": "A"} +{"index": {"_id": "01.50Z"}} +{"name": "Culture et élevage associés", "parent": "A"} +{"index": {"_id": "01.61Z"}} +{"name": "Activités de soutien aux cultures", "parent": "A"} +{"index": {"_id": "01.62Z"}} +{"name": "Activités de soutien à la production animale", "parent": "A"} +{"index": {"_id": "01.63Z"}} +{"name": "Traitement primaire des récoltes", "parent": "A"} +{"index": {"_id": "01.64Z"}} +{"name": "Traitement des semences", "parent": "A"} +{"index": {"_id": "01.70Z"}} +{"name": "Chasse, piégeage et services annexes", "parent": "A"} +{"index": {"_id": "02.10Z"}} +{"name": "Sylviculture et autres activités forestières", "parent": "A"} +{"index": {"_id": "02.20Z"}} +{"name": "Exploitation forestière", "parent": "A"} +{"index": {"_id": "02.30Z"}} +{"name": "Récolte de produits forestiers non ligneux poussant à l'état sauvage", "parent": "A"} +{"index": {"_id": "02.40Z"}} +{"name": "Services de soutien à l'exploitation forestière", "parent": "A"} +{"index": {"_id": "03.11Z"}} +{"name": "Pêche en mer", "parent": "A"} +{"index": {"_id": "03.12Z"}} +{"name": "Pêche en eau douce", "parent": "A"} +{"index": {"_id": "03.21Z"}} +{"name": "Aquaculture en mer", "parent": "A"} +{"index": {"_id": "03.22Z"}} +{"name": "Aquaculture en eau douce", "parent": "A"} +{"index": {"_id": "B"}} +{"name": "Industries extractives", "parent": null} +{"index": {"_id": "05.10Z"}} +{"name": "Extraction de houille", "parent": "B"} +{"index": {"_id": "05.20Z"}} +{"name": "Extraction de lignite", "parent": "B"} +{"index": {"_id": "06.10Z"}} +{"name": "Extraction de pétrole brut", "parent": "B"} +{"index": {"_id": "06.20Z"}} +{"name": "Extraction de gaz naturel", "parent": "B"} +{"index": {"_id": "07.10Z"}} +{"name": "Extraction de minerais de fer", "parent": "B"} +{"index": {"_id": "07.21Z"}} +{"name": "Extraction de minerais d'uranium et de thorium", "parent": "B"} +{"index": {"_id": "07.29Z"}} +{"name": "Extraction d'autres minerais de métaux non ferreux", "parent": "B"} +{"index": {"_id": "08.11Z"}} +{"name": "Extraction de pierres ornementales et de construction, de calcaire industriel, de gypse, de craie et d'ardoise", "parent": "B"} +{"index": {"_id": "08.12Z"}} +{"name": "Exploitation de gravières et sablières, extraction d'argiles et de kaolin", "parent": "B"} +{"index": {"_id": "08.91Z"}} +{"name": "Extraction des minéraux chimiques et d'engrais minéraux", "parent": "B"} +{"index": {"_id": "08.92Z"}} +{"name": "Extraction de tourbe", "parent": "B"} +{"index": {"_id": "08.93Z"}} +{"name": "Production de sel", "parent": "B"} +{"index": {"_id": "08.99Z"}} +{"name": "Autres activités extractives n.c.a.", "parent": "B"} +{"index": {"_id": "09.10Z"}} +{"name": "Activités de soutien à l'extraction d'hydrocarbures", "parent": "B"} +{"index": {"_id": "09.90Z"}} +{"name": "Activités de soutien aux autres industries extractives", "parent": "B"} +{"index": {"_id": "C"}} +{"name": "Industrie manufacturière", "parent": null} +{"index": {"_id": "10.11Z"}} +{"name": "Transformation et conservation de la viande de boucherie", "parent": "C"} +{"index": {"_id": "10.12Z"}} +{"name": "Transformation et conservation de la viande de volaille", "parent": "C"} +{"index": {"_id": "10.13A"}} +{"name": "Préparation industrielle de produits à base de viande", "parent": "C"} +{"index": {"_id": "10.13B"}} +{"name": "Charcuterie", "parent": "C"} +{"index": {"_id": "10.20Z"}} +{"name": "Transformation et conservation de poisson, de crustacés et de mollusques", "parent": "C"} +{"index": {"_id": "10.31Z"}} +{"name": "Transformation et conservation de pommes de terre", "parent": "C"} +{"index": {"_id": "10.32Z"}} +{"name": "Préparation de jus de fruits et légumes", "parent": "C"} +{"index": {"_id": "10.39A"}} +{"name": "Autre transformation et conservation de légumes", "parent": "C"} +{"index": {"_id": "10.39B"}} +{"name": "Transformation et conservation de fruits", "parent": "C"} +{"index": {"_id": "10.41A"}} +{"name": "Fabrication d'huiles et graisses brutes", "parent": "C"} +{"index": {"_id": "10.41B"}} +{"name": "Fabrication d'huiles et graisses raffinées", "parent": "C"} +{"index": {"_id": "10.42Z"}} +{"name": "Fabrication de margarine et graisses comestibles similaires", "parent": "C"} +{"index": {"_id": "10.51A"}} +{"name": "Fabrication de lait liquide et de produits frais", "parent": "C"} +{"index": {"_id": "10.51B"}} +{"name": "Fabrication de beurre", "parent": "C"} +{"index": {"_id": "10.51C"}} +{"name": "Fabrication de fromage", "parent": "C"} +{"index": {"_id": "10.51D"}} +{"name": "Fabrication d'autres produits laitiers", "parent": "C"} +{"index": {"_id": "10.52Z"}} +{"name": "Fabrication de glaces et sorbets", "parent": "C"} +{"index": {"_id": "10.61A"}} +{"name": "Meunerie", "parent": "C"} +{"index": {"_id": "10.61B"}} +{"name": "Autres activités du travail des grains", "parent": "C"} +{"index": {"_id": "10.62Z"}} +{"name": "Fabrication de produits amylacés", "parent": "C"} +{"index": {"_id": "10.71A"}} +{"name": "Fabrication industrielle de pain et de pâtisserie fraîche", "parent": "C"} +{"index": {"_id": "10.71B"}} +{"name": "Cuisson de produits de boulangerie", "parent": "C"} +{"index": {"_id": "10.71C"}} +{"name": "Boulangerie et boulangerie-pâtisserie", "parent": "C"} +{"index": {"_id": "10.71D"}} +{"name": "Pâtisserie", "parent": "C"} +{"index": {"_id": "10.72Z"}} +{"name": "Fabrication de biscuits, biscottes et pâtisseries de conservation", "parent": "C"} +{"index": {"_id": "10.73Z"}} +{"name": "Fabrication de pâtes alimentaires", "parent": "C"} +{"index": {"_id": "10.81Z"}} +{"name": "Fabrication de sucre", "parent": "C"} +{"index": {"_id": "10.82Z"}} +{"name": "Fabrication de cacao, chocolat et de produits de confiserie", "parent": "C"} +{"index": {"_id": "10.83Z"}} +{"name": "Transformation du thé et du café", "parent": "C"} +{"index": {"_id": "10.84Z"}} +{"name": "Fabrication de condiments et assaisonnements", "parent": "C"} +{"index": {"_id": "10.85Z"}} +{"name": "Fabrication de plats préparés", "parent": "C"} +{"index": {"_id": "10.86Z"}} +{"name": "Fabrication d'aliments homogénéisés et diététiques", "parent": "C"} +{"index": {"_id": "10.89Z"}} +{"name": "Fabrication d'autres produits alimentaires n.c.a.", "parent": "C"} +{"index": {"_id": "10.91Z"}} +{"name": "Fabrication d'aliments pour animaux de ferme", "parent": "C"} +{"index": {"_id": "10.92Z"}} +{"name": "Fabrication d'aliments pour animaux de compagnie", "parent": "C"} +{"index": {"_id": "11.01Z"}} +{"name": "Production de boissons alcooliques distillées", "parent": "C"} +{"index": {"_id": "11.02A"}} +{"name": "Fabrication de vins effervescents", "parent": "C"} +{"index": {"_id": "11.02B"}} +{"name": "Vinification", "parent": "C"} +{"index": {"_id": "11.03Z"}} +{"name": "Fabrication de cidre et de vins de fruits", "parent": "C"} +{"index": {"_id": "11.04Z"}} +{"name": "Production d'autres boissons fermentées non distillées", "parent": "C"} +{"index": {"_id": "11.05Z"}} +{"name": "Fabrication de bière", "parent": "C"} +{"index": {"_id": "11.06Z"}} +{"name": "Fabrication de malt", "parent": "C"} +{"index": {"_id": "11.07A"}} +{"name": "Industrie des eaux de table", "parent": "C"} +{"index": {"_id": "11.07B"}} +{"name": "Production de boissons rafraîchissantes", "parent": "C"} +{"index": {"_id": "12.00Z"}} +{"name": "Fabrication de produits à base de tabac", "parent": "C"} +{"index": {"_id": "13.10Z"}} +{"name": "Préparation de fibres textiles et filature", "parent": "C"} +{"index": {"_id": "13.20Z"}} +{"name": "Tissage", "parent": "C"} +{"index": {"_id": "13.30Z"}} +{"name": "Ennoblissement textile", "parent": "C"} +{"index": {"_id": "13.91Z"}} +{"name": "Fabrication d'étoffes à mailles", "parent": "C"} +{"index": {"_id": "13.92Z"}} +{"name": "Fabrication d'articles textiles, sauf habillement", "parent": "C"} +{"index": {"_id": "13.93Z"}} +{"name": "Fabrication de tapis et moquettes", "parent": "C"} +{"index": {"_id": "13.94Z"}} +{"name": "Fabrication de ficelles, cordes et filets", "parent": "C"} +{"index": {"_id": "13.95Z"}} +{"name": "Fabrication de non-tissés, sauf habillement", "parent": "C"} +{"index": {"_id": "13.96Z"}} +{"name": "Fabrication d'autres textiles techniques et industriels", "parent": "C"} +{"index": {"_id": "13.99Z"}} +{"name": "Fabrication d'autres textiles n.c.a.", "parent": "C"} +{"index": {"_id": "14.11Z"}} +{"name": "Fabrication de vêtements en cuir", "parent": "C"} +{"index": {"_id": "14.12Z"}} +{"name": "Fabrication de vêtements de travail", "parent": "C"} +{"index": {"_id": "14.13Z"}} +{"name": "Fabrication de vêtements de dessus", "parent": "C"} +{"index": {"_id": "14.14Z"}} +{"name": "Fabrication de vêtements de dessous", "parent": "C"} +{"index": {"_id": "14.19Z"}} +{"name": "Fabrication d'autres vêtements et accessoires", "parent": "C"} +{"index": {"_id": "14.20Z"}} +{"name": "Fabrication d'articles en fourrure", "parent": "C"} +{"index": {"_id": "14.31Z"}} +{"name": "Fabrication d'articles chaussants à mailles", "parent": "C"} +{"index": {"_id": "14.39Z"}} +{"name": "Fabrication d'autres articles à mailles", "parent": "C"} +{"index": {"_id": "15.11Z"}} +{"name": "Apprêt et tannage des cuirs ; préparation et teinture des fourrures", "parent": "C"} +{"index": {"_id": "15.12Z"}} +{"name": "Fabrication d'articles de voyage, de maroquinerie et de sellerie", "parent": "C"} +{"index": {"_id": "15.20Z"}} +{"name": "Fabrication de chaussures", "parent": "C"} +{"index": {"_id": "16.10A"}} +{"name": "Sciage et rabotage du bois, hors imprégnation", "parent": "C"} +{"index": {"_id": "16.10B"}} +{"name": "Imprégnation du bois", "parent": "C"} +{"index": {"_id": "16.21Z"}} +{"name": "Fabrication de placage et de panneaux de bois", "parent": "C"} +{"index": {"_id": "16.22Z"}} +{"name": "Fabrication de parquets assemblés", "parent": "C"} +{"index": {"_id": "16.23Z"}} +{"name": "Fabrication de charpentes et d'autres menuiseries", "parent": "C"} +{"index": {"_id": "16.24Z"}} +{"name": "Fabrication d'emballages en bois", "parent": "C"} +{"index": {"_id": "16.29Z"}} +{"name": "Fabrication d'objets divers en bois ; fabrication d'objets en liège, vannerie et sparterie", "parent": "C"} +{"index": {"_id": "17.11Z"}} +{"name": "Fabrication de pâte à papier", "parent": "C"} +{"index": {"_id": "17.12Z"}} +{"name": "Fabrication de papier et de carton", "parent": "C"} +{"index": {"_id": "17.21A"}} +{"name": "Fabrication de carton ondulé", "parent": "C"} +{"index": {"_id": "17.21B"}} +{"name": "Fabrication de cartonnages", "parent": "C"} +{"index": {"_id": "17.21C"}} +{"name": "Fabrication d'emballages en papier", "parent": "C"} +{"index": {"_id": "17.22Z"}} +{"name": "Fabrication d'articles en papier à usage sanitaire ou domestique", "parent": "C"} +{"index": {"_id": "17.23Z"}} +{"name": "Fabrication d'articles de papeterie", "parent": "C"} +{"index": {"_id": "17.24Z"}} +{"name": "Fabrication de papiers peints", "parent": "C"} +{"index": {"_id": "17.29Z"}} +{"name": "Fabrication d'autres articles en papier ou en carton", "parent": "C"} +{"index": {"_id": "18.11Z"}} +{"name": "Imprimerie de journaux", "parent": "C"} +{"index": {"_id": "18.12Z"}} +{"name": "Autre imprimerie (labeur)", "parent": "C"} +{"index": {"_id": "18.13Z"}} +{"name": "Activités de pré-presse", "parent": "C"} +{"index": {"_id": "18.14Z"}} +{"name": "Reliure et activités connexes", "parent": "C"} +{"index": {"_id": "18.20Z"}} +{"name": "Reproduction d'enregistrements", "parent": "C"} +{"index": {"_id": "19.10Z"}} +{"name": "Cokéfaction", "parent": "C"} +{"index": {"_id": "19.20Z"}} +{"name": "Raffinage du pétrole", "parent": "C"} +{"index": {"_id": "20.11Z"}} +{"name": "Fabrication de gaz industriels", "parent": "C"} +{"index": {"_id": "20.12Z"}} +{"name": "Fabrication de colorants et de pigments", "parent": "C"} +{"index": {"_id": "20.13A"}} +{"name": "Enrichissement et retraitement de matières nucléaires", "parent": "C"} +{"index": {"_id": "20.13B"}} +{"name": "Fabrication d'autres produits chimiques inorganiques de base n.c.a.", "parent": "C"} +{"index": {"_id": "20.14Z"}} +{"name": "Fabrication d'autres produits chimiques organiques de base", "parent": "C"} +{"index": {"_id": "20.15Z"}} +{"name": "Fabrication de produits azotés et d'engrais", "parent": "C"} +{"index": {"_id": "20.16Z"}} +{"name": "Fabrication de matières plastiques de base", "parent": "C"} +{"index": {"_id": "20.17Z"}} +{"name": "Fabrication de caoutchouc synthétique", "parent": "C"} +{"index": {"_id": "20.20Z"}} +{"name": "Fabrication de pesticides et d'autres produits agrochimiques", "parent": "C"} +{"index": {"_id": "20.30Z"}} +{"name": "Fabrication de peintures, vernis, encres et mastics", "parent": "C"} +{"index": {"_id": "20.41Z"}} +{"name": "Fabrication de savons, détergents et produits d'entretien", "parent": "C"} +{"index": {"_id": "20.42Z"}} +{"name": "Fabrication de parfums et de produits pour la toilette", "parent": "C"} +{"index": {"_id": "20.51Z"}} +{"name": "Fabrication de produits explosifs", "parent": "C"} +{"index": {"_id": "20.52Z"}} +{"name": "Fabrication de colles", "parent": "C"} +{"index": {"_id": "20.53Z"}} +{"name": "Fabrication d'huiles essentielles", "parent": "C"} +{"index": {"_id": "20.59Z"}} +{"name": "Fabrication d'autres produits chimiques n.c.a.", "parent": "C"} +{"index": {"_id": "20.60Z"}} +{"name": "Fabrication de fibres artificielles ou synthétiques", "parent": "C"} +{"index": {"_id": "21.10Z"}} +{"name": "Fabrication de produits pharmaceutiques de base", "parent": "C"} +{"index": {"_id": "21.20Z"}} +{"name": "Fabrication de préparations pharmaceutiques", "parent": "C"} +{"index": {"_id": "22.11Z"}} +{"name": "Fabrication et rechapage de pneumatiques", "parent": "C"} +{"index": {"_id": "22.19Z"}} +{"name": "Fabrication d'autres articles en caoutchouc", "parent": "C"} +{"index": {"_id": "22.21Z"}} +{"name": "Fabrication de plaques, feuilles, tubes et profilés en matières plastiques", "parent": "C"} +{"index": {"_id": "22.22Z"}} +{"name": "Fabrication d'emballages en matières plastiques", "parent": "C"} +{"index": {"_id": "22.23Z"}} +{"name": "Fabrication d'éléments en matières plastiques pour la construction", "parent": "C"} +{"index": {"_id": "22.29A"}} +{"name": "Fabrication de pièces techniques à base de matières plastiques", "parent": "C"} +{"index": {"_id": "22.29B"}} +{"name": "Fabrication de produits de consommation courante en matières plastiques", "parent": "C"} +{"index": {"_id": "23.11Z"}} +{"name": "Fabrication de verre plat", "parent": "C"} +{"index": {"_id": "23.12Z"}} +{"name": "Façonnage et transformation du verre plat", "parent": "C"} +{"index": {"_id": "23.13Z"}} +{"name": "Fabrication de verre creux", "parent": "C"} +{"index": {"_id": "23.14Z"}} +{"name": "Fabrication de fibres de verre", "parent": "C"} +{"index": {"_id": "23.19Z"}} +{"name": "Fabrication et façonnage d'autres articles en verre, y compris verre technique", "parent": "C"} +{"index": {"_id": "23.20Z"}} +{"name": "Fabrication de produits réfractaires", "parent": "C"} +{"index": {"_id": "23.31Z"}} +{"name": "Fabrication de carreaux en céramique", "parent": "C"} +{"index": {"_id": "23.32Z"}} +{"name": "Fabrication de briques, tuiles et produits de construction, en terre cuite", "parent": "C"} +{"index": {"_id": "23.41Z"}} +{"name": "Fabrication d'articles céramiques à usage domestique ou ornemental", "parent": "C"} +{"index": {"_id": "23.42Z"}} +{"name": "Fabrication d'appareils sanitaires en céramique", "parent": "C"} +{"index": {"_id": "23.43Z"}} +{"name": "Fabrication d'isolateurs et pièces isolantes en céramique", "parent": "C"} +{"index": {"_id": "23.44Z"}} +{"name": "Fabrication d'autres produits céramiques à usage technique", "parent": "C"} +{"index": {"_id": "23.49Z"}} +{"name": "Fabrication d'autres produits céramiques", "parent": "C"} +{"index": {"_id": "23.51Z"}} +{"name": "Fabrication de ciment", "parent": "C"} +{"index": {"_id": "23.52Z"}} +{"name": "Fabrication de chaux et plâtre", "parent": "C"} +{"index": {"_id": "23.61Z"}} +{"name": "Fabrication d'éléments en béton pour la construction", "parent": "C"} +{"index": {"_id": "23.62Z"}} +{"name": "Fabrication d'éléments en plâtre pour la construction", "parent": "C"} +{"index": {"_id": "23.63Z"}} +{"name": "Fabrication de béton prêt à l'emploi", "parent": "C"} +{"index": {"_id": "23.64Z"}} +{"name": "Fabrication de mortiers et bétons secs", "parent": "C"} +{"index": {"_id": "23.65Z"}} +{"name": "Fabrication d'ouvrages en fibre-ciment", "parent": "C"} +{"index": {"_id": "23.69Z"}} +{"name": "Fabrication d'autres ouvrages en béton, en ciment ou en plâtre", "parent": "C"} +{"index": {"_id": "23.70Z"}} +{"name": "Taille, façonnage et finissage de pierres", "parent": "C"} +{"index": {"_id": "23.91Z"}} +{"name": "Fabrication de produits abrasifs", "parent": "C"} +{"index": {"_id": "23.99Z"}} +{"name": "Fabrication d'autres produits minéraux non métalliques n.c.a.", "parent": "C"} +{"index": {"_id": "24.10Z"}} +{"name": "Sidérurgie", "parent": "C"} +{"index": {"_id": "24.20Z"}} +{"name": "Fabrication de tubes, tuyaux, profilés creux et accessoires correspondants en acier", "parent": "C"} +{"index": {"_id": "24.31Z"}} +{"name": "Étirage à froid de barres", "parent": "C"} +{"index": {"_id": "24.32Z"}} +{"name": "Laminage à froid de feuillards", "parent": "C"} +{"index": {"_id": "24.33Z"}} +{"name": "Profilage à froid par formage ou pliage", "parent": "C"} +{"index": {"_id": "24.34Z"}} +{"name": "Tréfilage à froid", "parent": "C"} +{"index": {"_id": "24.41Z"}} +{"name": "Production de métaux précieux", "parent": "C"} +{"index": {"_id": "24.42Z"}} +{"name": "Métallurgie de l'aluminium", "parent": "C"} +{"index": {"_id": "24.43Z"}} +{"name": "Métallurgie du plomb, du zinc ou de l'étain", "parent": "C"} +{"index": {"_id": "24.44Z"}} +{"name": "Métallurgie du cuivre", "parent": "C"} +{"index": {"_id": "24.45Z"}} +{"name": "Métallurgie des autres métaux non ferreux", "parent": "C"} +{"index": {"_id": "24.46Z"}} +{"name": "Élaboration et transformation de matières nucléaires", "parent": "C"} +{"index": {"_id": "24.51Z"}} +{"name": "Fonderie de fonte", "parent": "C"} +{"index": {"_id": "24.52Z"}} +{"name": "Fonderie d'acier", "parent": "C"} +{"index": {"_id": "24.53Z"}} +{"name": "Fonderie de métaux légers", "parent": "C"} +{"index": {"_id": "24.54Z"}} +{"name": "Fonderie d'autres métaux non ferreux", "parent": "C"} +{"index": {"_id": "25.11Z"}} +{"name": "Fabrication de structures métalliques et de parties de structures", "parent": "C"} +{"index": {"_id": "25.12Z"}} +{"name": "Fabrication de portes et fenêtres en métal", "parent": "C"} +{"index": {"_id": "25.21Z"}} +{"name": "Fabrication de radiateurs et de chaudières pour le chauffage central", "parent": "C"} +{"index": {"_id": "25.29Z"}} +{"name": "Fabrication d'autres réservoirs, citernes et conteneurs métalliques", "parent": "C"} +{"index": {"_id": "25.30Z"}} +{"name": "Fabrication de générateurs de vapeur, à l'exception des chaudières pour le chauffage central", "parent": "C"} +{"index": {"_id": "25.40Z"}} +{"name": "Fabrication d'armes et de munitions", "parent": "C"} +{"index": {"_id": "25.50A"}} +{"name": "Forge, estampage, matriçage ; métallurgie des poudres", "parent": "C"} +{"index": {"_id": "25.50B"}} +{"name": "Découpage, emboutissage", "parent": "C"} +{"index": {"_id": "25.61Z"}} +{"name": "Traitement et revêtement des métaux", "parent": "C"} +{"index": {"_id": "25.62A"}} +{"name": "Décolletage", "parent": "C"} +{"index": {"_id": "25.62B"}} +{"name": "Mécanique industrielle", "parent": "C"} +{"index": {"_id": "25.71Z"}} +{"name": "Fabrication de coutellerie", "parent": "C"} +{"index": {"_id": "25.72Z"}} +{"name": "Fabrication de serrures et de ferrures", "parent": "C"} +{"index": {"_id": "25.73A"}} +{"name": "Fabrication de moules et modèles", "parent": "C"} +{"index": {"_id": "25.73B"}} +{"name": "Fabrication d'autres outillages", "parent": "C"} +{"index": {"_id": "25.91Z"}} +{"name": "Fabrication de fûts et emballages métalliques similaires", "parent": "C"} +{"index": {"_id": "25.92Z"}} +{"name": "Fabrication d'emballages métalliques légers", "parent": "C"} +{"index": {"_id": "25.93Z"}} +{"name": "Fabrication d'articles en fils métalliques, de chaînes et de ressorts", "parent": "C"} +{"index": {"_id": "25.94Z"}} +{"name": "Fabrication de vis et de boulons", "parent": "C"} +{"index": {"_id": "25.99A"}} +{"name": "Fabrication d'articles métalliques ménagers", "parent": "C"} +{"index": {"_id": "25.99B"}} +{"name": "Fabrication d'autres articles métalliques", "parent": "C"} +{"index": {"_id": "26.11Z"}} +{"name": "Fabrication de composants électroniques", "parent": "C"} +{"index": {"_id": "26.12Z"}} +{"name": "Fabrication de cartes électroniques assemblées", "parent": "C"} +{"index": {"_id": "26.20Z"}} +{"name": "Fabrication d'ordinateurs et d'équipements périphériques", "parent": "C"} +{"index": {"_id": "26.30Z"}} +{"name": "Fabrication d'équipements de communication", "parent": "C"} +{"index": {"_id": "26.40Z"}} +{"name": "Fabrication de produits électroniques grand public", "parent": "C"} +{"index": {"_id": "26.51A"}} +{"name": "Fabrication d'équipements d'aide à la navigation", "parent": "C"} +{"index": {"_id": "26.51B"}} +{"name": "Fabrication d'instrumentation scientifique et technique", "parent": "C"} +{"index": {"_id": "26.52Z"}} +{"name": "Horlogerie", "parent": "C"} +{"index": {"_id": "26.60Z"}} +{"name": "Fabrication d'équipements d'irradiation médicale, d'équipements électromédicaux et électrothérapeutiques", "parent": "C"} +{"index": {"_id": "26.70Z"}} +{"name": "Fabrication de matériels optique et photographique", "parent": "C"} +{"index": {"_id": "26.80Z"}} +{"name": "Fabrication de supports magnétiques et optiques", "parent": "C"} +{"index": {"_id": "27.11Z"}} +{"name": "Fabrication de moteurs, génératrices et transformateurs électriques", "parent": "C"} +{"index": {"_id": "27.12Z"}} +{"name": "Fabrication de matériel de distribution et de commande électrique", "parent": "C"} +{"index": {"_id": "27.20Z"}} +{"name": "Fabrication de piles et d'accumulateurs électriques", "parent": "C"} +{"index": {"_id": "27.31Z"}} +{"name": "Fabrication de câbles de fibres optiques", "parent": "C"} +{"index": {"_id": "27.32Z"}} +{"name": "Fabrication d'autres fils et câbles électroniques ou électriques", "parent": "C"} +{"index": {"_id": "27.33Z"}} +{"name": "Fabrication de matériel d'installation électrique", "parent": "C"} +{"index": {"_id": "27.40Z"}} +{"name": "Fabrication d'appareils d'éclairage électrique", "parent": "C"} +{"index": {"_id": "27.51Z"}} +{"name": "Fabrication d'appareils électroménagers", "parent": "C"} +{"index": {"_id": "27.52Z"}} +{"name": "Fabrication d'appareils ménagers non électriques", "parent": "C"} +{"index": {"_id": "27.90Z"}} +{"name": "Fabrication d'autres matériels électriques", "parent": "C"} +{"index": {"_id": "28.11Z"}} +{"name": "Fabrication de moteurs et turbines, à l'exception des moteurs d'avions et de véhicules", "parent": "C"} +{"index": {"_id": "28.12Z"}} +{"name": "Fabrication d'équipements hydrauliques et pneumatiques", "parent": "C"} +{"index": {"_id": "28.13Z"}} +{"name": "Fabrication d'autres pompes et compresseurs", "parent": "C"} +{"index": {"_id": "28.14Z"}} +{"name": "Fabrication d'autres articles de robinetterie", "parent": "C"} +{"index": {"_id": "28.15Z"}} +{"name": "Fabrication d'engrenages et d'organes mécaniques de transmission", "parent": "C"} +{"index": {"_id": "28.21Z"}} +{"name": "Fabrication de fours et brûleurs", "parent": "C"} +{"index": {"_id": "28.22Z"}} +{"name": "Fabrication de matériel de levage et de manutention", "parent": "C"} +{"index": {"_id": "28.23Z"}} +{"name": "Fabrication de machines et d'équipements de bureau (à l'exception des ordinateurs et équipements périphériques)", "parent": "C"} +{"index": {"_id": "28.24Z"}} +{"name": "Fabrication d'outillage portatif à moteur incorporé", "parent": "C"} +{"index": {"_id": "28.25Z"}} +{"name": "Fabrication d'équipements aérauliques et frigorifiques industriels", "parent": "C"} +{"index": {"_id": "28.29A"}} +{"name": "Fabrication d'équipements d'emballage, de conditionnement et de pesage", "parent": "C"} +{"index": {"_id": "28.29B"}} +{"name": "Fabrication d'autres machines d'usage général", "parent": "C"} +{"index": {"_id": "28.30Z"}} +{"name": "Fabrication de machines agricoles et forestières", "parent": "C"} +{"index": {"_id": "28.41Z"}} +{"name": "Fabrication de machines-outils pour le travail des métaux", "parent": "C"} +{"index": {"_id": "28.49Z"}} +{"name": "Fabrication d'autres machines-outils", "parent": "C"} +{"index": {"_id": "28.91Z"}} +{"name": "Fabrication de machines pour la métallurgie", "parent": "C"} +{"index": {"_id": "28.92Z"}} +{"name": "Fabrication de machines pour l'extraction ou la construction", "parent": "C"} +{"index": {"_id": "28.93Z"}} +{"name": "Fabrication de machines pour l'industrie agro-alimentaire", "parent": "C"} +{"index": {"_id": "28.94Z"}} +{"name": "Fabrication de machines pour les industries textiles", "parent": "C"} +{"index": {"_id": "28.95Z"}} +{"name": "Fabrication de machines pour les industries du papier et du carton", "parent": "C"} +{"index": {"_id": "28.96Z"}} +{"name": "Fabrication de machines pour le travail du caoutchouc ou des plastiques", "parent": "C"} +{"index": {"_id": "28.99A"}} +{"name": "Fabrication de machines d'imprimerie", "parent": "C"} +{"index": {"_id": "28.99B"}} +{"name": "Fabrication d'autres machines spécialisées", "parent": "C"} +{"index": {"_id": "29.10Z"}} +{"name": "Construction de véhicules automobiles", "parent": "C"} +{"index": {"_id": "29.20Z"}} +{"name": "Fabrication de carrosseries et remorques", "parent": "C"} +{"index": {"_id": "29.31Z"}} +{"name": "Fabrication d'équipements électriques et électroniques automobiles", "parent": "C"} +{"index": {"_id": "29.32Z"}} +{"name": "Fabrication d'autres équipements automobiles", "parent": "C"} +{"index": {"_id": "30.11Z"}} +{"name": "Construction de navires et de structures flottantes", "parent": "C"} +{"index": {"_id": "30.12Z"}} +{"name": "Construction de bateaux de plaisance", "parent": "C"} +{"index": {"_id": "30.20Z"}} +{"name": "Construction de locomotives et d'autre matériel ferroviaire roulant", "parent": "C"} +{"index": {"_id": "30.30Z"}} +{"name": "Construction aéronautique et spatiale", "parent": "C"} +{"index": {"_id": "30.40Z"}} +{"name": "Construction de véhicules militaires de combat", "parent": "C"} +{"index": {"_id": "30.91Z"}} +{"name": "Fabrication de motocycles", "parent": "C"} +{"index": {"_id": "30.92Z"}} +{"name": "Fabrication de bicyclettes et de véhicules pour invalides", "parent": "C"} +{"index": {"_id": "30.99Z"}} +{"name": "Fabrication d'autres équipements de transport n.c.a.", "parent": "C"} +{"index": {"_id": "31.01Z"}} +{"name": "Fabrication de meubles de bureau et de magasin", "parent": "C"} +{"index": {"_id": "31.02Z"}} +{"name": "Fabrication de meubles de cuisine", "parent": "C"} +{"index": {"_id": "31.03Z"}} +{"name": "Fabrication de matelas", "parent": "C"} +{"index": {"_id": "31.09A"}} +{"name": "Fabrication de sièges d'ameublement d'intérieur", "parent": "C"} +{"index": {"_id": "31.09B"}} +{"name": "Fabrication d'autres meubles et industries connexes de l'ameublement", "parent": "C"} +{"index": {"_id": "32.11Z"}} +{"name": "Frappe de monnaie", "parent": "C"} +{"index": {"_id": "32.12Z"}} +{"name": "Fabrication d'articles de joaillerie et bijouterie", "parent": "C"} +{"index": {"_id": "32.13Z"}} +{"name": "Fabrication d'articles de bijouterie fantaisie et articles similaires", "parent": "C"} +{"index": {"_id": "32.20Z"}} +{"name": "Fabrication d'instruments de musique", "parent": "C"} +{"index": {"_id": "32.30Z"}} +{"name": "Fabrication d'articles de sport", "parent": "C"} +{"index": {"_id": "32.40Z"}} +{"name": "Fabrication de jeux et jouets", "parent": "C"} +{"index": {"_id": "32.50A"}} +{"name": "Fabrication de matériel médico-chirurgical et dentaire", "parent": "C"} +{"index": {"_id": "32.50B"}} +{"name": "Fabrication de lunettes", "parent": "C"} +{"index": {"_id": "32.91Z"}} +{"name": "Fabrication d'articles de brosserie", "parent": "C"} +{"index": {"_id": "32.99Z"}} +{"name": "Autres activités manufacturières n.c.a.", "parent": "C"} +{"index": {"_id": "33.11Z"}} +{"name": "Réparation d'ouvrages en métaux", "parent": "C"} +{"index": {"_id": "33.12Z"}} +{"name": "Réparation de machines et équipements mécaniques", "parent": "C"} +{"index": {"_id": "33.13Z"}} +{"name": "Réparation de matériels électroniques et optiques", "parent": "C"} +{"index": {"_id": "33.14Z"}} +{"name": "Réparation d'équipements électriques", "parent": "C"} +{"index": {"_id": "33.15Z"}} +{"name": "Réparation et maintenance navale", "parent": "C"} +{"index": {"_id": "33.16Z"}} +{"name": "Réparation et maintenance d'aéronefs et d'engins spatiaux", "parent": "C"} +{"index": {"_id": "33.17Z"}} +{"name": "Réparation et maintenance d'autres équipements de transport", "parent": "C"} +{"index": {"_id": "33.19Z"}} +{"name": "Réparation d'autres équipements", "parent": "C"} +{"index": {"_id": "33.20A"}} +{"name": "Installation de structures métalliques, chaudronnées et de tuyauterie", "parent": "C"} +{"index": {"_id": "33.20B"}} +{"name": "Installation de machines et équipements mécaniques", "parent": "C"} +{"index": {"_id": "33.20C"}} +{"name": "Conception d'ensemble et assemblage sur site industriel d'équipements de contrôle des processus industriels", "parent": "C"} +{"index": {"_id": "33.20D"}} +{"name": "Installation d'équipements électriques, de matériels électroniques et optiques ou d'autres matériels", "parent": "C"} +{"index": {"_id": "D"}} +{"name": "Production et distribution d'électricité, de gaz, de vapeur et d'air conditionné", "parent": null} +{"index": {"_id": "35.11Z"}} +{"name": "Production d'électricité", "parent": "D"} +{"index": {"_id": "35.12Z"}} +{"name": "Transport d'électricité", "parent": "D"} +{"index": {"_id": "35.13Z"}} +{"name": "Distribution d'électricité", "parent": "D"} +{"index": {"_id": "35.14Z"}} +{"name": "Commerce d'électricité", "parent": "D"} +{"index": {"_id": "35.21Z"}} +{"name": "Production de combustibles gazeux", "parent": "D"} +{"index": {"_id": "35.22Z"}} +{"name": "Distribution de combustibles gazeux par conduites", "parent": "D"} +{"index": {"_id": "35.23Z"}} +{"name": "Commerce de combustibles gazeux par conduites", "parent": "D"} +{"index": {"_id": "35.30Z"}} +{"name": "Production et distribution de vapeur et d'air conditionné", "parent": "D"} +{"index": {"_id": "E"}} +{"name": "Production et distribution d'eau ; assainissement, gestion des déchets et dépollution", "parent": null} +{"index": {"_id": "36.00Z"}} +{"name": "Captage, traitement et distribution d'eau", "parent": "E"} +{"index": {"_id": "37.00Z"}} +{"name": "Collecte et traitement des eaux usées", "parent": "E"} +{"index": {"_id": "38.11Z"}} +{"name": "Collecte des déchets non dangereux", "parent": "E"} +{"index": {"_id": "38.12Z"}} +{"name": "Collecte des déchets dangereux", "parent": "E"} +{"index": {"_id": "38.21Z"}} +{"name": "Traitement et élimination des déchets non dangereux", "parent": "E"} +{"index": {"_id": "38.22Z"}} +{"name": "Traitement et élimination des déchets dangereux", "parent": "E"} +{"index": {"_id": "38.31Z"}} +{"name": "Démantèlement d'épaves", "parent": "E"} +{"index": {"_id": "38.32Z"}} +{"name": "Récupération de déchets triés", "parent": "E"} +{"index": {"_id": "39.00Z"}} +{"name": "Dépollution et autres services de gestion des déchets", "parent": "E"} +{"index": {"_id": "F"}} +{"name": "Construction", "parent": null} +{"index": {"_id": "41.10A"}} +{"name": "Promotion immobilière de logements", "parent": "F"} +{"index": {"_id": "41.10B"}} +{"name": "Promotion immobilière de bureaux", "parent": "F"} +{"index": {"_id": "41.10C"}} +{"name": "Promotion immobilière d'autres bâtiments", "parent": "F"} +{"index": {"_id": "41.10D"}} +{"name": "Supports juridiques de programmes", "parent": "F"} +{"index": {"_id": "41.20A"}} +{"name": "Construction de maisons individuelles", "parent": "F"} +{"index": {"_id": "41.20B"}} +{"name": "Construction d'autres bâtiments", "parent": "F"} +{"index": {"_id": "42.11Z"}} +{"name": "Construction de routes et autoroutes", "parent": "F"} +{"index": {"_id": "42.12Z"}} +{"name": "Construction de voies ferrées de surface et souterraines", "parent": "F"} +{"index": {"_id": "42.13A"}} +{"name": "Construction d'ouvrages d'art", "parent": "F"} +{"index": {"_id": "42.13B"}} +{"name": "Construction et entretien de tunnels", "parent": "F"} +{"index": {"_id": "42.21Z"}} +{"name": "Construction de réseaux pour fluides", "parent": "F"} +{"index": {"_id": "42.22Z"}} +{"name": "Construction de réseaux électriques et de télécommunications", "parent": "F"} +{"index": {"_id": "42.91Z"}} +{"name": "Construction d'ouvrages maritimes et fluviaux", "parent": "F"} +{"index": {"_id": "42.99Z"}} +{"name": "Construction d'autres ouvrages de génie civil n.c.a.", "parent": "F"} +{"index": {"_id": "43.11Z"}} +{"name": "Travaux de démolition", "parent": "F"} +{"index": {"_id": "43.12A"}} +{"name": "Travaux de terrassement courants et travaux préparatoires", "parent": "F"} +{"index": {"_id": "43.12B"}} +{"name": "Travaux de terrassement spécialisés ou de grande masse", "parent": "F"} +{"index": {"_id": "43.13Z"}} +{"name": "Forages et sondages", "parent": "F"} +{"index": {"_id": "43.21A"}} +{"name": "Travaux d'installation électrique dans tous locaux", "parent": "F"} +{"index": {"_id": "43.21B"}} +{"name": "Travaux d'installation électrique sur la voie publique", "parent": "F"} +{"index": {"_id": "43.22A"}} +{"name": "Travaux d'installation d'eau et de gaz en tous locaux", "parent": "F"} +{"index": {"_id": "43.22B"}} +{"name": "Travaux d'installation d'équipements thermiques et de climatisation", "parent": "F"} +{"index": {"_id": "43.29A"}} +{"name": "Travaux d'isolation", "parent": "F"} +{"index": {"_id": "43.29B"}} +{"name": "Autres travaux d'installation n.c.a.", "parent": "F"} +{"index": {"_id": "43.31Z"}} +{"name": "Travaux de plâtrerie", "parent": "F"} +{"index": {"_id": "43.32A"}} +{"name": "Travaux de menuiserie bois et PVC", "parent": "F"} +{"index": {"_id": "43.32B"}} +{"name": "Travaux de menuiserie métallique et serrurerie", "parent": "F"} +{"index": {"_id": "43.32C"}} +{"name": "Agencement de lieux de vente", "parent": "F"} +{"index": {"_id": "43.33Z"}} +{"name": "Travaux de revêtement des sols et des murs", "parent": "F"} +{"index": {"_id": "43.34Z"}} +{"name": "Travaux de peinture et vitrerie", "parent": "F"} +{"index": {"_id": "43.39Z"}} +{"name": "Autres travaux de finition", "parent": "F"} +{"index": {"_id": "43.91A"}} +{"name": "Travaux de charpente", "parent": "F"} +{"index": {"_id": "43.91B"}} +{"name": "Travaux de couverture par éléments", "parent": "F"} +{"index": {"_id": "43.99A"}} +{"name": "Travaux d'étanchéification", "parent": "F"} +{"index": {"_id": "43.99B"}} +{"name": "Travaux de montage de structures métalliques", "parent": "F"} +{"index": {"_id": "43.99C"}} +{"name": "Travaux de maçonnerie générale et gros œuvre de bâtiment", "parent": "F"} +{"index": {"_id": "43.99D"}} +{"name": "Autres travaux spécialisés de construction", "parent": "F"} +{"index": {"_id": "43.99E"}} +{"name": "Location avec opérateur de matériel de construction", "parent": "F"} +{"index": {"_id": "G"}} +{"name": "Commerce ; réparation d'automobiles et de motocycles", "parent": null} +{"index": {"_id": "45.11Z"}} +{"name": "Commerce de voitures et de véhicules automobiles légers", "parent": "G"} +{"index": {"_id": "45.19Z"}} +{"name": "Commerce d'autres véhicules automobiles", "parent": "G"} +{"index": {"_id": "45.20A"}} +{"name": "Entretien et réparation de véhicules automobiles légers", "parent": "G"} +{"index": {"_id": "45.20B"}} +{"name": "Entretien et réparation d'autres véhicules automobiles", "parent": "G"} +{"index": {"_id": "45.31Z"}} +{"name": "Commerce de gros d'équipements automobiles", "parent": "G"} +{"index": {"_id": "45.32Z"}} +{"name": "Commerce de détail d'équipements automobiles", "parent": "G"} +{"index": {"_id": "45.40Z"}} +{"name": "Commerce et réparation de motocycles", "parent": "G"} +{"index": {"_id": "46.11Z"}} +{"name": "Intermédiaires du commerce en matières premières agricoles, animaux vivants, matières premières textiles et produits semi-finis", "parent": "G"} +{"index": {"_id": "46.12A"}} +{"name": "Centrales d'achat de carburant", "parent": "G"} +{"index": {"_id": "46.12B"}} +{"name": "Autres intermédiaires du commerce en combustibles, métaux, minéraux et produits chimiques", "parent": "G"} +{"index": {"_id": "46.13Z"}} +{"name": "Intermédiaires du commerce en bois et matériaux de construction", "parent": "G"} +{"index": {"_id": "46.14Z"}} +{"name": "Intermédiaires du commerce en machines, équipements industriels, navires et avions", "parent": "G"} +{"index": {"_id": "46.15Z"}} +{"name": "Intermédiaires du commerce en meubles, articles de ménage et quincaillerie", "parent": "G"} +{"index": {"_id": "46.16Z"}} +{"name": "Intermédiaires du commerce en textiles, habillement, fourrures, chaussures et articles en cuir", "parent": "G"} +{"index": {"_id": "46.17A"}} +{"name": "Centrales d'achat alimentaires", "parent": "G"} +{"index": {"_id": "46.17B"}} +{"name": "Autres intermédiaires du commerce en denrées, boissons et tabac", "parent": "G"} +{"index": {"_id": "46.18Z"}} +{"name": "Intermédiaires spécialisés dans le commerce d'autres produits spécifiques", "parent": "G"} +{"index": {"_id": "46.19A"}} +{"name": "Centrales d'achat non alimentaires", "parent": "G"} +{"index": {"_id": "46.19B"}} +{"name": "Autres intermédiaires du commerce en produits divers", "parent": "G"} +{"index": {"_id": "46.21Z"}} +{"name": "Commerce de gros (commerce interentreprises) de céréales, de tabac non manufacturé, de semences et d'aliments pour le bétail", "parent": "G"} +{"index": {"_id": "46.22Z"}} +{"name": "Commerce de gros (commerce interentreprises) de fleurs et plantes", "parent": "G"} +{"index": {"_id": "46.23Z"}} +{"name": "Commerce de gros (commerce interentreprises) d'animaux vivants", "parent": "G"} +{"index": {"_id": "46.24Z"}} +{"name": "Commerce de gros (commerce interentreprises) de cuirs et peaux", "parent": "G"} +{"index": {"_id": "46.31Z"}} +{"name": "Commerce de gros (commerce interentreprises) de fruits et légumes", "parent": "G"} +{"index": {"_id": "46.32A"}} +{"name": "Commerce de gros (commerce interentreprises) de viandes de boucherie", "parent": "G"} +{"index": {"_id": "46.32B"}} +{"name": "Commerce de gros (commerce interentreprises) de produits à base de viande", "parent": "G"} +{"index": {"_id": "46.32C"}} +{"name": "Commerce de gros (commerce interentreprises) de volailles et gibier", "parent": "G"} +{"index": {"_id": "46.33Z"}} +{"name": "Commerce de gros (commerce interentreprises) de produits laitiers, œufs, huiles et matières grasses comestibles", "parent": "G"} +{"index": {"_id": "46.34Z"}} +{"name": "Commerce de gros (commerce interentreprises) de boissons", "parent": "G"} +{"index": {"_id": "46.35Z"}} +{"name": "Commerce de gros (commerce interentreprises) de produits à base de tabac", "parent": "G"} +{"index": {"_id": "46.36Z"}} +{"name": "Commerce de gros (commerce interentreprises) de sucre, chocolat et confiserie", "parent": "G"} +{"index": {"_id": "46.37Z"}} +{"name": "Commerce de gros (commerce interentreprises) de café, thé, cacao et épices", "parent": "G"} +{"index": {"_id": "46.38A"}} +{"name": "Commerce de gros (commerce interentreprises) de poissons, crustacés et mollusques", "parent": "G"} +{"index": {"_id": "46.38B"}} +{"name": "Commerce de gros (commerce interentreprises) alimentaire spécialisé divers", "parent": "G"} +{"index": {"_id": "46.39A"}} +{"name": "Commerce de gros (commerce interentreprises) de produits surgelés", "parent": "G"} +{"index": {"_id": "46.39B"}} +{"name": "Commerce de gros (commerce interentreprises) alimentaire non spécialisé", "parent": "G"} +{"index": {"_id": "46.41Z"}} +{"name": "Commerce de gros (commerce interentreprises) de textiles", "parent": "G"} +{"index": {"_id": "46.42Z"}} +{"name": "Commerce de gros (commerce interentreprises) d'habillement et de chaussures", "parent": "G"} +{"index": {"_id": "46.43Z"}} +{"name": "Commerce de gros (commerce interentreprises) d'appareils électroménagers", "parent": "G"} +{"index": {"_id": "46.44Z"}} +{"name": "Commerce de gros (commerce interentreprises) de vaisselle, verrerie et produits d'entretien", "parent": "G"} +{"index": {"_id": "46.45Z"}} +{"name": "Commerce de gros (commerce interentreprises) de parfumerie et de produits de beauté", "parent": "G"} +{"index": {"_id": "46.46Z"}} +{"name": "Commerce de gros (commerce interentreprises) de produits pharmaceutiques", "parent": "G"} +{"index": {"_id": "46.47Z"}} +{"name": "Commerce de gros (commerce interentreprises) de meubles, de tapis et d'appareils d'éclairage", "parent": "G"} +{"index": {"_id": "46.48Z"}} +{"name": "Commerce de gros (commerce interentreprises) d'articles d'horlogerie et de bijouterie", "parent": "G"} +{"index": {"_id": "46.49Z"}} +{"name": "Commerce de gros (commerce interentreprises) d'autres biens domestiques", "parent": "G"} +{"index": {"_id": "46.51Z"}} +{"name": "Commerce de gros (commerce interentreprises) d'ordinateurs, d'équipements informatiques périphériques et de logiciels", "parent": "G"} +{"index": {"_id": "46.52Z"}} +{"name": "Commerce de gros (commerce interentreprises) de composants et d'équipements électroniques et de télécommunication", "parent": "G"} +{"index": {"_id": "46.61Z"}} +{"name": "Commerce de gros (commerce interentreprises) de matériel agricole", "parent": "G"} +{"index": {"_id": "46.62Z"}} +{"name": "Commerce de gros (commerce interentreprises) de machines-outils", "parent": "G"} +{"index": {"_id": "46.63Z"}} +{"name": "Commerce de gros (commerce interentreprises) de machines pour l'extraction, la construction et le génie civil", "parent": "G"} +{"index": {"_id": "46.64Z"}} +{"name": "Commerce de gros (commerce interentreprises) de machines pour l'industrie textile et l'habillement", "parent": "G"} +{"index": {"_id": "46.65Z"}} +{"name": "Commerce de gros (commerce interentreprises) de mobilier de bureau", "parent": "G"} +{"index": {"_id": "46.66Z"}} +{"name": "Commerce de gros (commerce interentreprises) d'autres machines et équipements de bureau", "parent": "G"} +{"index": {"_id": "46.69A"}} +{"name": "Commerce de gros (commerce interentreprises) de matériel électrique", "parent": "G"} +{"index": {"_id": "46.69B"}} +{"name": "Commerce de gros (commerce interentreprises) de fournitures et équipements industriels divers", "parent": "G"} +{"index": {"_id": "46.69C"}} +{"name": "Commerce de gros (commerce interentreprises) de fournitures et équipements divers pour le commerce et les services", "parent": "G"} +{"index": {"_id": "46.71Z"}} +{"name": "Commerce de gros (commerce interentreprises) de combustibles et de produits annexes", "parent": "G"} +{"index": {"_id": "46.72Z"}} +{"name": "Commerce de gros (commerce interentreprises) de minerais et métaux", "parent": "G"} +{"index": {"_id": "46.73A"}} +{"name": "Commerce de gros (commerce interentreprises) de bois et de matériaux de construction", "parent": "G"} +{"index": {"_id": "46.73B"}} +{"name": "Commerce de gros (commerce interentreprises) d'appareils sanitaires et de produits de décoration", "parent": "G"} +{"index": {"_id": "46.74A"}} +{"name": "Commerce de gros (commerce interentreprises) de quincaillerie", "parent": "G"} +{"index": {"_id": "46.74B"}} +{"name": "Commerce de gros (commerce interentreprises) de fournitures pour la plomberie et le chauffage", "parent": "G"} +{"index": {"_id": "46.75Z"}} +{"name": "Commerce de gros (commerce interentreprises) de produits chimiques", "parent": "G"} +{"index": {"_id": "46.76Z"}} +{"name": "Commerce de gros (commerce interentreprises) d'autres produits intermédiaires", "parent": "G"} +{"index": {"_id": "46.77Z"}} +{"name": "Commerce de gros (commerce interentreprises) de déchets et débris", "parent": "G"} +{"index": {"_id": "46.90Z"}} +{"name": "Commerce de gros (commerce interentreprises) non spécialisé", "parent": "G"} +{"index": {"_id": "47.11A"}} +{"name": "Commerce de détail de produits surgelés", "parent": "G"} +{"index": {"_id": "47.11B"}} +{"name": "Commerce d'alimentation générale", "parent": "G"} +{"index": {"_id": "47.11C"}} +{"name": "Supérettes", "parent": "G"} +{"index": {"_id": "47.11D"}} +{"name": "Supermarchés", "parent": "G"} +{"index": {"_id": "47.11E"}} +{"name": "Magasins multi-commerces", "parent": "G"} +{"index": {"_id": "47.11F"}} +{"name": "Hypermarchés", "parent": "G"} +{"index": {"_id": "47.19A"}} +{"name": "Grands magasins", "parent": "G"} +{"index": {"_id": "47.19B"}} +{"name": "Autres commerces de détail en magasin non spécialisé", "parent": "G"} +{"index": {"_id": "47.21Z"}} +{"name": "Commerce de détail de fruits et légumes en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.22Z"}} +{"name": "Commerce de détail de viandes et de produits à base de viande en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.23Z"}} +{"name": "Commerce de détail de poissons, crustacés et mollusques en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.24Z"}} +{"name": "Commerce de détail de pain, pâtisserie et confiserie en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.25Z"}} +{"name": "Commerce de détail de boissons en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.26Z"}} +{"name": "Commerce de détail de produits à base de tabac en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.29Z"}} +{"name": "Autres commerces de détail alimentaires en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.30Z"}} +{"name": "Commerce de détail de carburants en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.41Z"}} +{"name": "Commerce de détail d'ordinateurs, d'unités périphériques et de logiciels en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.42Z"}} +{"name": "Commerce de détail de matériels de télécommunication en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.43Z"}} +{"name": "Commerce de détail de matériels audio et vidéo en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.51Z"}} +{"name": "Commerce de détail de textiles en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.52A"}} +{"name": "Commerce de détail de quincaillerie, peintures et verres en petites surfaces (moins de 400 m²)", "parent": "G"} +{"index": {"_id": "47.52B"}} +{"name": "Commerce de détail de quincaillerie, peintures et verres en grandes surfaces (400 m² et plus)", "parent": "G"} +{"index": {"_id": "47.53Z"}} +{"name": "Commerce de détail de tapis, moquettes et revêtements de murs et de sols en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.54Z"}} +{"name": "Commerce de détail d'appareils électroménagers en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.59A"}} +{"name": "Commerce de détail de meubles", "parent": "G"} +{"index": {"_id": "47.59B"}} +{"name": "Commerce de détail d'autres équipements du foyer", "parent": "G"} +{"index": {"_id": "47.61Z"}} +{"name": "Commerce de détail de livres en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.62Z"}} +{"name": "Commerce de détail de journaux et papeterie en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.63Z"}} +{"name": "Commerce de détail d'enregistrements musicaux et vidéo en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.64Z"}} +{"name": "Commerce de détail d'articles de sport en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.65Z"}} +{"name": "Commerce de détail de jeux et jouets en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.71Z"}} +{"name": "Commerce de détail d'habillement en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.72A"}} +{"name": "Commerce de détail de la chaussure", "parent": "G"} +{"index": {"_id": "47.72B"}} +{"name": "Commerce de détail de maroquinerie et d'articles de voyage", "parent": "G"} +{"index": {"_id": "47.73Z"}} +{"name": "Commerce de détail de produits pharmaceutiques en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.74Z"}} +{"name": "Commerce de détail d'articles médicaux et orthopédiques en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.75Z"}} +{"name": "Commerce de détail de parfumerie et de produits de beauté en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.76Z"}} +{"name": "Commerce de détail de fleurs, plantes, graines, engrais, animaux de compagnie et aliments pour ces animaux en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.77Z"}} +{"name": "Commerce de détail d'articles d'horlogerie et de bijouterie en magasin spécialisé", "parent": "G"} +{"index": {"_id": "47.78A"}} +{"name": "Commerces de détail d'optique", "parent": "G"} +{"index": {"_id": "47.78B"}} +{"name": "Commerces de détail de charbons et combustibles", "parent": "G"} +{"index": {"_id": "47.78C"}} +{"name": "Autres commerces de détail spécialisés divers", "parent": "G"} +{"index": {"_id": "47.79Z"}} +{"name": "Commerce de détail de biens d'occasion en magasin", "parent": "G"} +{"index": {"_id": "47.81Z"}} +{"name": "Commerce de détail alimentaire sur éventaires et marchés", "parent": "G"} +{"index": {"_id": "47.82Z"}} +{"name": "Commerce de détail de textiles, d'habillement et de chaussures sur éventaires et marchés", "parent": "G"} +{"index": {"_id": "47.89Z"}} +{"name": "Autres commerces de détail sur éventaires et marchés", "parent": "G"} +{"index": {"_id": "47.91A"}} +{"name": "Vente à distance sur catalogue général", "parent": "G"} +{"index": {"_id": "47.91B"}} +{"name": "Vente à distance sur catalogue spécialisé", "parent": "G"} +{"index": {"_id": "47.99A"}} +{"name": "Vente à domicile", "parent": "G"} +{"index": {"_id": "47.99B"}} +{"name": "Vente par automates et autres commerces de détail hors magasin, éventaires ou marchés n.c.a.", "parent": "G"} +{"index": {"_id": "H"}} +{"name": "Transports et entreposage", "parent": null} +{"index": {"_id": "49.10Z"}} +{"name": "Transport ferroviaire interurbain de voyageurs", "parent": "H"} +{"index": {"_id": "49.20Z"}} +{"name": "Transports ferroviaires de fret", "parent": "H"} +{"index": {"_id": "49.31Z"}} +{"name": "Transports urbains et suburbains de voyageurs", "parent": "H"} +{"index": {"_id": "49.32Z"}} +{"name": "Transports de voyageurs par taxis", "parent": "H"} +{"index": {"_id": "49.39A"}} +{"name": "Transports routiers réguliers de voyageurs", "parent": "H"} +{"index": {"_id": "49.39B"}} +{"name": "Autres transports routiers de voyageurs", "parent": "H"} +{"index": {"_id": "49.39C"}} +{"name": "Téléphériques et remontées mécaniques", "parent": "H"} +{"index": {"_id": "49.41A"}} +{"name": "Transports routiers de fret interurbains", "parent": "H"} +{"index": {"_id": "49.41B"}} +{"name": "Transports routiers de fret de proximité", "parent": "H"} +{"index": {"_id": "49.41C"}} +{"name": "Location de camions avec chauffeur", "parent": "H"} +{"index": {"_id": "49.42Z"}} +{"name": "Services de déménagement", "parent": "H"} +{"index": {"_id": "49.50Z"}} +{"name": "Transports par conduites", "parent": "H"} +{"index": {"_id": "50.10Z"}} +{"name": "Transports maritimes et côtiers de passagers", "parent": "H"} +{"index": {"_id": "50.20Z"}} +{"name": "Transports maritimes et côtiers de fret", "parent": "H"} +{"index": {"_id": "50.30Z"}} +{"name": "Transports fluviaux de passagers", "parent": "H"} +{"index": {"_id": "50.40Z"}} +{"name": "Transports fluviaux de fret", "parent": "H"} +{"index": {"_id": "51.10Z"}} +{"name": "Transports aériens de passagers", "parent": "H"} +{"index": {"_id": "51.21Z"}} +{"name": "Transports aériens de fret", "parent": "H"} +{"index": {"_id": "51.22Z"}} +{"name": "Transports spatiaux", "parent": "H"} +{"index": {"_id": "52.10A"}} +{"name": "Entreposage et stockage frigorifique", "parent": "H"} +{"index": {"_id": "52.10B"}} +{"name": "Entreposage et stockage non frigorifique", "parent": "H"} +{"index": {"_id": "52.21Z"}} +{"name": "Services auxiliaires des transports terrestres", "parent": "H"} +{"index": {"_id": "52.22Z"}} +{"name": "Services auxiliaires des transports par eau", "parent": "H"} +{"index": {"_id": "52.23Z"}} +{"name": "Services auxiliaires des transports aériens", "parent": "H"} +{"index": {"_id": "52.24A"}} +{"name": "Manutention portuaire", "parent": "H"} +{"index": {"_id": "52.24B"}} +{"name": "Manutention non portuaire", "parent": "H"} +{"index": {"_id": "52.29A"}} +{"name": "Messagerie, fret express", "parent": "H"} +{"index": {"_id": "52.29B"}} +{"name": "Affrètement et organisation des transports", "parent": "H"} +{"index": {"_id": "53.10Z"}} +{"name": "Activités de poste dans le cadre d'une obligation de service universel", "parent": "H"} +{"index": {"_id": "53.20Z"}} +{"name": "Autres activités de poste et de courrier", "parent": "H"} +{"index": {"_id": "I"}} +{"name": "Hébergement et restauration", "parent": null} +{"index": {"_id": "55.10Z"}} +{"name": "Hôtels et hébergement similaire", "parent": "I"} +{"index": {"_id": "55.20Z"}} +{"name": "Hébergement touristique et autre hébergement de courte durée", "parent": "I"} +{"index": {"_id": "55.30Z"}} +{"name": "Terrains de camping et parcs pour caravanes ou véhicules de loisirs", "parent": "I"} +{"index": {"_id": "55.90Z"}} +{"name": "Autres hébergements", "parent": "I"} +{"index": {"_id": "56.10A"}} +{"name": "Restauration traditionnelle", "parent": "I"} +{"index": {"_id": "56.10B"}} +{"name": "Cafétérias et autres libres-services", "parent": "I"} +{"index": {"_id": "56.10C"}} +{"name": "Restauration de type rapide", "parent": "I"} +{"index": {"_id": "56.21Z"}} +{"name": "Services des traiteurs", "parent": "I"} +{"index": {"_id": "56.29A"}} +{"name": "Restauration collective sous contrat", "parent": "I"} +{"index": {"_id": "56.29B"}} +{"name": "Autres services de restauration n.c.a.", "parent": "I"} +{"index": {"_id": "56.30Z"}} +{"name": "Débits de boissons", "parent": "I"} +{"index": {"_id": "J"}} +{"name": "Information et communication", "parent": null} +{"index": {"_id": "58.11Z"}} +{"name": "Édition de livres", "parent": "J"} +{"index": {"_id": "58.12Z"}} +{"name": "Édition de répertoires et de fichiers d'adresses", "parent": "J"} +{"index": {"_id": "58.13Z"}} +{"name": "Édition de journaux", "parent": "J"} +{"index": {"_id": "58.14Z"}} +{"name": "Édition de revues et périodiques", "parent": "J"} +{"index": {"_id": "58.19Z"}} +{"name": "Autres activités d'édition", "parent": "J"} +{"index": {"_id": "58.21Z"}} +{"name": "Édition de jeux électroniques", "parent": "J"} +{"index": {"_id": "58.29A"}} +{"name": "Édition de logiciels système et de réseau", "parent": "J"} +{"index": {"_id": "58.29B"}} +{"name": "Édition de logiciels outils de développement et de langages", "parent": "J"} +{"index": {"_id": "58.29C"}} +{"name": "Édition de logiciels applicatifs", "parent": "J"} +{"index": {"_id": "59.11A"}} +{"name": "Production de films et de programmes pour la télévision", "parent": "J"} +{"index": {"_id": "59.11B"}} +{"name": "Production de films institutionnels et publicitaires", "parent": "J"} +{"index": {"_id": "59.11C"}} +{"name": "Production de films pour le cinéma", "parent": "J"} +{"index": {"_id": "59.12Z"}} +{"name": "Post-production de films cinématographiques, de vidéo et de programmes de télévision", "parent": "J"} +{"index": {"_id": "59.13A"}} +{"name": "Distribution de films cinématographiques", "parent": "J"} +{"index": {"_id": "59.13B"}} +{"name": "Édition et distribution vidéo", "parent": "J"} +{"index": {"_id": "59.14Z"}} +{"name": "Projection de films cinématographiques", "parent": "J"} +{"index": {"_id": "59.20Z"}} +{"name": "Enregistrement sonore et édition musicale", "parent": "J"} +{"index": {"_id": "60.10Z"}} +{"name": "Édition et diffusion de programmes radio", "parent": "J"} +{"index": {"_id": "60.20A"}} +{"name": "Édition de chaînes généralistes", "parent": "J"} +{"index": {"_id": "60.20B"}} +{"name": "Édition de chaînes thématiques", "parent": "J"} +{"index": {"_id": "61.10Z"}} +{"name": "Télécommunications filaires", "parent": "J"} +{"index": {"_id": "61.20Z"}} +{"name": "Télécommunications sans fil", "parent": "J"} +{"index": {"_id": "61.30Z"}} +{"name": "Télécommunications par satellite", "parent": "J"} +{"index": {"_id": "61.90Z"}} +{"name": "Autres activités de télécommunication", "parent": "J"} +{"index": {"_id": "62.01Z"}} +{"name": "Programmation informatique", "parent": "J"} +{"index": {"_id": "62.02A"}} +{"name": "Conseil en systèmes et logiciels informatiques", "parent": "J"} +{"index": {"_id": "62.02B"}} +{"name": "Tierce maintenance de systèmes et d'applications informatiques", "parent": "J"} +{"index": {"_id": "62.03Z"}} +{"name": "Gestion d'installations informatiques", "parent": "J"} +{"index": {"_id": "62.09Z"}} +{"name": "Autres activités informatiques", "parent": "J"} +{"index": {"_id": "63.11Z"}} +{"name": "Traitement de données, hébergement et activités connexes", "parent": "J"} +{"index": {"_id": "63.12Z"}} +{"name": "Portails Internet", "parent": "J"} +{"index": {"_id": "63.91Z"}} +{"name": "Activités des agences de presse", "parent": "J"} +{"index": {"_id": "63.99Z"}} +{"name": "Autres services d'information n.c.a.", "parent": "J"} +{"index": {"_id": "K"}} +{"name": "Activités financières et d'assurance", "parent": null} +{"index": {"_id": "64.11Z"}} +{"name": "Activités de banque centrale", "parent": "K"} +{"index": {"_id": "64.19Z"}} +{"name": "Autres intermédiations monétaires", "parent": "K"} +{"index": {"_id": "64.20Z"}} +{"name": "Activités des sociétés holding", "parent": "K"} +{"index": {"_id": "64.30Z"}} +{"name": "Fonds de placement et entités financières similaires", "parent": "K"} +{"index": {"_id": "64.91Z"}} +{"name": "Crédit-bail", "parent": "K"} +{"index": {"_id": "64.92Z"}} +{"name": "Autre distribution de crédit", "parent": "K"} +{"index": {"_id": "64.99Z"}} +{"name": "Autres activités des services financiers, hors assurance et caisses de retraite, n.c.a.", "parent": "K"} +{"index": {"_id": "65.11Z"}} +{"name": "Assurance vie", "parent": "K"} +{"index": {"_id": "65.12Z"}} +{"name": "Autres assurances", "parent": "K"} +{"index": {"_id": "65.20Z"}} +{"name": "Réassurance", "parent": "K"} +{"index": {"_id": "65.30Z"}} +{"name": "Caisses de retraite", "parent": "K"} +{"index": {"_id": "66.11Z"}} +{"name": "Administration de marchés financiers", "parent": "K"} +{"index": {"_id": "66.12Z"}} +{"name": "Courtage de valeurs mobilières et de marchandises", "parent": "K"} +{"index": {"_id": "66.19A"}} +{"name": "Supports juridiques de gestion de patrimoine mobilier", "parent": "K"} +{"index": {"_id": "66.19B"}} +{"name": "Autres activités auxiliaires de services financiers, hors assurance et caisses de retraite, n.c.a.", "parent": "K"} +{"index": {"_id": "66.21Z"}} +{"name": "Évaluation des risques et dommages", "parent": "K"} +{"index": {"_id": "66.22Z"}} +{"name": "Activités des agents et courtiers d'assurances", "parent": "K"} +{"index": {"_id": "66.29Z"}} +{"name": "Autres activités auxiliaires d'assurance et de caisses de retraite", "parent": "K"} +{"index": {"_id": "66.30Z"}} +{"name": "Gestion de fonds", "parent": "K"} +{"index": {"_id": "L"}} +{"name": "Activités immobilières", "parent": null} +{"index": {"_id": "68.10Z"}} +{"name": "Activités des marchands de biens immobiliers", "parent": "L"} +{"index": {"_id": "68.20A"}} +{"name": "Location de logements", "parent": "L"} +{"index": {"_id": "68.20B"}} +{"name": "Location de terrains et d'autres biens immobiliers", "parent": "L"} +{"index": {"_id": "68.31Z"}} +{"name": "Agences immobilières", "parent": "L"} +{"index": {"_id": "68.32A"}} +{"name": "Administration d'immeubles et autres biens immobiliers", "parent": "L"} +{"index": {"_id": "68.32B"}} +{"name": "Supports juridiques de gestion de patrimoine immobilier", "parent": "L"} +{"index": {"_id": "M"}} +{"name": "Activités spécialisées, scientifiques et techniques", "parent": null} +{"index": {"_id": "69.10Z"}} +{"name": "Activités juridiques", "parent": "M"} +{"index": {"_id": "69.20Z"}} +{"name": "Activités comptables", "parent": "M"} +{"index": {"_id": "70.10Z"}} +{"name": "Activités des sièges sociaux", "parent": "M"} +{"index": {"_id": "70.21Z"}} +{"name": "Conseil en relations publiques et communication", "parent": "M"} +{"index": {"_id": "70.22Z"}} +{"name": "Conseil pour les affaires et autres conseils de gestion", "parent": "M"} +{"index": {"_id": "71.11Z"}} +{"name": "Activités d'architecture", "parent": "M"} +{"index": {"_id": "71.12A"}} +{"name": "Activité des géomètres", "parent": "M"} +{"index": {"_id": "71.12B"}} +{"name": "Ingénierie, études techniques", "parent": "M"} +{"index": {"_id": "71.20A"}} +{"name": "Contrôle technique automobile", "parent": "M"} +{"index": {"_id": "71.20B"}} +{"name": "Analyses, essais et inspections techniques", "parent": "M"} +{"index": {"_id": "72.11Z"}} +{"name": "Recherche-développement en biotechnologie", "parent": "M"} +{"index": {"_id": "72.19Z"}} +{"name": "Recherche-développement en autres sciences physiques et naturelles", "parent": "M"} +{"index": {"_id": "72.20Z"}} +{"name": "Recherche-développement en sciences humaines et sociales", "parent": "M"} +{"index": {"_id": "73.11Z"}} +{"name": "Activités des agences de publicité", "parent": "M"} +{"index": {"_id": "73.12Z"}} +{"name": "Régie publicitaire de médias", "parent": "M"} +{"index": {"_id": "73.20Z"}} +{"name": "Études de marché et sondages", "parent": "M"} +{"index": {"_id": "74.10Z"}} +{"name": "Activités spécialisées de design", "parent": "M"} +{"index": {"_id": "74.20Z"}} +{"name": "Activités photographiques", "parent": "M"} +{"index": {"_id": "74.30Z"}} +{"name": "Traduction et interprétation", "parent": "M"} +{"index": {"_id": "74.90A"}} +{"name": "Activité des économistes de la construction", "parent": "M"} +{"index": {"_id": "74.90B"}} +{"name": "Activités spécialisées, scientifiques et techniques diverses", "parent": "M"} +{"index": {"_id": "N"}} +{"name": "Activités de services administratifs et de soutien", "parent": null} +{"index": {"_id": "75.00Z"}} +{"name": "Activités vétérinaires", "parent": "N"} +{"index": {"_id": "77.11A"}} +{"name": "Location de courte durée de voitures et de véhicules automobiles légers", "parent": "N"} +{"index": {"_id": "77.11B"}} +{"name": "Location de longue durée de voitures et de véhicules automobiles légers", "parent": "N"} +{"index": {"_id": "77.12Z"}} +{"name": "Location et location-bail de camions", "parent": "N"} +{"index": {"_id": "77.21Z"}} +{"name": "Location et location-bail d'articles de loisirs et de sport", "parent": "N"} +{"index": {"_id": "77.22Z"}} +{"name": "Location de vidéocassettes et disques vidéo", "parent": "N"} +{"index": {"_id": "77.29Z"}} +{"name": "Location et location-bail d'autres biens personnels et domestiques", "parent": "N"} +{"index": {"_id": "77.31Z"}} +{"name": "Location et location-bail de machines et équipements agricoles", "parent": "N"} +{"index": {"_id": "77.32Z"}} +{"name": "Location et location-bail de machines et équipements pour la construction", "parent": "N"} +{"index": {"_id": "77.33Z"}} +{"name": "Location et location-bail de machines de bureau et de matériel informatique", "parent": "N"} +{"index": {"_id": "77.34Z"}} +{"name": "Location et location-bail de matériels de transport par eau", "parent": "N"} +{"index": {"_id": "77.35Z"}} +{"name": "Location et location-bail de matériels de transport aérien", "parent": "N"} +{"index": {"_id": "77.39Z"}} +{"name": "Location et location-bail d'autres machines, équipements et biens matériels n.c.a.", "parent": "N"} +{"index": {"_id": "77.40Z"}} +{"name": "Location-bail de propriété intellectuelle et de produits similaires, à l'exception des œuvres soumises à copyright", "parent": "N"} +{"index": {"_id": "78.10Z"}} +{"name": "Activités des agences de placement de main-d'œuvre", "parent": "N"} +{"index": {"_id": "78.20Z"}} +{"name": "Activités des agences de travail temporaire", "parent": "N"} +{"index": {"_id": "78.30Z"}} +{"name": "Autre mise à disposition de ressources humaines", "parent": "N"} +{"index": {"_id": "79.11Z"}} +{"name": "Activités des agences de voyage", "parent": "N"} +{"index": {"_id": "79.12Z"}} +{"name": "Activités des voyagistes", "parent": "N"} +{"index": {"_id": "79.90Z"}} +{"name": "Autres services de réservation et activités connexes", "parent": "N"} +{"index": {"_id": "80.10Z"}} +{"name": "Activités de sécurité privée", "parent": "N"} +{"index": {"_id": "80.20Z"}} +{"name": "Activités liées aux systèmes de sécurité", "parent": "N"} +{"index": {"_id": "80.30Z"}} +{"name": "Activités d'enquête", "parent": "N"} +{"index": {"_id": "81.10Z"}} +{"name": "Activités combinées de soutien lié aux bâtiments", "parent": "N"} +{"index": {"_id": "81.21Z"}} +{"name": "Nettoyage courant des bâtiments", "parent": "N"} +{"index": {"_id": "81.22Z"}} +{"name": "Autres activités de nettoyage des bâtiments et nettoyage industriel", "parent": "N"} +{"index": {"_id": "81.29A"}} +{"name": "Désinfection, désinsectisation, dératisation", "parent": "N"} +{"index": {"_id": "81.29B"}} +{"name": "Autres activités de nettoyage n.c.a.", "parent": "N"} +{"index": {"_id": "81.30Z"}} +{"name": "Services d'aménagement paysager", "parent": "N"} +{"index": {"_id": "82.11Z"}} +{"name": "Services administratifs combinés de bureau", "parent": "N"} +{"index": {"_id": "82.19Z"}} +{"name": "Photocopie, préparation de documents et autres activités spécialisées de soutien de bureau", "parent": "N"} +{"index": {"_id": "82.20Z"}} +{"name": "Activités de centres d'appels", "parent": "N"} +{"index": {"_id": "82.30Z"}} +{"name": "Organisation de foires, salons professionnels et congrès", "parent": "N"} +{"index": {"_id": "82.91Z"}} +{"name": "Activités des agences de recouvrement de factures et des sociétés d'information financière sur la clientèle", "parent": "N"} +{"index": {"_id": "82.92Z"}} +{"name": "Activités de conditionnement", "parent": "N"} +{"index": {"_id": "82.99Z"}} +{"name": "Autres activités de soutien aux entreprises n.c.a.", "parent": "N"} +{"index": {"_id": "O"}} +{"name": "Administration publique", "parent": null} +{"index": {"_id": "84.11Z"}} +{"name": "Administration publique générale", "parent": "O"} +{"index": {"_id": "84.12Z"}} +{"name": "Administration publique (tutelle) de la santé, de la formation, de la culture et des services sociaux, autre que sécurité sociale", "parent": "O"} +{"index": {"_id": "84.13Z"}} +{"name": "Administration publique (tutelle) des activités économiques", "parent": "O"} +{"index": {"_id": "84.21Z"}} +{"name": "Affaires étrangères", "parent": "O"} +{"index": {"_id": "84.22Z"}} +{"name": "Défense", "parent": "O"} +{"index": {"_id": "84.23Z"}} +{"name": "Justice", "parent": "O"} +{"index": {"_id": "84.24Z"}} +{"name": "Activités d'ordre public et de sécurité", "parent": "O"} +{"index": {"_id": "84.25Z"}} +{"name": "Services du feu et de secours", "parent": "O"} +{"index": {"_id": "84.30A"}} +{"name": "Activités générales de sécurité sociale", "parent": "O"} +{"index": {"_id": "84.30B"}} +{"name": "Gestion des retraites complémentaires", "parent": "O"} +{"index": {"_id": "84.30C"}} +{"name": "Distribution sociale de revenus", "parent": "O"} +{"index": {"_id": "P"}} +{"name": "Enseignement", "parent": null} +{"index": {"_id": "85.10Z"}} +{"name": "Enseignement pré-primaire", "parent": "P"} +{"index": {"_id": "85.20Z"}} +{"name": "Enseignement primaire", "parent": "P"} +{"index": {"_id": "85.31Z"}} +{"name": "Enseignement secondaire général", "parent": "P"} +{"index": {"_id": "85.32Z"}} +{"name": "Enseignement secondaire technique ou professionnel", "parent": "P"} +{"index": {"_id": "85.41Z"}} +{"name": "Enseignement post-secondaire non supérieur", "parent": "P"} +{"index": {"_id": "85.42Z"}} +{"name": "Enseignement supérieur", "parent": "P"} +{"index": {"_id": "85.51Z"}} +{"name": "Enseignement de disciplines sportives et d'activités de loisirs", "parent": "P"} +{"index": {"_id": "85.52Z"}} +{"name": "Enseignement culturel", "parent": "P"} +{"index": {"_id": "85.53Z"}} +{"name": "Enseignement de la conduite", "parent": "P"} +{"index": {"_id": "85.59A"}} +{"name": "Formation continue d'adultes", "parent": "P"} +{"index": {"_id": "85.59B"}} +{"name": "Autres enseignements", "parent": "P"} +{"index": {"_id": "85.60Z"}} +{"name": "Activités de soutien à l'enseignement", "parent": "P"} +{"index": {"_id": "Q"}} +{"name": "Santé humaine et action sociale", "parent": null} +{"index": {"_id": "86.10Z"}} +{"name": "Activités hospitalières", "parent": "Q"} +{"index": {"_id": "86.21Z"}} +{"name": "Activité des médecins généralistes", "parent": "Q"} +{"index": {"_id": "86.22A"}} +{"name": "Activités de radiodiagnostic et de radiothérapie", "parent": "Q"} +{"index": {"_id": "86.22B"}} +{"name": "Activités chirurgicales", "parent": "Q"} +{"index": {"_id": "86.22C"}} +{"name": "Autres activités des médecins spécialistes", "parent": "Q"} +{"index": {"_id": "86.23Z"}} +{"name": "Pratique dentaire", "parent": "Q"} +{"index": {"_id": "86.90A"}} +{"name": "Ambulances", "parent": "Q"} +{"index": {"_id": "86.90B"}} +{"name": "Laboratoires d'analyses médicales", "parent": "Q"} +{"index": {"_id": "86.90C"}} +{"name": "Centres de collecte et banques d'organes", "parent": "Q"} +{"index": {"_id": "86.90D"}} +{"name": "Activités des infirmiers et des sages-femmes", "parent": "Q"} +{"index": {"_id": "86.90E"}} +{"name": "Activités des professionnels de la rééducation, de l'appareillage et des pédicures-podologues", "parent": "Q"} +{"index": {"_id": "86.90F"}} +{"name": "Activités de santé humaine non classées ailleurs", "parent": "Q"} +{"index": {"_id": "87.10A"}} +{"name": "Hébergement médicalisé pour personnes âgées", "parent": "Q"} +{"index": {"_id": "87.10B"}} +{"name": "Hébergement médicalisé pour enfants handicapés", "parent": "Q"} +{"index": {"_id": "87.10C"}} +{"name": "Hébergement médicalisé pour adultes handicapés et autre hébergement médicalisé", "parent": "Q"} +{"index": {"_id": "87.20A"}} +{"name": "Hébergement social pour handicapés mentaux et malades mentaux", "parent": "Q"} +{"index": {"_id": "87.20B"}} +{"name": "Hébergement social pour toxicomanes", "parent": "Q"} +{"index": {"_id": "87.30A"}} +{"name": "Hébergement social pour personnes âgées", "parent": "Q"} +{"index": {"_id": "87.30B"}} +{"name": "Hébergement social pour handicapés physiques", "parent": "Q"} +{"index": {"_id": "87.90A"}} +{"name": "Hébergement social pour enfants en difficultés", "parent": "Q"} +{"index": {"_id": "87.90B"}} +{"name": "Hébergement social pour adultes et familles en difficultés et autre hébergement social", "parent": "Q"} +{"index": {"_id": "88.10A"}} +{"name": "Aide à domicile", "parent": "Q"} +{"index": {"_id": "88.10B"}} +{"name": "Accueil ou accompagnement sans hébergement d'adultes handicapés ou de personnes âgées", "parent": "Q"} +{"index": {"_id": "88.10C"}} +{"name": "Aide par le travail", "parent": "Q"} +{"index": {"_id": "88.91A"}} +{"name": "Accueil de jeunes enfants", "parent": "Q"} +{"index": {"_id": "88.91B"}} +{"name": "Accueil ou accompagnement sans hébergement d'enfants handicapés", "parent": "Q"} +{"index": {"_id": "88.99A"}} +{"name": "Autre accueil ou accompagnement sans hébergement d'enfants et d'adolescents", "parent": "Q"} +{"index": {"_id": "88.99B"}} +{"name": "Action sociale sans hébergement n.c.a.", "parent": "Q"} +{"index": {"_id": "R"}} +{"name": "Arts, spectacles et activités récréatives", "parent": null} +{"index": {"_id": "90.01Z"}} +{"name": "Arts du spectacle vivant", "parent": "R"} +{"index": {"_id": "90.02Z"}} +{"name": "Activités de soutien au spectacle vivant", "parent": "R"} +{"index": {"_id": "90.03A"}} +{"name": "Création artistique relevant des arts plastiques", "parent": "R"} +{"index": {"_id": "90.03B"}} +{"name": "Autre création artistique", "parent": "R"} +{"index": {"_id": "90.04Z"}} +{"name": "Gestion de salles de spectacles", "parent": "R"} +{"index": {"_id": "91.01Z"}} +{"name": "Gestion des bibliothèques et des archives", "parent": "R"} +{"index": {"_id": "91.02Z"}} +{"name": "Gestion des musées", "parent": "R"} +{"index": {"_id": "91.03Z"}} +{"name": "Gestion des sites et monuments historiques et des attractions touristiques similaires", "parent": "R"} +{"index": {"_id": "91.04Z"}} +{"name": "Gestion des jardins botaniques et zoologiques et des réserves naturelles", "parent": "R"} +{"index": {"_id": "92.00Z"}} +{"name": "Organisation de jeux de hasard et d'argent", "parent": "R"} +{"index": {"_id": "93.11Z"}} +{"name": "Gestion d'installations sportives", "parent": "R"} +{"index": {"_id": "93.12Z"}} +{"name": "Activités de clubs de sports", "parent": "R"} +{"index": {"_id": "93.13Z"}} +{"name": "Activités des centres de culture physique", "parent": "R"} +{"index": {"_id": "93.19Z"}} +{"name": "Autres activités liées au sport", "parent": "R"} +{"index": {"_id": "93.21Z"}} +{"name": "Activités des parcs d'attractions et parcs à thèmes", "parent": "R"} +{"index": {"_id": "93.29Z"}} +{"name": "Autres activités récréatives et de loisirs", "parent": "R"} +{"index": {"_id": "S"}} +{"name": "Autres activités de services", "parent": null} +{"index": {"_id": "94.11Z"}} +{"name": "Activités des organisations patronales et consulaires", "parent": "S"} +{"index": {"_id": "94.12Z"}} +{"name": "Activités des organisations professionnelles", "parent": "S"} +{"index": {"_id": "94.20Z"}} +{"name": "Activités des syndicats de salariés", "parent": "S"} +{"index": {"_id": "94.91Z"}} +{"name": "Activités des organisations religieuses", "parent": "S"} +{"index": {"_id": "94.92Z"}} +{"name": "Activités des organisations politiques", "parent": "S"} +{"index": {"_id": "94.99Z"}} +{"name": "Autres organisations fonctionnant par adhésion volontaire", "parent": "S"} +{"index": {"_id": "95.11Z"}} +{"name": "Réparation d'ordinateurs et d'équipements périphériques", "parent": "S"} +{"index": {"_id": "95.12Z"}} +{"name": "Réparation d'équipements de communication", "parent": "S"} +{"index": {"_id": "95.21Z"}} +{"name": "Réparation de produits électroniques grand public", "parent": "S"} +{"index": {"_id": "95.22Z"}} +{"name": "Réparation d'appareils électroménagers et d'équipements pour la maison et le jardin", "parent": "S"} +{"index": {"_id": "95.23Z"}} +{"name": "Réparation de chaussures et d'articles en cuir", "parent": "S"} +{"index": {"_id": "95.24Z"}} +{"name": "Réparation de meubles et d'équipements du foyer", "parent": "S"} +{"index": {"_id": "95.25Z"}} +{"name": "Réparation d'articles d'horlogerie et de bijouterie", "parent": "S"} +{"index": {"_id": "95.29Z"}} +{"name": "Réparation d'autres biens personnels et domestiques", "parent": "S"} +{"index": {"_id": "96.01A"}} +{"name": "Blanchisserie-teinturerie de gros", "parent": "S"} +{"index": {"_id": "96.01B"}} +{"name": "Blanchisserie-teinturerie de détail", "parent": "S"} +{"index": {"_id": "96.02A"}} +{"name": "Coiffure", "parent": "S"} +{"index": {"_id": "96.02B"}} +{"name": "Soins de beauté", "parent": "S"} +{"index": {"_id": "96.03Z"}} +{"name": "Services funéraires", "parent": "S"} +{"index": {"_id": "96.04Z"}} +{"name": "Entretien corporel", "parent": "S"} +{"index": {"_id": "96.09Z"}} +{"name": "Autres services personnels n.c.a.", "parent": "S"} +{"index": {"_id": "T"}} +{"name": "Activités des ménages en tant qu'employeurs ; activités indifférenciées des ménages en tant que producteurs de biens et services pour usage propre", "parent": null} +{"index": {"_id": "97.00Z"}} +{"name": "Activités des ménages en tant qu'employeurs de personnel domestique", "parent": "T"} +{"index": {"_id": "98.10Z"}} +{"name": "Activités indifférenciées des ménages en tant que producteurs de biens pour usage propre", "parent": "T"} +{"index": {"_id": "98.20Z"}} +{"name": "Activités indifférenciées des ménages en tant que producteurs de services pour usage propre", "parent": "T"} +{"index": {"_id": "U"}} +{"name": "Activités extra-territoriales", "parent": null} +{"index": {"_id": "99.00Z"}} +{"name": "Activités des organisations et organismes extraterritoriaux", "parent": "U"} diff --git a/duniter4j-es-gchange/src/test/es-home/config/elasticsearch.yml b/duniter4j-es-gchange/src/test/es-home/config/elasticsearch.yml new file mode 100644 index 0000000000000000000000000000000000000000..828ddbd04543ef810f380ec8e0dbf229e6c2c48c --- /dev/null +++ b/duniter4j-es-gchange/src/test/es-home/config/elasticsearch.yml @@ -0,0 +1,178 @@ +# ======================== Elasticsearch Configuration ========================= +# +# NOTE: Elasticsearch comes with reasonable defaults for most settings. +# Before you set out to tweak and tune the configuration, make sure you +# understand what are you trying to accomplish and the consequences. +# +# The primary way of configuring a node is via this file. This template lists +# the most important settings you may want to configure for a production cluster. +# +# Please see the documentation for further information on configuration options: +# <http://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration.html> +# +# ---------------------------------- Cluster ----------------------------------- +# +# Use a descriptive name for your cluster: +# +# cluster.name: my-application +cluster.name: duniter4j-elasticsearch-TEST +# +# ------------------------------------ Node ------------------------------------ +# +# Use a descriptive name for the node: +# +# node.name: node-1 +# +# Add custom attributes to the node: +# +# node.rack: r1 +# +# ----------------------------------- Paths ------------------------------------ +# +# Path to directory where to store the data (separate multiple locations by comma): +# +# path.data: /path/to/data +# +# Path to log files: +# +# path.logs: /path/to/logs +# +# ----------------------------------- Memory ----------------------------------- +# +# Lock the memory on startup: +# +# bootstrap.mlockall: true +# +# Make sure that the `ES_HEAP_SIZE` environment variable is set to about half the memory +# available on the system and that the owner of the process is allowed to use this limit. +# +# Elasticsearch performs poorly when the system is swapping the memory. +# +# ---------------------------------- Network ----------------------------------- +# +# Set the bind address to a specific IP (IPv4 or IPv6): +# +# network.host: 192.168.233.1 +# +# Set a custom port for HTTP: +# +# http.port: 9200-9300 + +http.cors.allow-origin: "/.*/" +http.cors.enabled: true + +# Internal transport layer +# +# transport.tcp.port: 9210-9220 +# +# For more information, see the documentation at: +# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-network.html> +# +# --------------------------------- Discovery ---------------------------------- +# +# Pass an initial list of hosts to perform discovery when new node is started: +# The default list of hosts is ["127.0.0.1", "[::1]"] +# +# discovery.zen.ping.unicast.hosts: ["host1", "host2"] +#discovery.zen.ping.unicast.hosts: ["127.0.0.1", ""] +# +# Prevent the "split brain" by configuring the majority of nodes (total number of nodes / 2 + 1): +# +# discovery.zen.minimum_master_nodes: 3 +# +# For more information, see the documentation at: +# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-discovery.html> +# +# ---------------------------------- Gateway ----------------------------------- +# +# Block initial recovery after a full cluster restart until N nodes are started: +# +# gateway.recover_after_nodes: 3 +# +# For more information, see the documentation at: +# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-gateway.html> +# +# ---------------------------------- Various ----------------------------------- +# +# Disable starting multiple nodes on a single system: +# +# node.max_local_storage_nodes: 1 +# +# Require explicit names when deleting indices: +# +# rest.destructive_requires_name: true + +security.manager.enabled: false + +# +# ---------------------------------- Duniter4j --------------------------------- +# +# Disbale duniter4j plugin +# +# duniter.enabled: false +# +# Reset and reload all Duniter4j data at startup - DO SET to true in production +# +# duniter.indices.reload: true +# +# Default string analyzer +# +duniter.string.analyzer: french +# +# Enabling node blockchain synchronization +# +duniter.blockchain.sync.enable: false +# +# Duniter node to synchronize +# +duniter.host: cgeek.fr +duniter.port: 9330 +# +# ---------------------------------- Duniter4j security ------------------------- +# +duniter.keyring.salt: abc +duniter.keyring.password: def + +# Enable security, to disable HTTP access to the default ES admin API +# +duniter.security.enable: false +# +# Security token prefix (default: 'duniter-') +# +# duniter.auth.token.prefix: duniter- +# +# Token validity duration, in seconds (default: 600) +# +# duniter.auth.tokenValidityDuration: 3600 # = 1hour +# +# ---------------------------------- Duniter4j P2P sync ------------------------- +# +# Should synchronize data using P2P +# +duniter.data.sync.enable: false +#duniter.data.sync.host: data.duniter.fr +#duniter.data.sync.port: 80 + +# ---------------------------------- Duniter4j SMTP server ------------------------- +# +# SMTP server configuration (host and port) +# +#duniter.mail.smtp.host: localhost +#duniter.mail.smtp.port: 25 +# +# Mail 'from' address +# +#duniter.mail.from: no-reply@domain.com +duniter.mail.from: root@EIS-DEV +# +# Mail: admin address +# +#duniter.mail.admin: user@domain.com +duniter.mail.admin: blavenie@EIS-DEV +# +# Mail subject prefix +# +#duniter.mail.subject.prefix: [Duniter4j ES] + +duniter.changes.listenSource: */block +duniter.ws.port: 9400 diff --git a/duniter4j-es-gchange/src/test/es-home/config/logging.yml b/duniter4j-es-gchange/src/test/es-home/config/logging.yml new file mode 100644 index 0000000000000000000000000000000000000000..15cfa3e195cb46a62c7536f118d1684acfcc2ecf --- /dev/null +++ b/duniter4j-es-gchange/src/test/es-home/config/logging.yml @@ -0,0 +1,97 @@ +# you can override this using by setting a system property, for example -Des.logger.level=DEBUG +es.logger.level: INFO +rootLogger: ${es.logger.level}, console, file +logger: + # log rest execution errors for easier debugging + action: DEBUG + + # deprecation logging, turn to DEBUG to see them + deprecation: INFO, deprecation_log_file + + # reduce the logging for aws, too much is logged under the default INFO + com.amazonaws: WARN + # aws will try to do some sketchy JMX stuff, but its not needed. + com.amazonaws.jmx.SdkMBeanRegistrySupport: ERROR + com.amazonaws.metrics.AwsSdkMetrics: ERROR + + org.apache.http: INFO + + org.duniter: INFO + + org.duniter.elasticsearch: DEBUG + + duniter : DEBUG + duniter.network.p2p: TRACE + + security: DEBUG + + org.nuiton.i18n: WARN + org.nuiton.config: WARN + + # gateway + #gateway: DEBUG + #index.gateway: DEBUG + + # peer shard recovery + #indices.recovery: DEBUG + + # discovery + #discovery: TRACE + + index.search.slowlog: TRACE, index_search_slow_log_file + index.indexing.slowlog: TRACE, index_indexing_slow_log_file + +additivity: + index.search.slowlog: false + index.indexing.slowlog: false + deprecation: false + +appender: + console: + type: console + layout: + type: consolePattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %.10000m%n" + + # Use the following log4j-extras RollingFileAppender to enable gzip compression of log files. + # For more information see https://logging.apache.org/log4j/extras/apidocs/org/apache/log4j/rolling/RollingFileAppender.html + #file: + #type: extrasRollingFile + #file: ${path.logs}/${cluster.name}.log + #rollingPolicy: timeBased + #rollingPolicy.FileNamePattern: ${path.logs}/${cluster.name}.log.%d{yyyy-MM-dd}.gz + #layout: + #type: pattern + #conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + deprecation_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_deprecation.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + index_search_slow_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_index_search_slowlog.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + index_indexing_slow_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_index_indexing_slowlog.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" diff --git a/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/TestFixtures.java b/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/TestFixtures.java new file mode 100644 index 0000000000000000000000000000000000000000..36f093303027109c68ee24aa3e8d1b763c865daf --- /dev/null +++ b/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/TestFixtures.java @@ -0,0 +1,28 @@ +package org.duniter.elasticsearch; + +/* + * #%L + * UCoin Java Client :: ElasticSearch Indexer + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + + +public class TestFixtures extends org.duniter.core.test.TestFixtures { + +} diff --git a/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/TestResource.java b/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/TestResource.java new file mode 100644 index 0000000000000000000000000000000000000000..2b27a3e55828026c4e9b37c4a3dba40020e161c4 --- /dev/null +++ b/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/TestResource.java @@ -0,0 +1,141 @@ +package org.duniter.elasticsearch; + +/* + * #%L + * UCoin Java Client :: Core API + * %% + * Copyright (C) 2014 - 2015 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + + +import com.google.common.collect.Lists; +import org.duniter.core.client.config.ConfigurationOption; +import org.duniter.core.client.service.ServiceLocator; +import org.apache.commons.io.FileUtils; +import org.junit.runner.Description; +import org.nuiton.i18n.I18n; +import org.nuiton.i18n.init.DefaultI18nInitializer; +import org.nuiton.i18n.init.UserI18nInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +public class TestResource extends org.duniter.core.test.TestResource { + + private static final Logger log = LoggerFactory.getLogger(TestResource.class); + + public static TestResource create() { + return new TestResource(null); + } + + public static TestResource create(String configName) { + return new TestResource(configName); + } + + private TestFixtures fixtures = new TestFixtures(); + + protected TestResource(String configName) { + super(configName); + } + + public TestFixtures getFixtures() { + return fixtures; + } + + protected void before(Description description) throws Throwable { + super.before(description); + + // Initialize configuration + initConfiguration(getConfigFileName()); + + // Init i18n + initI18n(); + + // Initialize service locator + ServiceLocator.instance().init(); + } + + /** + * Return configuration files prefix (i.e. 'allegro-test') + * Could be override by external project + * + * @return the prefix to use to retrieve configuration files + */ + protected String getConfigFilesPrefix() { + return "duniter4j-elasticsearch-test"; + } + + protected String getI18nBundleName() { + return "duniter4j-elasticsearch-i18n"; + } + + /* -- -- */ + + /** + * Convenience methods that could be override to initialize other configuration + * + * @param configFilename + * @param configArgs + */ + protected void initConfiguration(String configFilename) { + String[] configArgs = getConfigArgs(); + //PluginSettings config = new PluginSettings(configFilename, configArgs); + //PluginSettings.setInstance(config); + } + + protected void initI18n() throws IOException { + /*PluginSettings config ;//= PluginSettings.instance(); + + // --------------------------------------------------------------------// + // init i18n + // --------------------------------------------------------------------// + File i18nDirectory = new File(config.getDataDirectory(), "i18n"); + if (i18nDirectory.exists()) { + // clean i18n cache + FileUtils.cleanDirectory(i18nDirectory); + } + + FileUtils.forceMkdir(i18nDirectory); + + if (log.isDebugEnabled()) { + log.debug("I18N directory: " + i18nDirectory); + } + + Locale i18nLocale = config.getI18nLocale(); + + if (log.isInfoEnabled()) { + log.info(String.format("Starts i18n with locale [%s] at [%s]", + i18nLocale, i18nDirectory)); + } + I18n.init(new UserI18nInitializer( + i18nDirectory, new DefaultI18nInitializer(getI18nBundleName())), + i18nLocale);*/ + } + + protected String[] getConfigArgs() { + List<String> configArgs = Lists.newArrayList(); + configArgs.addAll(Lists.newArrayList( + "--option", ConfigurationOption.BASEDIR.getKey(), getResourceDirectory().getAbsolutePath())); + return configArgs.toArray(new String[configArgs.size()]); + } + +} diff --git a/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/service/BlockchainServiceTest.java b/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/service/BlockchainServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a305ac1b40ff0fca3ed27b843d7c737d1f117d50 --- /dev/null +++ b/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/service/BlockchainServiceTest.java @@ -0,0 +1,172 @@ +package org.duniter.elasticsearch.service; + +/* + * #%L + * UCoin Java Client :: Core API + * %% + * Copyright (C) 2014 - 2015 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + + +import org.duniter.core.client.config.Configuration; +import org.duniter.core.client.model.bma.BlockchainBlock; +import org.duniter.core.client.model.local.Peer; +import org.duniter.core.client.service.bma.BlockchainRemoteService; +import org.duniter.elasticsearch.TestResource; +import org.junit.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +@Ignore +public class BlockchainServiceTest { + + private static final Logger log = LoggerFactory.getLogger(BlockchainServiceTest.class); + + @ClassRule + public static final TestResource resource = TestResource.create(); + + private BlockchainService service; + private BlockchainRemoteService blockchainRemoteService; + private Configuration config; + private Peer peer; + + @Before + public void setUp() throws Exception { + //service = ServiceLocator.instance().getBlockIndexerService(); + //blockchainRemoteService = ServiceLocator.instance().getBlockchainRemoteService(); + config = Configuration.instance(); + peer = createTestPeer(); + + initLocalNode(); + } + + @Test + public void createIndex() throws Exception { + String currencyName = resource.getFixtures().getCurrency(); + + // drop and recreate index + service.deleteIndex(currencyName); + + service.createIndex(currencyName); + } + + + @Test + public void indexBlock() throws Exception { + // Read a block + BlockchainBlock currentBlock = blockchainRemoteService.getCurrentBlock(peer); + + // Create a new non-existing block + service.indexBlock(currentBlock, true); + + // Update a existing block + { + currentBlock.setMembersCount(1000000); + + service.indexBlock(currentBlock, true); + } + } + + @Test + public void indexCurrentBlock() throws Exception { + // Create a block with a fake hash + BlockchainBlock aBlock = blockchainRemoteService.getBlock(peer, 8450); + service.indexCurrentBlock(aBlock, true); + } + + @Test + // FIXME make this works + @Ignore + public void searchBlocks() throws Exception { + String currencyName = resource.getFixtures().getCurrency(); + + // Create a block with a fake hash + BlockchainBlock aBlock = blockchainRemoteService.getCurrentBlock(peer); + aBlock.setHash("myUnitTestHash"); + service.saveBlock(aBlock, true, true); + + Thread.sleep(5 * 1000); // wait 5s that ES process the block + + // match multi words + String queryText = aBlock.getHash(); + List<BlockchainBlock> blocks = service.findBlocksByHash(currencyName, queryText); + //assertResults(queryText, blocks); + + Thread.sleep(5 * 1000); // wait 5s that ES process the block + + BlockchainBlock loadBlock = service.getBlockById(currencyName, aBlock.getNumber()); + Assert.assertNotNull(loadBlock); + Assert.assertEquals(aBlock.getHash(), loadBlock.getHash()); + } + + @Test + public void getMaxBlockNumber() throws Exception { + String currencyName = resource.getFixtures().getCurrency(); + + // match multi words + Integer maxBlockNumber = service.getMaxBlockNumber(currencyName); + Assert.assertNotNull(maxBlockNumber); + } + + + @Test + @Ignore + public void allInOne() throws Exception { + + createIndex(); + indexBlock(); + searchBlocks(); + } + + /* -- internal methods */ + + protected void initLocalNode() throws Exception { + String currencyName = resource.getFixtures().getCurrency(); + + // Make sure the index exists + service.deleteIndex(currencyName); + service.createIndex(currencyName); + + // Get the first block from peer + BlockchainBlock firstBlock = blockchainRemoteService.getBlock(peer, 0); + + // Make sure the block has been indexed + service.indexBlock(firstBlock, true); + + } + + protected void assertResults(String queryText, List<BlockchainBlock> result) { + log.info(String.format("Results for a search on [%s]", queryText)); + Assert.assertNotNull(result); + Assert.assertTrue(result.size() > 0); + for (BlockchainBlock block: result) { + log.info(" - " + block.getNumber()); + } + } + + protected Peer createTestPeer() { + Peer peer = new Peer( + Configuration.instance().getNodeHost(), + Configuration.instance().getNodePort()); + + return peer; + } + +} diff --git a/duniter4j-elasticsearch/src/test/java/org/duniter/elasticsearch/service/RegistryRecordIndexerServiceTest.java b/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/service/RegistryRecordIndexerServiceTest.java similarity index 96% rename from duniter4j-elasticsearch/src/test/java/org/duniter/elasticsearch/service/RegistryRecordIndexerServiceTest.java rename to duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/service/RegistryRecordIndexerServiceTest.java index adc4a46c01cb12e9950be99e82a3684051099582..4987281db4a472a7657256319818daccb07a7bd4 100644 --- a/duniter4j-elasticsearch/src/test/java/org/duniter/elasticsearch/service/RegistryRecordIndexerServiceTest.java +++ b/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/service/RegistryRecordIndexerServiceTest.java @@ -23,6 +23,7 @@ package org.duniter.elasticsearch.service; */ import org.duniter.elasticsearch.TestResource; +import org.duniter.elasticsearch.gchange.service.RegistryService; import org.junit.Before; import org.junit.ClassRule; import org.junit.Ignore; diff --git a/duniter4j-es-gchange/src/test/resources/META-INF/services/org.duniter.core.beans.Bean b/duniter4j-es-gchange/src/test/resources/META-INF/services/org.duniter.core.beans.Bean new file mode 100644 index 0000000000000000000000000000000000000000..1d327a744e4eec6703b417254f5b19dfdbc09fa4 --- /dev/null +++ b/duniter4j-es-gchange/src/test/resources/META-INF/services/org.duniter.core.beans.Bean @@ -0,0 +1,14 @@ +org.duniter.core.client.service.bma.BlockchainRemoteServiceImpl +org.duniter.core.client.service.bma.NetworkRemoteServiceImpl +org.duniter.core.client.service.bma.WotRemoteServiceImpl +org.duniter.core.client.service.bma.TransactionRemoteServiceImpl +org.duniter.core.service.Ed25519CryptoServiceImpl +org.duniter.core.service.MailServiceImpl +org.duniter.core.client.service.HttpServiceImpl +org.duniter.core.client.service.DataContext +org.duniter.core.client.service.local.PeerServiceImpl +org.duniter.core.client.service.local.CurrencyServiceImpl +org.duniter.core.client.dao.mem.MemoryCurrencyDaoImpl +org.duniter.core.client.dao.mem.MemoryPeerDaoImpl +org.duniter.elasticsearch.service.ElasticSearchService +org.duniter.elasticsearch.service.registry.CurrencyRegistryService \ No newline at end of file diff --git a/duniter4j-es-gchange/src/test/resources/curl_test.sh b/duniter4j-es-gchange/src/test/resources/curl_test.sh new file mode 100755 index 0000000000000000000000000000000000000000..4f62377a7b20ee747e1a99f1c6ab627e9caa8b35 --- /dev/null +++ b/duniter4j-es-gchange/src/test/resources/curl_test.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +curl -XPOST "http://data.duniter.fr/market/comment/_search?pretty" -d' +{ + "query": { + "bool":{ + "filter": [ + {"term":{ + "record":"AVbieTIAup9uzWgKipsC" + } + } + ] + } + } +}' + diff --git a/duniter4j-es-gchange/src/test/resources/duniter4j-elasticsearch-localhost-node.properties b/duniter4j-es-gchange/src/test/resources/duniter4j-elasticsearch-localhost-node.properties new file mode 100644 index 0000000000000000000000000000000000000000..38d7a5d9655c9a5bb5babc7487d246139417a8ff --- /dev/null +++ b/duniter4j-es-gchange/src/test/resources/duniter4j-elasticsearch-localhost-node.properties @@ -0,0 +1,12 @@ +duniter4j.node.host=metab.ucoin.fr +duniter4j.node.port=9201 + +duniter4j.elasticsearch.embedded.enable=false +duniter4j.elasticsearch.local=fals +duniter4j.elasticsearch.http.enable=false +duniter4j.elasticsearch.cluster.name=duniter4j-elacticsearch-test + +#duniter4j.elasticsearch.cluster.name=duniter4j-elacticsearch + +duniter4j.elasticsearch.host=localhost +duniter4j.elasticsearch.port=9300 diff --git a/duniter4j-es-gchange/src/test/resources/duniter4j-elasticsearch-test.properties b/duniter4j-es-gchange/src/test/resources/duniter4j-elasticsearch-test.properties new file mode 100644 index 0000000000000000000000000000000000000000..27e326f1e7a5e4a4ee67c86df9ca03e8c5b6d2df --- /dev/null +++ b/duniter4j-es-gchange/src/test/resources/duniter4j-elasticsearch-test.properties @@ -0,0 +1,16 @@ +duniter4j.node.host=metab.ucoin.fr +duniter4j.node.port=9201 + +duniter4j.basedir=target/es-home + +#duniter4j.elasticsearch.data +#duniter4j.elasticsearch.embedded.enable=true +duniter4j.elasticsearch.local=false +duniter4j.elasticsearch.http.enable=true +duniter4j.elasticsearch.cluster.name=duniter4j-elasticsearch + +#duniter4j.elasticsearch.cluster.name=duniter4j-elacticsearch + +duniter4j.elasticsearch.embedded.enable=false +duniter4j.elasticsearch.host=192.168.0.5 +duniter4j.elasticsearch.port=9300 diff --git a/duniter4j-es-gchange/src/test/resources/log4j.properties b/duniter4j-es-gchange/src/test/resources/log4j.properties new file mode 100644 index 0000000000000000000000000000000000000000..2712b72e0f06c247e8b96a4b1265f95105fda739 --- /dev/null +++ b/duniter4j-es-gchange/src/test/resources/log4j.properties @@ -0,0 +1,18 @@ +### +# Global logging configuration +log4j.rootLogger=ERROR, stdout + +# Console output +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %5p (%c:%L) - [%t] %m%n + +# duniter4j levels +log4j.logger.org.duniter=INFO +#log4j.logger.org.duniter=DEBUG +log4j.logger.org.duniter.core=WARN +log4j.logger.org.duniter.elasticsearch=DEBUG + +# Other frameworks levels +log4j.logger.org.elasticsearch=INFO + diff --git a/duniter4j-es-gchange/src/test/resources/registry-test-records.json b/duniter4j-es-gchange/src/test/resources/registry-test-records.json new file mode 100644 index 0000000000000000000000000000000000000000..c9c11c01be29242dca860f9e0e692539c101e07c --- /dev/null +++ b/duniter4j-es-gchange/src/test/resources/registry-test-records.json @@ -0,0 +1,23 @@ +/* + * #%L + * Duniter4j :: ElasticSearch Plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ +{"index": {"_id": "AVOt3cmVo7-63byx1Jow"}} +{"title": "Benoit Lavenier (kimamila)", "description": "Pasionné de plein de trucs...", "category": "particulier", "location":"Martigné-sur-Mayenne", "pictures": [{"src": "data:image/jpeg;base64,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"}], "issuer": "G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU", "hash": "7zPfFwyXGZKYMmLTzVynFGVEwLDfj48a5E3EA2RWtifq", "signature": "CP4p0+Nby/Fs7exp/mlLnOAu8iMJwyLRW6UHkZXZX8q8WbqxHYBRo2uzpcFAT0zEYSig7j3HqYdeoA3MpU1BCQ=="} diff --git a/duniter4j-es-user/pom.xml b/duniter4j-es-user/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..d4f1f5651b7f30c470046df1cf94dcf91e589ec3 --- /dev/null +++ b/duniter4j-es-user/pom.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>duniter4j</artifactId> + <groupId>org.duniter</groupId> + <version>0.3.5-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>duniter4j-es-user</artifactId> + <packaging>jar</packaging> + <name>Duniter4j :: ElasticSearch User plugin</name> + + <dependencies> + <dependency> + <groupId>org.duniter</groupId> + <artifactId>duniter4j-es-core</artifactId> + <version>${project.version}</version> + </dependency> + + <!-- Elastic Search --> + <dependency> + <groupId>org.elasticsearch</groupId> + <artifactId>elasticsearch</artifactId> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <resources> + <resource> + <directory>src/main/filtered-resources</directory> + <filtering>true</filtering> + <includes> + <include>*.config</include> + <include>**/*.properties</include> + </includes> + </resource> + <resource> + <directory>src/main/resources</directory> + <filtering>false</filtering> + </resource> + </resources> + + <plugins> + <plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>i18n-maven-plugin</artifactId> + + <executions> + <execution> + <id>scan-sources</id> + <configuration> + <entries> + <entry> + <specificGoal>parserValidation</specificGoal> + <basedir>${maven.src.dir}/main/java/</basedir> + <includes> + <param>**/**-validation.xml</param> + </includes> + </entry> + </entries> + </configuration> + <goals> + <goal>parserJava</goal> + <goal>parserValidation</goal> + <goal>gen</goal> + </goals> + </execution> + <execution> + <id>make-bundle</id> + <goals> + <goal>bundle</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>assembly-plugin</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + <configuration> + <attach>true</attach> + <appendAssemblyId>false</appendAssemblyId> + <finalName>${bundlePrefix}</finalName> + <descriptors> + <descriptor> + ${basedir}/src/main/assembly/plugin.xml + </descriptor> + </descriptors> + <skipAssembly>${assembly.skip}</skipAssembly> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> \ No newline at end of file diff --git a/duniter4j-es-user/src/main/assembly/plugin.xml b/duniter4j-es-user/src/main/assembly/plugin.xml new file mode 100644 index 0000000000000000000000000000000000000000..141f9bb56a0c0393ad0f19537ef197805bc5bc18 --- /dev/null +++ b/duniter4j-es-user/src/main/assembly/plugin.xml @@ -0,0 +1,43 @@ +<?xml version="1.0"?> +<assembly> + <id>plugin</id> + + + <formats> + <format>zip</format> + </formats> + + <includeBaseDirectory>false</includeBaseDirectory> + + <dependencySets> + <dependencySet> + <outputDirectory>/</outputDirectory> + <useProjectArtifact>true</useProjectArtifact> + <useTransitiveFiltering>true</useTransitiveFiltering> + <excludes> + <exclude>org.duniter:duniter4j-es-core</exclude> + <exclude>org.elasticsearch:elasticsearch</exclude> + <exclude>net.java.dev.jna:jna</exclude> + <exclude>com.fasterxml.jackson.core:jackson-core</exclude> + <exclude>log4j:log4j</exclude> + </excludes> + </dependencySet> + </dependencySets> + + <fileSets> + <fileSet> + <includes> + <include>LICENSE</include> + </includes> + </fileSet> + + <fileSet> + <directory>target/classes</directory> + <outputDirectory/> + <includes> + <include>plugin-descriptor.properties</include> + <include>plugin-security.policy</include> + </includes> + </fileSet> + </fileSets> +</assembly> \ No newline at end of file diff --git a/duniter4j-es-user/src/main/filtered-resources/log4j.properties b/duniter4j-es-user/src/main/filtered-resources/log4j.properties new file mode 100644 index 0000000000000000000000000000000000000000..7b6667b1facc361ed8b1993869f728e2c01f1799 --- /dev/null +++ b/duniter4j-es-user/src/main/filtered-resources/log4j.properties @@ -0,0 +1,32 @@ + +# Global logging configuration +log4j.rootLogger=ERROR, stdout, file +#log4j.rootLogger=ERROR, stdout + +# Console output +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %5p %m%n + +# Duniter4j levels +log4j.logger.org.duniter=INFO +#log4j.logger.org.duniter.core.client=DEBUG +#log4j.logger.org.duniter.core.client.service=DEBUG +log4j.logger.org.duniter.elasticsearch=DEBUG + +# Other frameworks levels +log4j.logger.org.nuiton.util=WARN +log4j.logger.org.nuiton.config=WARN +log4j.logger.org.nuiton.converter=WARN +log4j.logger.org.nuiton.i18n=ERROR +log4j.logger.org.elasticsearch=WARN +#log4j.logger.org.elasticsearch=INFO + +log4j.appender.file=org.apache.log4j.RollingFileAppender +log4j.appender.file.file=${duniter4j.log.file} +log4j.appender.file.MaxFileSize=10MB +log4j.appender.file.MaxBackupIndex=4 + +log4j.appender.file.layout=org.apache.log4j.PatternLayout +log4j.appender.file.layout.ConversionPattern=%d{ISO8601} %5p (%c:%L) - [%t] %m%n + diff --git a/duniter4j-es-user/src/main/filtered-resources/plugin-descriptor.properties b/duniter4j-es-user/src/main/filtered-resources/plugin-descriptor.properties new file mode 100644 index 0000000000000000000000000000000000000000..785b7ddf0ec956453593ec5a48aa641e6578269c --- /dev/null +++ b/duniter4j-es-user/src/main/filtered-resources/plugin-descriptor.properties @@ -0,0 +1,9 @@ +name=duniter4j-es-user +description=Plugin for Duniter User API +version=${project.version} +site=false +jvm=true +classname=org.duniter.elasticsearch.user.Plugin +java.version=1.7 +elasticsearch.version=2.3.3 +isolated=true diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/Plugin.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/Plugin.java new file mode 100644 index 0000000000000000000000000000000000000000..f1bde795a5cf87a32a28e936046038343f51fb35 --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/Plugin.java @@ -0,0 +1,86 @@ +package org.duniter.elasticsearch.user; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import com.google.common.collect.Lists; +import org.duniter.elasticsearch.PluginInit; +import org.duniter.elasticsearch.user.service.ServiceModule; +import org.duniter.elasticsearch.user.rest.RestModule; +import org.elasticsearch.common.component.LifecycleComponent; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.inject.Module; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.ESLoggerFactory; +import org.elasticsearch.common.settings.Settings; + +import java.util.Collection; + +public class Plugin extends org.elasticsearch.plugins.Plugin { + + private ESLogger log = ESLoggerFactory.getLogger(Plugin.class.getName()); + + private boolean enable; + + @Inject public Plugin(Settings settings) { + this.enable = settings.getAsBoolean("duniter.user.enabled", true); + } + + @Override + public String name() { + return "duniter.user"; + } + + @Override + public String description() { + return "Duniter ElasticSearch User Plugin"; + } + + @Override + public Collection<Module> nodeModules() { + Collection<Module> modules = Lists.newArrayList(); + if (!enable) { + log.warn(description() + " has been disabled."); + return modules; + } + + modules.add(new RestModule()); + modules.add(new ServiceModule()); + + return modules; + } + + @Override + public Collection<Class<? extends LifecycleComponent>> nodeServices() { + Collection<Class<? extends LifecycleComponent>> components = Lists.newArrayList(); + if (!enable) { + return components; + } + components.add(PluginSettings.class); + components.add(PluginInit.class); + return components; + } + + /* -- protected methods -- */ + + +} \ No newline at end of file diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/node/DuniterNode.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginInit.java similarity index 57% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/node/DuniterNode.java rename to duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginInit.java index 1f6d9019d64ea18069dc3984e0ac9cc978a568a0..87f044bf233fbc8891f4ead71a9cbd35a70c5889 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/node/DuniterNode.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginInit.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.node; +package org.duniter.elasticsearch.user; /* * #%L @@ -22,17 +22,13 @@ package org.duniter.elasticsearch.node; * #L% */ -import org.duniter.core.client.model.elasticsearch.Currency; -import org.duniter.core.client.model.local.Peer; import org.duniter.elasticsearch.PluginSettings; -import org.duniter.elasticsearch.action.security.RestSecurityController; -import org.duniter.elasticsearch.service.*; -import org.duniter.elasticsearch.service.event.Event; -import org.duniter.elasticsearch.service.event.EventCodes; -import org.duniter.elasticsearch.service.event.EventService; -import org.duniter.elasticsearch.service.synchro.SynchroService; import org.duniter.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.client.Client; +import org.duniter.elasticsearch.user.service.HistoryService; +import org.duniter.elasticsearch.user.service.MessageService; +import org.duniter.elasticsearch.user.service.SynchroService; +import org.duniter.elasticsearch.user.service.UserService; +import org.duniter.elasticsearch.user.service.event.UserEventService; import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.inject.Inject; @@ -40,28 +36,23 @@ import org.elasticsearch.common.inject.Injector; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.rest.RestRequest; /** * Created by blavenie on 17/06/16. */ -public class DuniterNode extends AbstractLifecycleComponent<DuniterNode> { +public class PluginInit extends AbstractLifecycleComponent<org.duniter.elasticsearch.PluginInit> { private final PluginSettings pluginSettings; private final ThreadPool threadPool; private final Injector injector; private final static ESLogger logger = Loggers.getLogger("node"); - private final Client client; - private final String clusterName; @Inject - public DuniterNode(Client client, Settings settings, PluginSettings pluginSettings, ThreadPool threadPool, final Injector injector) { + public PluginInit(Settings settings, PluginSettings pluginSettings, ThreadPool threadPool, final Injector injector) { super(settings); this.pluginSettings = pluginSettings; this.threadPool = threadPool; this.injector = injector; - this.client = client; - this.clusterName = settings.get("cluster.name"); } @Override @@ -75,15 +66,6 @@ public class DuniterNode extends AbstractLifecycleComponent<DuniterNode> { }, ClusterHealthStatus.YELLOW, ClusterHealthStatus.GREEN); }, ClusterHealthStatus.YELLOW, ClusterHealthStatus.GREEN); - // When started - threadPool.scheduleOnStarted(() -> { - // Notify admin - injector.getInstance(EventService.class) - .notifyAdmin(new Event( - Event.EventType.INFO, - EventCodes.NODE_STARTED.name(), - new String[]{clusterName})); - }); } @Override @@ -102,27 +84,18 @@ public class DuniterNode extends AbstractLifecycleComponent<DuniterNode> { if (reloadIndices) { if (logger.isInfoEnabled()) { - logger.info("Reloading all Duniter indices..."); + logger.info("Reloading all User indices..."); } - injector.getInstance(RegistryService.class) - .deleteIndex() - .createIndexIfNotExists(); - injector.getInstance(MarketService.class) + injector.getInstance(HistoryService.class) .deleteIndex() .createIndexIfNotExists(); injector.getInstance(MessageService.class) .deleteIndex() .createIndexIfNotExists(); - injector.getInstance(UserService.class) .deleteIndex() .createIndexIfNotExists(); - - injector.getInstance(HistoryService.class) - .deleteIndex() - .createIndexIfNotExists(); - - injector.getInstance(EventService.class) + injector.getInstance(UserEventService.class) .deleteIndex() .createIndexIfNotExists(); @@ -135,12 +108,10 @@ public class DuniterNode extends AbstractLifecycleComponent<DuniterNode> { if (logger.isInfoEnabled()) { logger.info("Checking Duniter indices..."); } - injector.getInstance(RegistryService.class).createIndexIfNotExists(); - injector.getInstance(MarketService.class).createIndexIfNotExists(); - injector.getInstance(MessageService.class).createIndexIfNotExists(); - injector.getInstance(UserService.class).createIndexIfNotExists(); injector.getInstance(HistoryService.class).createIndexIfNotExists(); - injector.getInstance(EventService.class).createIndexIfNotExists(); + injector.getInstance(UserService.class).createIndexIfNotExists(); + injector.getInstance(MessageService.class).createIndexIfNotExists(); + injector.getInstance(UserEventService.class).createIndexIfNotExists(); if (logger.isInfoEnabled()) { logger.info("Checking Duniter indices... [OK]"); @@ -149,24 +120,6 @@ public class DuniterNode extends AbstractLifecycleComponent<DuniterNode> { } protected void synchronize() { - if (pluginSettings.enableBlockchainSync()) { - - Peer peer = pluginSettings.checkAndGetPeer(); - - // Index (or refresh) node's currency - Currency currency = injector.getInstance(RegistryService.class).indexCurrencyFromPeer(peer, true); - - // Add access to currency index - injector.getInstance(RestSecurityController.class).allowIndexType(RestRequest.Method.GET, - currency.getCurrencyName(), - BlockchainService.BLOCK_TYPE); - - // Index blocks (and listen if new block appear) - injector.getInstance(BlockchainService.class) - .indexLastBlocks(peer) - .listenAndIndexNewBlock(peer); - } - if (pluginSettings.enableDataSync()) { // Synchronize injector.getInstance(SynchroService.class).synchronize(); diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginSettings.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginSettings.java new file mode 100644 index 0000000000000000000000000000000000000000..2f6e10774d1140209e129e3cc85742a8b921e763 --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginSettings.java @@ -0,0 +1,104 @@ +package org.duniter.elasticsearch.user; + +/* + * #%L + * UCoin Java Client :: Core API + * %% + * Copyright (C) 2014 - 2015 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + + +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; + +/** + * Access to configuration options + * @author Benoit Lavenier <benoit.lavenier@e-is.pro> + * @since 1.0 + */ +public class PluginSettings extends org.duniter.elasticsearch.PluginSettings { + + @Inject + public PluginSettings(Settings settings) { + super(settings); + } + + public String getDefaultStringAnalyzer() { + return settings.get("duniter.string.analyzer", "english"); + } + + public String getKeyringSalt() { + return settings.get("duniter.keyring.salt"); + } + + public String getKeyringPassword() { + return settings.get("duniter.keyring.password"); + } + + public String getKeyringPublicKey() { + return settings.get("duniter.keyring.pub"); + } + + public String getKeyringSecretKey() { + return settings.get("duniter.keyring.sec"); + } + + public boolean enableDataSync() { + return settings.getAsBoolean("duniter.user.sync.enable", false); + } + + public String getDataSyncHost() { + return settings.get("duniter.user.sync.host", "data.duniter.fr"); + } + + public int getDataSyncPort() { + return settings.getAsInt("duniter.user.sync.port", 80); + } + + public String getMailSmtpHost() { + return settings.get("duniter.mail.smtp.host", "localhost"); + } + + public int getMailSmtpPort() { + return settings.getAsInt("duniter.mail.smtp.port", 25); + } + + public String getMailSmtpUsername() { + return settings.get("duniter.mail.smtp.username"); + } + + public String getMailSmtpPassword() { + return settings.get("duniter.mail.smtp.password"); + } + + public String getMailAdmin() { + return settings.get("duniter.mail.admin"); + } + + public String getMailFrom() { + return settings.get("duniter.mail.from", "no-reply@duniter.fr"); + } + + public String getMailSubjectPrefix() { + return settings.get("duniter.mail.subject.prefix", "[Duniter4j ES]"); + } + + protected String getI18nBundleName() { + return "duniter4j-es-user-i18n"; + } +} diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/RestModule.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/RestModule.java new file mode 100644 index 0000000000000000000000000000000000000000..bb044f174266f3ec88b1c3e27f50f866df0de519 --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/RestModule.java @@ -0,0 +1,52 @@ +package org.duniter.elasticsearch.user.rest; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.user.rest.history.RestHistoryDeleteIndexAction; +import org.duniter.elasticsearch.user.rest.message.RestMessageInboxIndexAction; +import org.duniter.elasticsearch.user.rest.message.RestMessageOutboxIndexAction; +import org.duniter.elasticsearch.user.rest.user.RestUserProfileIndexAction; +import org.duniter.elasticsearch.user.rest.user.RestUserProfileUpdateAction; +import org.duniter.elasticsearch.user.rest.user.RestUserSettingsIndexAction; +import org.duniter.elasticsearch.user.rest.user.RestUserSettingsUpdateAction; +import org.elasticsearch.common.inject.AbstractModule; +import org.elasticsearch.common.inject.Module; + +public class RestModule extends AbstractModule implements Module { + + @Override protected void configure() { + + // User + bind(RestUserProfileIndexAction.class).asEagerSingleton(); + bind(RestUserProfileUpdateAction.class).asEagerSingleton(); + bind(RestUserSettingsIndexAction.class).asEagerSingleton(); + bind(RestUserSettingsUpdateAction.class).asEagerSingleton(); + + // History + bind(RestHistoryDeleteIndexAction.class).asEagerSingleton(); + + // Message + bind(RestMessageInboxIndexAction.class).asEagerSingleton(); + bind(RestMessageOutboxIndexAction.class).asEagerSingleton(); + } +} \ No newline at end of file diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/history/RestHistoryDeleteIndexAction.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/history/RestHistoryDeleteIndexAction.java new file mode 100644 index 0000000000000000000000000000000000000000..8c7ddae551366ac77b78701bb70ea003b970ce41 --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/history/RestHistoryDeleteIndexAction.java @@ -0,0 +1,45 @@ +package org.duniter.elasticsearch.user.rest.history; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.user.service.HistoryService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.ESLoggerFactory; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestHistoryDeleteIndexAction extends AbstractRestPostIndexAction { + + @Inject + public RestHistoryDeleteIndexAction(Settings settings, RestController controller, Client client, + RestSecurityController securityController, HistoryService service) { + super(settings, controller, client, securityController, + HistoryService.INDEX, + HistoryService.DELETE_TYPE, + json -> service.indexDeleteFromJson(json)); + } +} \ No newline at end of file diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/message/RestMessageInboxIndexAction.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/message/RestMessageInboxIndexAction.java new file mode 100644 index 0000000000000000000000000000000000000000..c469cba9b240b12f3f78baa1c16b777f317d106e --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/message/RestMessageInboxIndexAction.java @@ -0,0 +1,44 @@ +package org.duniter.elasticsearch.user.rest.message; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.user.service.MessageService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestMessageInboxIndexAction extends AbstractRestPostIndexAction { + + @Inject + public RestMessageInboxIndexAction(Settings settings, RestController controller, Client client, + RestSecurityController securityController, + final MessageService service) { + super(settings, controller, client, securityController, + MessageService.INDEX, + MessageService.RECORD_TYPE, + json -> service.indexRecordFromJson(json)); + } +} \ No newline at end of file diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/message/RestMessageOutboxIndexAction.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/message/RestMessageOutboxIndexAction.java new file mode 100644 index 0000000000000000000000000000000000000000..e2ac877416f12db6cea984347f1e7cb8197d16d8 --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/message/RestMessageOutboxIndexAction.java @@ -0,0 +1,44 @@ +package org.duniter.elasticsearch.user.rest.message; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.user.service.MessageService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestMessageOutboxIndexAction extends AbstractRestPostIndexAction { + + @Inject + public RestMessageOutboxIndexAction(Settings settings, RestController controller, Client client, + RestSecurityController securityController, + final MessageService service) { + super(settings, controller, client, securityController, + MessageService.INDEX, + MessageService.OUTBOX_TYPE, + json -> service.indexRecordFromJson(json)); + } +} \ No newline at end of file diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserProfileIndexAction.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserProfileIndexAction.java new file mode 100644 index 0000000000000000000000000000000000000000..899db7e74d54db3a845941a58f762ff392496c28 --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserProfileIndexAction.java @@ -0,0 +1,44 @@ +package org.duniter.elasticsearch.user.rest.user; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.user.service.UserService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestUserProfileIndexAction extends AbstractRestPostIndexAction { + + @Inject + public RestUserProfileIndexAction(Settings settings, RestController controller, Client client, + RestSecurityController securityController, + UserService service) { + super(settings, controller, client, securityController, + UserService.INDEX, + UserService.PROFILE_TYPE, + json -> service.indexProfileFromJson(json)); + } +} \ No newline at end of file diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserProfileUpdateAction.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserProfileUpdateAction.java new file mode 100644 index 0000000000000000000000000000000000000000..b33b4f710c26a79e0e36827d6a773dad3ddc5c3f --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserProfileUpdateAction.java @@ -0,0 +1,45 @@ +package org.duniter.elasticsearch.user.rest.user; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.user.service.UserService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestUserProfileUpdateAction extends AbstractRestPostUpdateAction { + + @Inject + public RestUserProfileUpdateAction(Settings settings, RestController controller, Client client, + RestSecurityController securityController, + UserService service) { + super(settings, controller, client, securityController, + UserService.INDEX, + UserService.PROFILE_TYPE, + (json, id) -> service.updateProfileFromJson(json, id)); + } + +} \ No newline at end of file diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserSettingsIndexAction.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserSettingsIndexAction.java new file mode 100644 index 0000000000000000000000000000000000000000..79371aa009d18686a411833a984847ea75fa0ff7 --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserSettingsIndexAction.java @@ -0,0 +1,45 @@ +package org.duniter.elasticsearch.user.rest.user; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.user.service.UserService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestUserSettingsIndexAction extends AbstractRestPostIndexAction { + + @Inject + public RestUserSettingsIndexAction(Settings settings, RestController controller, Client client, + RestSecurityController securityController, + final UserService service) { + super(settings, controller, client, securityController, + UserService.INDEX, + UserService.SETTINGS_TYPE, + json -> service.indexSettingsFromJson(json)); + } + +} \ No newline at end of file diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserSettingsUpdateAction.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserSettingsUpdateAction.java new file mode 100644 index 0000000000000000000000000000000000000000..24c8f5be74b42c771320bb455052832e00f56f1a --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/rest/user/RestUserSettingsUpdateAction.java @@ -0,0 +1,45 @@ +package org.duniter.elasticsearch.user.rest.user; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; +import org.duniter.elasticsearch.rest.security.RestSecurityController; +import org.duniter.elasticsearch.user.service.UserService; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestController; + +public class RestUserSettingsUpdateAction extends AbstractRestPostUpdateAction { + + @Inject + public RestUserSettingsUpdateAction(Settings settings, RestController controller, Client client, + RestSecurityController securityController, + final UserService service) { + super(settings, controller, client, securityController, + UserService.INDEX, + UserService.SETTINGS_TYPE, + (json, id) -> service.updateSettingsFromJson(json, id)); + } + +} \ No newline at end of file diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/HistoryService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/HistoryService.java similarity index 98% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/HistoryService.java rename to duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/HistoryService.java index fc3f0e4368b1c4c9bfd757e71a9711f01f2caedf..a000508510784883614e8ec24c7c76edcf68fd0b 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/HistoryService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/HistoryService.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.service; +package org.duniter.elasticsearch.user.service; /* * #%L @@ -31,6 +31,7 @@ import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; import org.duniter.elasticsearch.PluginSettings; import org.duniter.elasticsearch.exception.NotFoundException; +import org.duniter.elasticsearch.service.AbstractService; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.Client; diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/MessageService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java similarity index 96% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/MessageService.java rename to duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java index e6674b8756d5f2012de025443cec8956a472069c..3f501b75b044cef9db25a9e186fc4463db3e4d0a 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/MessageService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.service; +package org.duniter.elasticsearch.user.service; /* * #%L @@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; import org.duniter.elasticsearch.PluginSettings; +import org.duniter.elasticsearch.service.AbstractService; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.Client; @@ -49,8 +50,8 @@ public class MessageService extends AbstractService { @Inject - public MessageService(Client client, PluginSettings settings, CryptoService cryptoService) { - super("gchange." + INDEX, client, settings, cryptoService); + public MessageService(Client client, PluginSettings settings, CryptoService cryptoService, UserService userService) { + super("duniter." + INDEX, client, settings, cryptoService); } /** @@ -62,7 +63,6 @@ public class MessageService extends AbstractService { return this; } - public boolean existsIndex() { return super.existsIndex(INDEX); } diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/ServiceModule.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/ServiceModule.java new file mode 100644 index 0000000000000000000000000000000000000000..a24ab24b969f2bf69e0b18fcc87d8eb6581bc7f4 --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/ServiceModule.java @@ -0,0 +1,41 @@ +package org.duniter.elasticsearch.user.service; + +/* + * #%L + * duniter4j-elasticsearch-plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.elasticsearch.user.service.event.UserEventService; +import org.elasticsearch.common.inject.AbstractModule; +import org.elasticsearch.common.inject.Module; + +public class ServiceModule extends AbstractModule implements Module { + + @Override protected void configure() { + bind(MessageService.class).asEagerSingleton(); + bind(HistoryService.class).asEagerSingleton(); + bind(UserService.class).asEagerSingleton(); + bind(UserEventService.class).asEagerSingleton(); + bind(SynchroService.class).asEagerSingleton(); + } + + /* protected methods */ + +} \ No newline at end of file diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/SynchroService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/SynchroService.java new file mode 100644 index 0000000000000000000000000000000000000000..155e9c56fcc07bd7f036badf27405bde2d49f60a --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/SynchroService.java @@ -0,0 +1,75 @@ +package org.duniter.elasticsearch.user.service; + +/* + * #%L + * Duniter4j :: ElasticSearch Plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import org.duniter.core.client.model.local.Peer; +import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.PluginSettings; +import org.duniter.elasticsearch.service.ServiceLocator; +import org.duniter.elasticsearch.service.AbstractSynchroService; +import org.duniter.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; + +/** + * Created by blavenie on 27/10/16. + */ +public class SynchroService extends AbstractSynchroService { + + @Inject + public SynchroService(Client client, PluginSettings settings, CryptoService cryptoService, + ThreadPool threadPool, final ServiceLocator serviceLocator) { + super(client, settings, cryptoService, threadPool, serviceLocator); + } + + public void synchronize() { + logger.info("Synchronizing user data..."); + + Peer peer = getPeerFromAPI("ES API"); + synchronize(peer); + } + + /* -- protected methods -- */ + + + protected void synchronize(Peer peer) { + + long sinceTime = 0; // ToDO: get last sync time from somewhere ? (e.g. a specific index) + + logger.info(String.format("[%s] Synchronizing user data since %s...", peer.toString(), sinceTime)); + + importUserChanges(peer, sinceTime); + importMessageChanges(peer, sinceTime); + + logger.info(String.format("[%s] Synchronizing user data since %s [OK]", peer.toString(), sinceTime)); + } + + protected void importUserChanges(Peer peer, long sinceTime) { + importChanges(peer, UserService.INDEX, UserService.PROFILE_TYPE, sinceTime); + importChanges(peer, UserService.INDEX, UserService.SETTINGS_TYPE, sinceTime); + } + + protected void importMessageChanges(Peer peer, long sinceTime) { + importChanges(peer, MessageService.INDEX, MessageService.RECORD_TYPE, sinceTime); + } +} diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/UserService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java similarity index 98% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/UserService.java rename to duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java index 4163b6e2f936922ad3f53c6b986de9d8d05fac84..9977f5a3b10d562522b9cd1b35780c614943479f 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/UserService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.service; +package org.duniter.elasticsearch.user.service; /* * #%L @@ -25,13 +25,12 @@ package org.duniter.elasticsearch.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import org.duniter.core.client.service.bma.BlockchainRemoteService; -import org.duniter.core.client.service.bma.WotRemoteService; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; import org.duniter.core.service.MailService; import org.duniter.elasticsearch.PluginSettings; import org.duniter.elasticsearch.exception.AccessDeniedException; +import org.duniter.elasticsearch.service.AbstractService; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.update.UpdateResponse; @@ -50,6 +49,7 @@ public class UserService extends AbstractService { public static final String INDEX = "user"; public static final String PROFILE_TYPE = "profile"; + public static final String EVENT_TYPE = "profile"; public static final String SETTINGS_TYPE = "settings"; @Inject diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/event/Event.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEvent.java similarity index 58% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/event/Event.java rename to duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEvent.java index 6b5d517646c118040d3e1170d3a896913b096413..9046f3c145480f2199dc86d54659e938f2179921 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/event/Event.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEvent.java @@ -1,4 +1,26 @@ -package org.duniter.elasticsearch.service.event; +package org.duniter.elasticsearch.user.service.event; + +/* + * #%L + * Duniter4j :: ElasticSearch Plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ import org.nuiton.i18n.I18n; @@ -7,7 +29,7 @@ import java.util.Locale; /** * Created by blavenie on 29/11/16. */ -public class Event { +public class UserEvent { private EventType type; @@ -20,11 +42,11 @@ public class Event { private String[] params; - public Event(EventType type, String code) { + public UserEvent(EventType type, String code) { this(type, code, null); } - public Event(EventType type, String code, String[] params) { + public UserEvent(EventType type, String code, String[] params) { this.type = type; this.code = code; this.params = params; diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventCodes.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventCodes.java new file mode 100644 index 0000000000000000000000000000000000000000..368e025de692bf7e1ee3540ca8549f6fa6851d7a --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventCodes.java @@ -0,0 +1,34 @@ +package org.duniter.elasticsearch.user.service.event; + +/* + * #%L + * Duniter4j :: ElasticSearch Plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +/** + * Created by blavenie on 29/11/16. + */ +public enum UserEventCodes { + + NODE_STARTED, + CREATE_DOC, + UPDATE_DOC, + COMMENT_DOC +} diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventListener.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventListener.java new file mode 100644 index 0000000000000000000000000000000000000000..7d34b28bb45e7e0d8ca85a632a746fdf00eece72 --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventListener.java @@ -0,0 +1,6 @@ +package org.duniter.elasticsearch.user.service.event; + +public interface UserEventListener { + String getId(); + void onEvent(UserEvent event); +} \ No newline at end of file diff --git a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/event/EventService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventService.java similarity index 78% rename from duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/event/EventService.java rename to duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventService.java index 745063d57705025440ba88191c78d4b23e409403..0ce4ede8b2c1e27aea6d47ea16f9b3ba130b63ae 100644 --- a/duniter4j-elasticsearch/src/main/java/org/duniter/elasticsearch/service/event/EventService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventService.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.service.event; +package org.duniter.elasticsearch.user.service.event; /* * #%L @@ -34,42 +34,63 @@ import org.duniter.core.util.crypto.CryptoUtils; import org.duniter.core.util.crypto.KeyPair; import org.duniter.elasticsearch.PluginSettings; import org.duniter.elasticsearch.service.AbstractService; +import org.duniter.elasticsearch.service.changes.ChangeEvent; +import org.duniter.elasticsearch.service.changes.ChangeListener; +import org.duniter.elasticsearch.service.changes.ChangeService; +import org.duniter.elasticsearch.service.changes.ChangeUtils; +import org.duniter.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.nuiton.i18n.I18n; import java.io.IOException; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; /** * Created by Benoit on 30/03/2015. */ -public class EventService extends AbstractService { +public class UserEventService extends AbstractService implements ChangeListener { public static final String INDEX = "user"; public static final String EVENT_TYPE = "event"; + private static final Map<String, UserEventListener> LISTENERS = new HashMap<>(); + + public static void registerListener(UserEventListener listener) { + LISTENERS.put(listener.getId(), listener); + } + + public static void unregisterListener(UserEventListener listener) { + LISTENERS.remove(listener.getId()); + } private final MailService mailService; + private final ThreadPool threadPool; public final KeyPair nodeKeyPair; public final String nodePubkey; @Inject - public EventService(Client client, PluginSettings settings, CryptoService cryptoService, MailService mailService) { + public UserEventService(Client client, PluginSettings settings, CryptoService cryptoService, MailService mailService, + ThreadPool threadPool) { super("duniter.event." + INDEX, client, settings, cryptoService); this.mailService = mailService; + this.threadPool = threadPool; this.nodeKeyPair = getNodeKeyPairOrNull(pluginSettings); this.nodePubkey = getNodePubKey(this.nodeKeyPair); + ChangeService.registerListener(this); } /** * Notify cluster admin */ - public void notifyAdmin(Event event) { + public void notifyAdmin(UserEvent event) { Locale locale = I18n.getDefaultLocale(); // TODO get locale from admin // Add new event to index @@ -92,32 +113,54 @@ public class EventService extends AbstractService { } } + /** + * Notify a new document + */ + public void notifyNewDocument(String index, String type, String id, String issuer) { + + String docId = String.format("%s/%s/%s", index, type, id); + logger.info(String.format("Detected new document at: %s", docId)); + + notifyUser(issuer, new UserEvent(UserEvent.EventType.INFO, UserEventCodes.CREATE_DOC.name(), new String[]{docId})); + } + /** * Notify a user */ - public void notifyUser(String recipient, Event event) { + public void notifyUser(String recipient, UserEvent event) { + // Notify user + threadPool.schedule(() -> { + doNotifyUser(recipient, event); + }, TimeValue.timeValueMillis(100)); + } - String email = getEmailByPk(recipient); - Locale locale = I18n.getDefaultLocale(); // TODO get locale + @Override + public void onChanges(String json) { + // TODO get doc issuer + String issuer = nodePubkey; - // Add new event to index - indexEvent(recipient, locale, event); + ChangeEvent event = ChangeUtils.fromJson(objectMapper, json); - // Send email to user - if (StringUtils.isNotBlank(email)) { - String subjectPrefix = pluginSettings.getMailSubjectPrefix(); - sendEmail(email, - I18n.l(locale, "duniter4j.event.subject."+event.getType().name(), subjectPrefix), - event.getLocalizedMessage(locale)); + // Skip event itself (avoid recursive call) + if (event.getIndex().equals(INDEX) && event.getType().equals(EVENT_TYPE)) { + return; + } + + if (event.getOperation() == ChangeEvent.Operation.CREATE) { + notifyNewDocument(event.getIndex(), event.getType(), event.getId(), issuer); } } + @Override + public String getId() { + return "UserEventService"; + } + /** * Delete blockchain index, and all data - * @throws JsonProcessingException */ - public EventService deleteIndex() { + public UserEventService deleteIndex() { deleteIndexIfExists(INDEX); return this; } @@ -129,7 +172,7 @@ public class EventService extends AbstractService { /** * Create index need for blockchain registry, if need */ - public EventService createIndexIfNotExists() { + public UserEventService createIndexIfNotExists() { try { if (!existsIndex(INDEX)) { createIndex(); @@ -146,7 +189,7 @@ public class EventService extends AbstractService { * Create index need for category registry * @throws JsonProcessingException */ - public EventService createIndex() throws JsonProcessingException { + public UserEventService createIndex() throws JsonProcessingException { logger.info(String.format("Creating index [%s/%s]", INDEX, EVENT_TYPE)); CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(INDEX); @@ -162,7 +205,7 @@ public class EventService extends AbstractService { return this; } - public String indexEvent(String recipient, Locale locale, Event event) { + public String indexEvent(String recipient, Locale locale, UserEvent event) { // Generate json String eventJson; if (StringUtils.isNotBlank(nodePubkey)) { @@ -265,10 +308,11 @@ public class EventService extends AbstractService { } private String getEmailByPk(String issuerPk) { - return "benoit.lavenier@e-is.pro"; + // TODO get it from user profile ? + return pluginSettings.getMailAdmin(); } - private String getEmailSubject(Locale locale, Event event) { + private String getEmailSubject(Locale locale, UserEvent event) { return I18n.l(locale, "duniter4j.event.subject."+event.getType().name()); } @@ -296,7 +340,7 @@ public class EventService extends AbstractService { } } - private String toJson(String issuer, String recipient, Locale locale, Event event, String signature) { + private String toJson(String issuer, String recipient, Locale locale, UserEvent event, String signature) { try { XContentBuilder eventObject = XContentFactory.jsonBuilder().startObject() .field("type", event.getType().name()) @@ -335,4 +379,28 @@ public class EventService extends AbstractService { if (nodeKeyPair == null) return null; return CryptoUtils.encodeBase58(nodeKeyPair.getPubKey()); } + + /** + * Notify a user + */ + private void doNotifyUser(String recipient, UserEvent event) { + + String email = getEmailByPk(recipient); + Locale locale = I18n.getDefaultLocale(); // TODO get locale + + // Add new event to index + indexEvent(recipient, locale, event); + + // Send email to user + if (StringUtils.isNotBlank(email)) { + String subjectPrefix = pluginSettings.getMailSubjectPrefix(); + sendEmail(email, + I18n.l(locale, "duniter4j.event.subject."+event.getType().name(), subjectPrefix), + event.getLocalizedMessage(locale)); + } + + for (UserEventListener listener: LISTENERS.values()) { + listener.onEvent(event); + } + } } diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/websocket/WebsocketUserEventEndPoint.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/websocket/WebsocketUserEventEndPoint.java new file mode 100644 index 0000000000000000000000000000000000000000..7591f42997c4c387f07585d68784edb9348a145f --- /dev/null +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/websocket/WebsocketUserEventEndPoint.java @@ -0,0 +1,98 @@ +package org.duniter.elasticsearch.user.websocket; + +/* + * #%L + * Duniter4j :: ElasticSearch Plugin + * %% + * Copyright (C) 2014 - 2016 EIS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +/* + Copyright 2015 ForgeRock AS + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +//@ServerEndpoint(value = "/event/user/{pubkey}") +public class WebsocketUserEventEndPoint /*implements UserEventListener*/ { + + /* private static final String PATH_PARAM_PUBKEY = "pubkey"; + private final static Pattern PUBKEY_PATTERN = Pattern.compile(Constants.Regex.PUBKEY); + + private final ESLogger log = Loggers.getLogger("duniter.ws.user.event"); + private Session session; + private String pubkey; + + + @OnOpen + public void onOpen(Session session) { + this.session = session; + this.pubkey = session.getPathParameters() != null ? session.getPathParameters().get(PATH_PARAM_PUBKEY) : null; + + if (StringUtils.isBlank(pubkey) || !PUBKEY_PATTERN.matcher(pubkey).matches()) { + try { + this.session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "Invalid pubkey")); + } catch (IOException e) { + // silent + } + return; + } + + log.info("User [%s] connecting with id [%s]", session.getId()); + UserEventService.registerListener(this); + } + + @Override + public void onEvent(UserEvent event) { + session.getAsyncRemote().sendText("{\"type\":\""+event.getType().name()+"\",\"message\":\"" + event.getMessage() + "\"}"); + } + + @Override + public String getId() { + return session == null ? null : session.getId(); + } + + @OnMessage + public void onMessage(String message) { + log.info("Received message: "+message); + } + + @OnClose + public void onClose(CloseReason reason) { + log.info("Closing websocket: "+reason); + UserEventService.unregisterListener(this); + this.session = null; + } + + @OnError + public void onError(Throwable t) { + log.error("Error on websocket "+(session == null ? null : session.getId()), t); + } +*/ + +} diff --git a/duniter4j-es-user/src/main/misc/cities-fr.geoJson.txt b/duniter4j-es-user/src/main/misc/cities-fr.geoJson.txt new file mode 100644 index 0000000000000000000000000000000000000000..bb4b1bd422f6de2c147bba0ab39ce74aadf01f6c --- /dev/null +++ b/duniter4j-es-user/src/main/misc/cities-fr.geoJson.txt @@ -0,0 +1,8 @@ +Les données Francaise des communes provient de ce fichier : +http://public.opendatasoft.com/explore/dataset/geoflar-communes-2015/export/ + +>> Cliquer sur Export > GeoJSON + + +Ou directement via : + http://public.opendatasoft.com/explore/dataset/geoflar-communes-2015/download/?format=geojson&timezone=Europe/Berlin \ No newline at end of file diff --git a/duniter4j-es-user/src/main/misc/curl_test.sh b/duniter4j-es-user/src/main/misc/curl_test.sh new file mode 100755 index 0000000000000000000000000000000000000000..4f62377a7b20ee747e1a99f1c6ab627e9caa8b35 --- /dev/null +++ b/duniter4j-es-user/src/main/misc/curl_test.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +curl -XPOST "http://data.duniter.fr/market/comment/_search?pretty" -d' +{ + "query": { + "bool":{ + "filter": [ + {"term":{ + "record":"AVbieTIAup9uzWgKipsC" + } + } + ] + } + } +}' + diff --git a/duniter4j-es-user/src/main/misc/index.sh b/duniter4j-es-user/src/main/misc/index.sh new file mode 100755 index 0000000000000000000000000000000000000000..02b66934dd45e338506a7adc1e25500fabac9710 --- /dev/null +++ b/duniter4j-es-user/src/main/misc/index.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +curl -X POST '192.168.0.28:9200/places' -d '{ + "mappings": { + "place": { + "properties": { + "id": {"type": "double"}, + "name": {"type": "string"}, + "type": {"type": "string"}, + "location": {"type": "geo_point"} + } + } + } +}' \ No newline at end of file diff --git a/duniter4j-es-user/src/main/misc/registry-categories-naf2008_liste_n5.ods b/duniter4j-es-user/src/main/misc/registry-categories-naf2008_liste_n5.ods new file mode 100644 index 0000000000000000000000000000000000000000..acc6ea091339d82dacf7533c99e3cf9876599984 Binary files /dev/null and b/duniter4j-es-user/src/main/misc/registry-categories-naf2008_liste_n5.ods differ diff --git a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties new file mode 100644 index 0000000000000000000000000000000000000000..452f86030f449dec66b1856c42eb67c701f51138 --- /dev/null +++ b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties @@ -0,0 +1,4 @@ +duniter4j.event.NODE_STARTED=Node started on cluster Duniter4j ES [%s] +duniter4j.event.subject.ERROR=[%s] Error message +duniter4j.event.subject.INFO=[%s] Information message +duniter4j.event.subject.WARN=[%s] Warning message diff --git a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties new file mode 100644 index 0000000000000000000000000000000000000000..4ae1466a52f86484a597b4f91319ec1262e49b0b --- /dev/null +++ b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties @@ -0,0 +1,4 @@ +duniter4j.event.NODE_STARTED=Noeud démarré sur le cluster Duniter4j ES [%s] +duniter4j.event.subject.ERROR=%s Message d'erreur +duniter4j.event.subject.INFO=%s Message d'information +duniter4j.event.subject.WARN=%s Message d'avertissement diff --git a/duniter4j-es-user/src/main/resources/plugin-security.policy b/duniter4j-es-user/src/main/resources/plugin-security.policy new file mode 100644 index 0000000000000000000000000000000000000000..23b556b1994f188dcf5fd757e8861972b6b63699 --- /dev/null +++ b/duniter4j-es-user/src/main/resources/plugin-security.policy @@ -0,0 +1,5 @@ +grant codeBase "file:${es.path.home}/plugins/duniter4j-es-user/"{ + permission java.io.FilePermission "/etc/ld.so.conf", "read"; + permission java.io.FilePermission "/etc/ld.so.conf.d/*.conf", "read"; + permission java.io.FilePermission "/usr/local/lib/*", "read"; +}; \ No newline at end of file diff --git a/pom.xml b/pom.xml index 493d3164aa66694d6f2d004720a4e72463c8e77c..60b16e97cce2e7bdac3326977e05c2a4c8263088 100644 --- a/pom.xml +++ b/pom.xml @@ -84,6 +84,8 @@ <distribution.snapshotRepository.url>http://nexus.e-is.pro/nexus/content/repositories/duniter4j-snapshots</distribution.snapshotRepository.url> <github.global.server>github</github.global.server> + + <assembly.skip>false</assembly.skip> </properties> <licenses> @@ -99,7 +101,10 @@ <modules> <module>duniter4j-core-shared</module> <module>duniter4j-core-client</module> - <module>duniter4j-elasticsearch</module> + <module>duniter4j-es-core</module> + <module>duniter4j-es-user</module> + <module>duniter4j-es-gchange</module> + <module>duniter4j-es-assembly</module> </modules> <scm>