diff --git a/.gitignore b/.gitignore index 535e5df50329583e8df5cbc48e38e0304813433f..9b56c22f28c83a694e542dd3fbad3d598c27dcae 100644 --- a/.gitignore +++ b/.gitignore @@ -7,10 +7,5 @@ .classpath .settings .idea -duniter4j-cesium/src/cesium/**/*.* target -*/target -duniter4j-elasticsearch/src/main/misc/.index.sh.kate-swp -duniter4j-elasticsearch/src/main/misc/fr-cities.ods -duniter4j-elasticsearch/src/main/misc/geoflar-communes-2015.geojson -duniter4j.iml \ No newline at end of file +*/target \ No newline at end of file diff --git a/duniter4j-core-client/pom.xml b/duniter4j-core-client/pom.xml index 868081fa1f3c5cbbd5e886eb002826dc51f12772..3721938557b5390f38788597e8a9338bb144f763 100644 --- a/duniter4j-core-client/pom.xml +++ b/duniter4j-core-client/pom.xml @@ -7,7 +7,6 @@ <version>0.9.2-SNAPSHOT</version> </parent> - <groupId>org.duniter</groupId> <artifactId>duniter4j-core-client</artifactId> <packaging>jar</packaging> <name>Duniter4j :: Core Client API</name> diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/CurrencyDao.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/CurrencyDao.java index 249975d38111f019f11863ae98102cc4643a5126..cef9f90a3a6138c68f20ac1be141374553940e83 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/CurrencyDao.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/CurrencyDao.java @@ -32,7 +32,7 @@ import java.util.Set; /** * Created by eis on 07/02/15. */ -public interface CurrencyDao extends Bean, EntityDao<Currency> { +public interface CurrencyDao extends Bean, EntityDao<String, Currency> { Currency create(final Currency currency); @@ -42,44 +42,20 @@ public interface CurrencyDao extends Bean, EntityDao<Currency> { List<Currency> getCurrencies(long accountId); - /** - * Return a (cached) currency name, by id - * @param currencyId - * @return - */ - String getCurrencyNameById(long currencyId); - - /** - * Return a currency id, by name - * @param currencyName - * @return - */ - Long getCurrencyIdByName(String currencyName); - - /** - * Return a (cached) list of blockchain ids - * @return - */ - Set<Long> getCurrencyIds(); - - /** - * Return a (cached) number of registered currencies - * @return - */ - int getCurrencyCount(); - /** * Return the value of the last universal dividend * @param currencyId * @return */ - long getLastUD(long currencyId); + long getLastUD(String currencyId); /** * Return a map of UD (key=blockNumber, value=amount) * @return */ - Map<Integer, Long> getAllUD(long currencyId); + Map<Integer, Long> getAllUD(String currencyId); + + void insertUDs(String currencyId, Map<Integer, Long> newUDs); - void insertUDs(Long currencyId, Map<Integer, Long> newUDs); + boolean isExists(String currencyId); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/EntityDao.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/EntityDao.java index e66e8a779d3c04a7578e0a874ee379beb0c71867..49ca4c347ac1d03d9daf037811bc1acdaa7e4fb4 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/EntityDao.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/EntityDao.java @@ -28,13 +28,13 @@ import org.duniter.core.client.model.local.LocalEntity; /** * Created by blavenie on 29/12/15. */ -public interface EntityDao<B extends LocalEntity> extends Bean{ +public interface EntityDao<I, B extends LocalEntity<I>> extends Bean{ B create(B entity); B update(B entity); - B getById(long id); + B getById(I id); void remove(B entity); diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/PeerDao.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/PeerDao.java index 143981ca1dc0419ef71da70f34cd2316a11f4161..3b3d598973342ef288714065297ae953c19b91ce 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/PeerDao.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/PeerDao.java @@ -29,7 +29,9 @@ import java.util.List; /** * Created by blavenie on 29/12/15. */ -public interface PeerDao extends EntityDao<Peer> { +public interface PeerDao extends EntityDao<String, Peer> { - List<Peer> getPeersByCurrencyId(long currencyId); + List<Peer> getPeersByCurrencyId(String currencyId); + + boolean isExists(String currencyId, String peerId); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryCurrencyDaoImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryCurrencyDaoImpl.java index 5d9dffbfa7654f15b080de5b6e7b09ae3e9d7652..20699952287a2bd613304f024d95bccb1f82d39e 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryCurrencyDaoImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryCurrencyDaoImpl.java @@ -32,17 +32,18 @@ import java.util.*; public class MemoryCurrencyDaoImpl implements CurrencyDao { - private Map<Long, org.duniter.core.client.model.local.Currency> currencies = new HashMap<>(); + private Map<String, org.duniter.core.client.model.local.Currency> currencies = new HashMap<>(); - private Map<Long, Map<Integer, Long>> currencyUDsByBlock = new HashMap<>(); + private Map<String, Map<Integer, Long>> currencyUDsByBlock = new HashMap<>(); + + public MemoryCurrencyDaoImpl() { + super(); + } @Override public org.duniter.core.client.model.local.Currency create(final org.duniter.core.client.model.local.Currency entity) { - long id = getMaxId() + 1; - entity.setId(id); - - currencies.put(id, entity); + currencies.put(entity.getId(), entity); return entity; } @@ -66,42 +67,13 @@ public class MemoryCurrencyDaoImpl implements CurrencyDao { } @Override - public org.duniter.core.client.model.local.Currency getById(long currencyId) { - return currencies.get(currencyId); - } - - @Override - public String getCurrencyNameById(long currencyId) { - org.duniter.core.client.model.local.Currency currency = getById(currencyId); - if (currency == null) { - return null; - } - return currency.getCurrencyName(); - } - - @Override - public Long getCurrencyIdByName(String currencyName) { - for(org.duniter.core.client.model.local.Currency currency: currencies.values()) { - if (currencyName.equalsIgnoreCase(currency.getCurrencyName())) { - return currency.getId(); - } - } - return null; - } - - @Override - public Set<Long> getCurrencyIds() { - return currencies.keySet(); + public org.duniter.core.client.model.local.Currency getById(String id) { + return currencies.get(id); } @Override - public int getCurrencyCount() { - return currencies.size(); - } - - @Override - public long getLastUD(long currencyId) { - org.duniter.core.client.model.local.Currency currency = getById(currencyId); + public long getLastUD(String id) { + org.duniter.core.client.model.local.Currency currency = getById(id); if (currency == null) { return -1; } @@ -109,31 +81,23 @@ public class MemoryCurrencyDaoImpl implements CurrencyDao { } @Override - public Map<Integer, Long> getAllUD(long currencyId) { + public Map<Integer, Long> getAllUD(String id) { - return currencyUDsByBlock.get(currencyId); + return currencyUDsByBlock.get(id); } @Override - public void insertUDs(Long currencyId, Map<Integer, Long> newUDs) { - Map<Integer, Long> udsByBlock = currencyUDsByBlock.get(currencyId); + public void insertUDs(String id, Map<Integer, Long> newUDs) { + Map<Integer, Long> udsByBlock = currencyUDsByBlock.get(id); if (udsByBlock == null) { udsByBlock = new HashMap<>(); - currencyUDsByBlock.put(currencyId, udsByBlock); + currencyUDsByBlock.put(id, udsByBlock); } udsByBlock.putAll(newUDs); } - /* -- internal methods -- */ - - protected long getMaxId() { - long currencyId = -1; - for (Long anId : currencies.keySet()) { - if (anId > currencyId) { - currencyId = anId; - } - } - - return currencyId; + @Override + public boolean isExists(String currencyId) { + return currencies.get(currencyId) != null; } } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryPeerDaoImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryPeerDaoImpl.java index d48075bcc2664a95ec54d69d6cd0f34f6ee44e5a..24ce18f6f5c2da4eca552a2e34dbf52256b86c0e 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryPeerDaoImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/dao/mem/MemoryPeerDaoImpl.java @@ -29,20 +29,24 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * Created by blavenie on 29/12/15. */ public class MemoryPeerDaoImpl implements PeerDao { - private Map<Long, Peer> peersByCurrencyId = new HashMap<>(); + private Map<String, Peer> peersByCurrencyId = new HashMap<>(); + + public MemoryPeerDaoImpl() { + super(); + } @Override public Peer create(Peer entity) { - long id = getMaxId() + 1; - entity.setId(id); + entity.setId(entity.computeKey()); - peersByCurrencyId.put(id, entity); + peersByCurrencyId.put(entity.getId(), entity); return entity; } @@ -54,7 +58,7 @@ public class MemoryPeerDaoImpl implements PeerDao { } @Override - public Peer getById(long id) { + public Peer getById(String id) { return peersByCurrencyId.get(id); } @@ -64,29 +68,15 @@ public class MemoryPeerDaoImpl implements PeerDao { } @Override - public List<Peer> getPeersByCurrencyId(long currencyId) { - - List<Peer> result = new ArrayList<>(); - - for(Peer peer: peersByCurrencyId.values()) { - if (peer.getCurrencyId() == currencyId) { - result.add(peer); - } - } - - return result; + public List<Peer> getPeersByCurrencyId(final String currencyId) { + return peersByCurrencyId.values().stream() + .filter(peer -> currencyId.equals(peer.getCurrency())) + .collect(Collectors.toList()); } - /* -- internal methods -- */ - - protected long getMaxId() { - long currencyId = -1; - for (Long anId : peersByCurrencyId.keySet()) { - if (anId > currencyId) { - currencyId = anId; - } - } - - return currencyId; + @Override + public boolean isExists(final String currencyId, final String peerId) { + return peersByCurrencyId.values().stream() + .anyMatch(peer -> currencyId.equals(peer.getCurrency()) && peerId.equals(peer.getId())); } } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/Account.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/Account.java index b4291040fcabbca875af8b3cbdded1cc3eb064fe..0be89fb3e410c2f469badd6abb260c896771468e 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/Account.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/Account.java @@ -28,7 +28,7 @@ import org.duniter.core.client.model.local.LocalEntity; /** * Created by eis on 07/02/15. */ -public class Account implements LocalEntity { +public class Account implements LocalEntity<Long> { private Long id; private String uid; diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/elasticsearch/Currency.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/elasticsearch/Currency.java index 665e91fef579d1eba144202d6ffd78b65e79b0c2..9a23e4f041706b57272f1259f76b4c4f4817aa60 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/elasticsearch/Currency.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/elasticsearch/Currency.java @@ -30,59 +30,11 @@ import java.io.Serializable; /** * Created by eis on 05/02/15. */ -public class Currency implements Serializable { - - public static final String PROPERTY_CURRENCY = "currency"; - - private String currency; - private Integer membersCount; - private String firstBlockSignature; - private Long lastUD; - private BlockchainParameters parameters; +public class Currency extends org.duniter.core.client.model.local.Currency implements Serializable { private String[] tags; private String issuer; - public String getCurrency() { - return currency; - } - - public void setCurrency(String currency) { - this.currency = currency; - } - - public Integer getMembersCount() { - return membersCount; - } - - public void setMembersCount(Integer membersCount) { - this.membersCount = membersCount; - } - - public String getFirstBlockSignature() { - return firstBlockSignature; - } - - public void setFirstBlockSignature(String firstBlockSignature) { - this.firstBlockSignature = firstBlockSignature; - } - - public Long getLastUD() { - return lastUD; - } - - public void setLastUD(Long lastUD) { - this.lastUD = lastUD; - } - - public BlockchainParameters getParameters() { - return parameters; - } - - public void setParameters(BlockchainParameters parameters) { - this.parameters = parameters; - } - public String[] getTags() { return tags; } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Certification.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Certification.java index c1ffc270785de68062d000a81a893a29dc673fe9..ec5ac6b055c256afa5890d59025c4ccac3845ca8 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Certification.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Certification.java @@ -35,7 +35,7 @@ public class Certification { private static final long serialVersionUID = 2204517069552693026L; - private long currencyId; + private String currencyId; private String uid; @@ -68,11 +68,11 @@ public class Certification { super(); } - public long getCurrencyId() { + public String getCurrencyId() { return currencyId; } - public void setCurrencyId(long currencyId) { + public void setCurrencyId(String currencyId) { this.currencyId = currencyId; } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Contact.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Contact.java index b134f54f8420a15e31843892c235bae1e519d1d0..6d95919e9c812ae80c7939b0ef617b7d714ee834 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Contact.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Contact.java @@ -32,7 +32,7 @@ import java.util.List; * A wallet is a user account * Created by eis on 13/01/15. */ -public class Contact implements LocalEntity, Serializable { +public class Contact implements LocalEntity<Long>, Serializable { private long id; private long accountId; @@ -111,12 +111,9 @@ public class Contact implements LocalEntity, Serializable { return false; } - public boolean hasIdentityForCurrency(long currencyId) { - for(Identity identity:identities) { - if (identity.getCurrencyId() != null && identity.getCurrencyId().longValue() == currencyId) { - return true; - } - } - return false; + public boolean hasIdentityForCurrency(String currencyId) { + return identities.stream() + .anyMatch(identity -> identity.getCurrencyId() != null + && currencyId.equals(identity.getCurrencyId())); } } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Currency.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Currency.java index cb0fca9de29e3785504d400ecdfc026c504801c3..04770f58f5b0a07a1315706d2c0d430976bddf8b 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Currency.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Currency.java @@ -24,22 +24,18 @@ package org.duniter.core.client.model.local; import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonIgnore; import org.duniter.core.client.model.Account; import org.duniter.core.client.model.bma.BlockchainParameters; /** * Created by eis on 05/02/15. */ -public class Currency implements LocalEntity, Serializable { +public class Currency implements LocalEntity<String>, Serializable { - private Peer peers[]; - - private Long id; private String currencyName; private Integer membersCount; private String firstBlockSignature; - private Account account; - private Long accountId; private Long lastUD; private BlockchainParameters parameters; @@ -49,17 +45,16 @@ public class Currency implements LocalEntity, Serializable { public Currency(String currencyName, String firstBlockSignature, int membersCount, - Peer[] peers, BlockchainParameters parameters) { this.currencyName = currencyName; this.firstBlockSignature = firstBlockSignature; this.membersCount = membersCount; - this.peers = peers; this.parameters = parameters; } - public Long getId() { - return id; + @JsonIgnore + public String getId() { + return currencyName; } public String getCurrencyName() @@ -75,16 +70,8 @@ public class Currency implements LocalEntity, Serializable { return firstBlockSignature; } - public Peer[] getPeers() { - return peers; - } - - public void setPeers(Peer[] peers) { - this.peers = peers; - } - - public void setId(Long id) { - this.id = id; + public void setId(String id) { + this.currencyName = id; } public void setCurrencyName(String currencyName) { @@ -99,22 +86,6 @@ public class Currency implements LocalEntity, Serializable { this.firstBlockSignature = firstBlockSignature; } - public Account getAccount() { - return account; - } - - public void setAccount(Account account) { - this.account = account; - } - - public Long getAccountId() { - return accountId; - } - - public void setAccountId(Long accountId) { - this.accountId = accountId; - } - public Long getLastUD() { return lastUD; } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Identity.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Identity.java index 8f082680af46b092504317d264cf721607e3cfc6..c860cc4ad469e24773c4e939b23675f5e93d59e5 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Identity.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Identity.java @@ -33,7 +33,7 @@ public class Identity extends BasicIdentity { private Boolean isMember = null; - private Long currencyId; + private String currencyId; /** * The timestamp value of the signature date (a BLOCK_UID) @@ -58,11 +58,11 @@ public class Identity extends BasicIdentity { this.isMember = isMember; } - public Long getCurrencyId() { + public String getCurrencyId() { return currencyId; } - public void setCurrencyId(Long currencyId) { + public void setCurrencyId(String currencyId) { this.currencyId = currencyId; } } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/LocalEntity.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/LocalEntity.java index 770672e2df3312de56703c82f045eb79438d08e3..6216420f0769230662729b420596b9bb71acee4c 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/LocalEntity.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/LocalEntity.java @@ -26,8 +26,8 @@ package org.duniter.core.client.model.local; /** * Created by eis on 07/02/15. */ -public interface LocalEntity { +public interface LocalEntity<I extends Object> { - Long getId(); - void setId(Long id); + I getId(); + void setId(I id); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Movement.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Movement.java index 55822574af94fd113e144c2378fc3a45c996e496..8e2d134e9dd22c02a791d3571ce7a51585423b62 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Movement.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Movement.java @@ -28,7 +28,7 @@ import java.io.Serializable; * A wallet's movement (DU or transfer) * @author */ -public class Movement implements LocalEntity, Serializable { +public class Movement implements LocalEntity<Long>, Serializable { private Long id; private long walletId; diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Peer.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Peer.java index d31a8496e6e44f0ed5b8f70725d58ac5d9cabb90..b6b410006427c698e317bb799f82275342738152 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Peer.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Peer.java @@ -34,7 +34,7 @@ import org.duniter.core.util.http.InetAddressUtils; import java.io.Serializable; import java.util.StringJoiner; -public class Peer implements LocalEntity, Serializable { +public class Peer implements LocalEntity<String>, Serializable { @@ -161,9 +161,7 @@ public class Peer implements LocalEntity, Serializable { } - // Local entity attribute (only used for local DB) - private Long id; - private Long currencyId; + private String id; private String api; private String dns; @@ -232,23 +230,13 @@ public class Peer implements LocalEntity, Serializable { } @JsonIgnore - public Long getId() { + public String getId() { return id; } @JsonIgnore - public void setId(Long id) { - this.id = id; - } - - @JsonIgnore - public Long getCurrencyId() { - return currencyId; - } - - @JsonIgnore - public void setCurrencyId(Long currencyId) { - this.currencyId = currencyId; + public void setId(String id) { + this.id = id; } public String getApi() { diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Wallet.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Wallet.java index 5f31acad8b44b1d61ee2148486b32a1280bee457..675f5d9bbec4cf3c8e71d78754adc2da87a3cd8e 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Wallet.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Wallet.java @@ -34,12 +34,12 @@ import org.duniter.core.util.crypto.KeyPair; * A wallet is a user account * Created by eis on 13/01/15. */ -public class Wallet extends KeyPair implements LocalEntity, Serializable { +public class Wallet extends KeyPair implements LocalEntity<Long>, Serializable { private Long id; - private Long currencyId; private Long accountId; + private String currency; private String name; private Long credit; private Identity identity; @@ -53,10 +53,6 @@ public class Wallet extends KeyPair implements LocalEntity, Serializable { */ private boolean isDirty = false; - // TODO : voir si besoin de les garder ou pas - private String salt; - private String currency; - public Wallet() { super(null, null); this.identity = new Identity(); @@ -96,14 +92,6 @@ public class Wallet extends KeyPair implements LocalEntity, Serializable { return identity.getPubkey(); } - public String getSalt(){ - return salt; - } - - public void setSalt(String salt){ - this.salt = salt; - } - public String getCurrency() { return currency; } @@ -120,12 +108,12 @@ public class Wallet extends KeyPair implements LocalEntity, Serializable { return identity.getTimestamp() != null; } - public Long getCurrencyId() { - return currencyId; + public String getCurrencyId() { + return currency; } - public void setCurrencyId(Long currencyId) { - this.currencyId = currencyId; + public void setCurrencyId(String currencyId) { + this.currency = currencyId; } public Long getAccountId() { @@ -235,7 +223,7 @@ public class Wallet extends KeyPair implements LocalEntity, Serializable { if (o instanceof Wallet) { return ObjectUtils.equals(id, ((Wallet)o).id) && ObjectUtils.equals(getPubKeyHash(), ((Wallet)o).getPubKeyHash()) - && ObjectUtils.equals(currencyId, ((Wallet)o).currencyId); + && ObjectUtils.equals(currency, ((Wallet)o).currency); } return super.equals(o); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BaseRemoteServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BaseRemoteServiceImpl.java index b655d2ee0ebf17e4b29709bf22ceb80d8987427a..b1cfc667e3614188b9425aaca6985d758cf14000 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BaseRemoteServiceImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BaseRemoteServiceImpl.java @@ -22,14 +22,14 @@ package org.duniter.core.client.service.bma; * #L% */ +import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.utils.URIBuilder; import org.duniter.core.beans.InitializingBean; import org.duniter.core.beans.Service; import org.duniter.core.client.model.local.Peer; import org.duniter.core.client.service.HttpService; -import org.duniter.core.client.service.local.PeerService; import org.duniter.core.client.service.ServiceLocator; -import org.apache.http.client.methods.HttpUriRequest; +import org.duniter.core.client.service.local.PeerService; import org.duniter.core.exception.TechnicalException; import org.duniter.core.util.websocket.WebsocketClientEndpoint; @@ -37,7 +37,6 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.util.ServiceConfigurationError; /** * Created by eis on 05/02/15. @@ -63,7 +62,7 @@ public abstract class BaseRemoteServiceImpl implements Service, InitializingBean return httpService.executeRequest(peer, absolutePath, resultClass); } - public <T> T executeRequest(long currencyId, String absolutePath, Class<? extends T> resultClass) { + public <T> T executeRequest(String currencyId, String absolutePath, Class<? extends T> resultClass) { Peer peer = peerService.getActivePeerByCurrencyId(currencyId); return httpService.executeRequest(peer, absolutePath, resultClass); } @@ -72,7 +71,7 @@ public abstract class BaseRemoteServiceImpl implements Service, InitializingBean return httpService.executeRequest(request, resultClass); } - public String getPath(long currencyId, String aPath) { + public String getPath(String currencyId, String aPath) { Peer peer = peerService.getActivePeerByCurrencyId(currencyId); return httpService.getPath(peer, aPath); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BlockchainRemoteService.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BlockchainRemoteService.java index 2231f3f2561fedfda4a6248b23d21254e4a8b521..eaac3b408694040101a197afee66b7505285259a 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BlockchainRemoteService.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BlockchainRemoteService.java @@ -47,7 +47,7 @@ public interface BlockchainRemoteService extends Service { * @param useCache * @return */ - BlockchainParameters getParameters(long currencyId, boolean useCache); + BlockchainParameters getParameters(String currencyId, boolean useCache); /** * get the blockchain parameters (currency parameters) @@ -55,7 +55,7 @@ public interface BlockchainRemoteService extends Service { * @param currencyId * @return */ - BlockchainParameters getParameters(long currencyId); + BlockchainParameters getParameters(String currencyId); /** * get the blockchain parameters (currency parameters) @@ -72,7 +72,7 @@ public interface BlockchainRemoteService extends Service { * @param number * @return */ - BlockchainBlock getBlock(long currencyId, long number) throws BlockNotFoundException; + BlockchainBlock getBlock(String currencyId, long number) throws BlockNotFoundException; /** * Retrieve the dividend of a block, by id (from 0 to current). @@ -82,7 +82,7 @@ public interface BlockchainRemoteService extends Service { * @param number * @return */ - Long getBlockDividend(long currencyId, long number) throws BlockNotFoundException; + Long getBlockDividend(String currencyId, long number) throws BlockNotFoundException; /** * Retrieve a block, by id (from 0 to current) @@ -124,14 +124,14 @@ public interface BlockchainRemoteService extends Service { * * @return */ - BlockchainBlock getCurrentBlock(long currencyId, boolean useCache); + BlockchainBlock getCurrentBlock(String currencyId, boolean useCache); /** * Retrieve the current block * * @return */ - BlockchainBlock getCurrentBlock(long currencyId); + BlockchainBlock getCurrentBlock(String currencyId); /** * Retrieve the current block @@ -157,7 +157,7 @@ public interface BlockchainRemoteService extends Service { * @param currencyId id of currency * @return */ - long getLastUD(long currencyId); + long getLastUD(String currencyId); /** * Retrieve the last emitted UD, from a peer (or ud0 if not UD emitted yet) @@ -191,12 +191,12 @@ public interface BlockchainRemoteService extends Service { * * @param identity */ - void loadMembership(long currencyId, Identity identity, boolean checkLookupForNonMember); + void loadMembership(String currencyId, Identity identity, boolean checkLookupForNonMember); - BlockchainMemberships getMembershipByUid(long currencyId, String uid); + BlockchainMemberships getMembershipByUid(String currencyId, String uid); - BlockchainMemberships getMembershipByPublicKey(long currencyId, String pubkey); + BlockchainMemberships getMembershipByPublicKey(String currencyId, String pubkey); /** * Request to integrate the wot @@ -205,7 +205,7 @@ public interface BlockchainRemoteService extends Service { void requestMembership(Peer peer, String currency, byte[] pubKey, byte[] secKey, String uid, String membershipBlockUid, String selfBlockUid); - BlockchainMemberships getMembershipByPubkeyOrUid(long currencyId, String uidOrPubkey); + BlockchainMemberships getMembershipByPubkeyOrUid(String currencyId, String uidOrPubkey); BlockchainMemberships getMembershipByPubkeyOrUid(Peer peer, String uidOrPubkey); @@ -220,7 +220,7 @@ public interface BlockchainRemoteService extends Service { * @param startOffset * @return */ - Map<Integer, Long> getUDs(long currencyId, long startOffset); + Map<Integer, Long> getUDs(String currencyId, long startOffset); /** * Listening new block event @@ -229,7 +229,7 @@ public interface BlockchainRemoteService extends Service { * @param autoReconnect * @return */ - WebsocketClientEndpoint addBlockListener(long currencyId, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect); + WebsocketClientEndpoint addBlockListener(String currencyId, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect); WebsocketClientEndpoint addBlockListener(Peer peer, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect); diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BlockchainRemoteServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BlockchainRemoteServiceImpl.java index 22c7f92e4ac677b1a4fbd763a4480cf44bd87600..c577dbc8b5bc982b9c135f1ccdb8fddcbd6079d3 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BlockchainRemoteServiceImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/BlockchainRemoteServiceImpl.java @@ -22,15 +22,12 @@ package org.duniter.core.client.service.bma; * #L% */ -import com.fasterxml.jackson.databind.ObjectMapper; -import org.duniter.core.util.Preconditions; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.message.BasicNameValuePair; import org.duniter.core.client.config.Configuration; import org.duniter.core.client.model.bma.*; -import org.duniter.core.util.json.JsonArrayParser; import org.duniter.core.client.model.local.Identity; import org.duniter.core.client.model.local.Peer; import org.duniter.core.client.model.local.Wallet; @@ -38,10 +35,12 @@ import org.duniter.core.client.service.ServiceLocator; import org.duniter.core.client.service.exception.*; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; +import org.duniter.core.util.Preconditions; import org.duniter.core.util.StringUtils; import org.duniter.core.util.cache.Cache; import org.duniter.core.util.cache.SimpleCache; import org.duniter.core.util.crypto.CryptoUtils; +import org.duniter.core.util.json.JsonArrayParser; import org.duniter.core.util.websocket.WebsocketClientEndpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,7 +48,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; -import java.net.URISyntaxException; import java.util.*; public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implements BlockchainRemoteService { @@ -79,20 +77,14 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement public static final String URL_WS_BLOCK = "/ws/block"; - public static final String URL_WS_PEER = "/ws/peer"; - - private ObjectMapper objectMapper; - - private NetworkRemoteService networkRemoteService; - private Configuration config; // Cache need for wallet refresh : iteration on wallet should not // execute a download of the current block - private Cache<Long, BlockchainBlock> mCurrentBlockCache; + private Cache<String, BlockchainBlock> mCurrentBlockCache; // Cache on blockchain parameters - private Cache<Long, BlockchainParameters> mParametersCache; + private Cache<String, BlockchainParameters> mParametersCache; private Map<URI, WebsocketClientEndpoint> wsEndPoints = new HashMap<>(); @@ -103,7 +95,6 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement @Override public void afterPropertiesSet() { super.afterPropertiesSet(); - networkRemoteService = ServiceLocator.instance().getNetworkRemoteService(); config = Configuration.instance(); // Initialize caches @@ -123,7 +114,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement } @Override - public BlockchainParameters getParameters(long currencyId, boolean useCache) { + public BlockchainParameters getParameters(String currencyId, boolean useCache) { if (!useCache) { return getParameters(currencyId); } else { @@ -132,7 +123,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement } @Override - public BlockchainParameters getParameters(long currencyId) { + public BlockchainParameters getParameters(String currencyId) { // get blockchain parameter BlockchainParameters result = executeRequest(currencyId, URL_PARAMETERS, BlockchainParameters.class); return result; @@ -146,7 +137,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement } @Override - public BlockchainBlock getBlock(long currencyId, long number) throws BlockNotFoundException { + public BlockchainBlock getBlock(String currencyId, long number) throws BlockNotFoundException { String path = String.format(URL_BLOCK, number); try { return executeRequest(currencyId, path, BlockchainBlock.class); @@ -157,7 +148,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement } @Override - public Long getBlockDividend(long currencyId, long number) throws BlockNotFoundException { + public Long getBlockDividend(String currencyId, long number) throws BlockNotFoundException { String path = String.format(URL_BLOCK, number); try { String json = executeRequest(currencyId, path, String.class); @@ -221,7 +212,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement * * @return */ - public BlockchainBlock getCurrentBlock(long currencyId, boolean useCache) { + public BlockchainBlock getCurrentBlock(String currencyId, boolean useCache) { if (!useCache) { return getCurrentBlock(currencyId); } else { @@ -230,7 +221,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement } @Override - public BlockchainBlock getCurrentBlock(long currencyId) { + public BlockchainBlock getCurrentBlock(String currencyId) { // get blockchain parameter BlockchainBlock result = executeRequest(currencyId, URL_BLOCK_CURRENT, BlockchainBlock.class); return result; @@ -264,7 +255,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement } @Override - public long getLastUD(long currencyId) { + public long getLastUD(String currencyId) { // get block number with UD String blocksWithUdResponse = executeRequest(currencyId, URL_BLOCK_WITH_UD, String.class); Integer blockNumber = getLastBlockNumberFromJson(blocksWithUdResponse); @@ -374,12 +365,12 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement * * @param identity */ - public void loadMembership(long currencyId, Identity identity, boolean checkLookupForNonMember) { + public void loadMembership(String currencyId, Identity identity, boolean checkLookupForNonMember) { loadMembership(currencyId, null, identity, checkLookupForNonMember); } - public BlockchainMemberships getMembershipByUid(long currencyId, String uid) { + public BlockchainMemberships getMembershipByUid(String currencyId, String uid) { Preconditions.checkArgument(StringUtils.isNotBlank(uid)); BlockchainMemberships result = getMembershipByPubkeyOrUid(currencyId, uid); @@ -389,7 +380,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement return result; } - public BlockchainMemberships getMembershipByPublicKey(long currencyId, String pubkey) { + public BlockchainMemberships getMembershipByPublicKey(String currencyId, String pubkey) { Preconditions.checkArgument(StringUtils.isNotBlank(pubkey)); BlockchainMemberships result = getMembershipByPubkeyOrUid(currencyId, pubkey); @@ -460,7 +451,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement executeRequest(httpPost, String.class); } - public BlockchainMemberships getMembershipByPubkeyOrUid(long currencyId, String uidOrPubkey) { + public BlockchainMemberships getMembershipByPubkeyOrUid(String currencyId, String uidOrPubkey) { String path = String.format(URL_MEMBERSHIP_SEARCH, uidOrPubkey); // search blockchain membership @@ -514,7 +505,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement * @param startOffset * @return */ - public Map<Integer, Long> getUDs(long currencyId, long startOffset) { + public Map<Integer, Long> getUDs(String currencyId, long startOffset) { log.debug(String.format("Getting block's UD from block [%s]", startOffset)); int[] blockNumbersWithUD = getBlocksWithUD(currencyId); @@ -566,7 +557,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement } @Override - public WebsocketClientEndpoint addBlockListener(long currencyId, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect) { + public WebsocketClientEndpoint addBlockListener(String currencyId, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect) { return addBlockListener(peerService.getActivePeerByCurrencyId(currencyId), listener, autoReconnect); } @@ -593,23 +584,23 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement protected void initCaches() { int cacheTimeInMillis = config.getNetworkCacheTimeInMillis(); - mCurrentBlockCache = new SimpleCache<Long, BlockchainBlock>(cacheTimeInMillis) { + mCurrentBlockCache = new SimpleCache<String, BlockchainBlock>(cacheTimeInMillis) { @Override - public BlockchainBlock load(Long currencyId) { + public BlockchainBlock load(String currencyId) { return getCurrentBlock(currencyId); } }; - mParametersCache = new SimpleCache<Long, BlockchainParameters>(/*eternal cache*/) { + mParametersCache = new SimpleCache<String, BlockchainParameters>(/*eternal cache*/) { @Override - public BlockchainParameters load(Long currencyId) { + public BlockchainParameters load(String currencyId) { return getParameters(currencyId); } }; } - protected void loadMembership(Long currencyId, Peer peer, Identity identity, boolean checkLookupForNonMember) { + protected void loadMembership(String currencyId, Peer peer, Identity identity, boolean checkLookupForNonMember) { Preconditions.checkNotNull(identity); Preconditions.checkArgument(StringUtils.isNotBlank(identity.getUid())); Preconditions.checkArgument(StringUtils.isNotBlank(identity.getPubkey())); @@ -655,7 +646,7 @@ public class BlockchainRemoteServiceImpl extends BaseRemoteServiceImpl implement } - private int[] getBlocksWithUD(long currencyId) { + private int[] getBlocksWithUD(String currencyId) { log.debug("Getting blocks with UD"); String json = executeRequest(currencyId, URL_BLOCK_WITH_UD, String.class); diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteService.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteService.java index b8e1a0fc784258a9ee22d061019043fc4723cce2..dc14cc9cc2750d2ecf1da97af4bd433db1714f30 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteService.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteService.java @@ -46,7 +46,7 @@ public interface NetworkRemoteService extends Service { List<Peer> findPeers(Peer peer, String status, EndpointApi endpointApi, Integer currentBlockNumber, String currentBlockHash); - WebsocketClientEndpoint addPeerListener(long currencyId, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect); + WebsocketClientEndpoint addPeerListener(String currencyId, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect); WebsocketClientEndpoint addPeerListener(Peer peer, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteServiceImpl.java index 5d8b1aef38e1b490ee3209656c8353f6e78f1a3b..7cfafe68ee2c9e99390094c550fec9b06e579366 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteServiceImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteServiceImpl.java @@ -153,7 +153,7 @@ public class NetworkRemoteServiceImpl extends BaseRemoteServiceImpl implements N @Override - public WebsocketClientEndpoint addPeerListener(long currencyId, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect) { + public WebsocketClientEndpoint addPeerListener(String currencyId, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect) { Peer peer = peerService.getActivePeerByCurrencyId(currencyId); return addPeerListener(peer, listener, autoReconnect); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/TransactionRemoteService.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/TransactionRemoteService.java index 882f4c542310df2c93a8ebf1f863ccd413d6d97f..0e5ab18a7569748dcce3f1229db76b93685d905f 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/TransactionRemoteService.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/TransactionRemoteService.java @@ -58,17 +58,17 @@ public interface TransactionRemoteService extends Service { String transfer(Wallet wallet, String destPubKey, long amount, String comment) throws InsufficientCreditException; - TxSource getSources(long currencyId, String pubKey); + TxSource getSources(String currencyId, String pubKey); TxSource getSources(Peer peer, String pubKey); - long getCreditOrZero(long currencyId, String pubKey); + long getCreditOrZero(String currencyId, String pubKey); - Long getCredit(long currencyId, String pubKey); + Long getCredit(String currencyId, String pubKey); Long getCredit(Peer peer, String pubKey); long computeCredit(TxSource.Source[] sources); - TxHistory getTxHistory(long currencyId, String pubKey, long fromBlockNumber, long toBlockNumber); + TxHistory getTxHistory(String currencyId, String pubKey, long fromBlockNumber, long toBlockNumber); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/TransactionRemoteServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/TransactionRemoteServiceImpl.java index 4c8e869fec2bf9770e9f83cbd042bbe55e9de857..47c91a51dab46017ec0d4aa9f6e00bc25e35437e 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/TransactionRemoteServiceImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/TransactionRemoteServiceImpl.java @@ -136,7 +136,7 @@ public class TransactionRemoteServiceImpl extends BaseRemoteServiceImpl implemen } - public TxSource getSources(long currencyId, String pubKey) { + public TxSource getSources(String currencyId, String pubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Get sources by pubKey: %s", pubKey)); } @@ -160,7 +160,7 @@ public class TransactionRemoteServiceImpl extends BaseRemoteServiceImpl implemen return result; } - public long getCreditOrZero(long currencyId, String pubKey) { + public long getCreditOrZero(String currencyId, String pubKey) { Long credit = getCredit(currencyId, pubKey); if (credit == null) { @@ -169,7 +169,7 @@ public class TransactionRemoteServiceImpl extends BaseRemoteServiceImpl implemen return credit.longValue(); } - public Long getCredit(long currencyId, String pubKey) { + public Long getCredit(String currencyId, String pubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Get credit by pubKey [%s] for currency [id=%s]", pubKey, currencyId)); } @@ -216,7 +216,7 @@ public class TransactionRemoteServiceImpl extends BaseRemoteServiceImpl implemen return credit; } - public TxHistory getTxHistory(long currencyId, String pubKey, long fromBlockNumber, long toBlockNumber) { + public TxHistory getTxHistory(String currencyId, String pubKey, long fromBlockNumber, long toBlockNumber) { Preconditions.checkNotNull(pubKey); Preconditions.checkArgument(fromBlockNumber >= 0); Preconditions.checkArgument(fromBlockNumber <= toBlockNumber); diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/WotRemoteService.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/WotRemoteService.java index 0b8aca4c0fb407f1f8cca179b9503fab95618ff2..f89c399f433c277eed28ec2a95cac30bb5587f06 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/WotRemoteService.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/WotRemoteService.java @@ -37,39 +37,39 @@ import java.util.Set; public interface WotRemoteService extends Service { - List<Identity> findIdentities(Set<Long> currenciesIds, String uidOrPubKey); + List<Identity> findIdentities(Set<String> currenciesIds, String uidOrPubKey); - WotLookup.Uid find(long currencyId, String uidOrPubKey); + WotLookup.Uid find(String currencyId, String uidOrPubKey); - void getRequirments(long currencyId, String pubKey); + void getRequirments(String currencyId, String pubKey); - WotLookup.Uid findByUid(long currencyId, String uid); + WotLookup.Uid findByUid(String currencyId, String uid); - WotLookup.Uid findByUidAndPublicKey(long currencyId, String uid, String pubKey); + WotLookup.Uid findByUidAndPublicKey(String currencyId, String uid, String pubKey); WotLookup.Uid findByUidAndPublicKey(Peer peer, String uid, String pubKey); - Identity getIdentity(long currencyId, String uid, String pubKey); + Identity getIdentity(String currencyId, String uid, String pubKey); - Identity getIdentity(long currencyId, String pubKey); + Identity getIdentity(String currencyId, String pubKey); Identity getIdentity(Peer peer, String uid, String pubKey); - Collection<Certification> getCertifications(long currencyId, String uid, String pubkey, boolean isMember); + Collection<Certification> getCertifications(String currencyId, String uid, String pubkey, boolean isMember); - WotCertification getCertifiedBy(long currencyId, String uid); + WotCertification getCertifiedBy(String currencyId, String uid); - int countValidCertifiers(long currencyId, String pubkey); + int countValidCertifiers(String currencyId, String pubkey); - WotCertification getCertifiersOf(long currencyId, String uid); + WotCertification getCertifiersOf(String currencyId, String uid); String getSignedIdentity(String currency, byte[] pubKey, byte[] secKey, String uid, String blockUid); - Map<String, String> getMembersUids(long currencyId); + Map<String, String> getMembersUids(String currencyId); Map<String, String> getMembersUids(Peer peer); - void sendIdentity(long currencyId, byte[] pubKey, byte[] secKey, String uid, String blockUid); + void sendIdentity(String currencyId, byte[] pubKey, byte[] secKey, String uid, String blockUid); void sendIdentity(Peer peer, String currency, byte[] pubKey, byte[] secKey, String uid, String blockUid); @@ -79,7 +79,7 @@ public interface WotRemoteService extends Service { String sendCertification(Wallet wallet, Identity identity); - String sendCertification(long currencyId, + String sendCertification(String currencyId, byte[] pubKey, byte[] secKey, String uid, String timestamp, String userUid, String userPubKeyHash, diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/WotRemoteServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/WotRemoteServiceImpl.java index 863e6e07aa8de33b579d75095e158a52cee4c1b7..f2869f899cf87c58ddee69ee2d8746fc8f6bfbda 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/WotRemoteServiceImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/WotRemoteServiceImpl.java @@ -87,12 +87,12 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe currencyService = ServiceLocator.instance().getCurrencyService(); } - public List<Identity> findIdentities(Set<Long> currenciesIds, String uidOrPubKey) { + public List<Identity> findIdentities(Set<String> currenciesIds, String uidOrPubKey) { List<Identity> result = new ArrayList<Identity>(); String path = String.format(URL_LOOKUP, uidOrPubKey); - for (Long currencyId: currenciesIds) { + for (String currencyId: currenciesIds) { WotLookup lookupResult = executeRequest(currencyId, path, WotLookup.class); @@ -102,7 +102,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe return result; } - public WotLookup.Uid find(long currencyId, String uidOrPubKey) { + public WotLookup.Uid find(String currencyId, String uidOrPubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Try to find user by looking up on [%s]", uidOrPubKey)); } @@ -122,7 +122,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe } - public Map<String, String> getMembersUids(long currencyId) { + public Map<String, String> getMembersUids(String currencyId) { // get /wot/members JsonNode json = executeRequest(currencyId, URL_MEMBERS, JsonNode.class); @@ -151,7 +151,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe } - public void getRequirments(long currencyId, String pubKey) { + public void getRequirments(String currencyId, String pubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Try to find user requirements on [%s]", pubKey)); } @@ -162,7 +162,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe } - public WotLookup.Uid findByUid(long currencyId, String uid) { + public WotLookup.Uid findByUid(String currencyId, String uid) { if (log.isDebugEnabled()) { log.debug(String.format("Try to find user info by uid: %s", uid)); } @@ -180,7 +180,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe return uniqueResult; } - public WotLookup.Uid findByUidAndPublicKey(long currencyId, String uid, String pubKey) { + public WotLookup.Uid findByUidAndPublicKey(String currencyId, String uid, String pubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Try to find user info by uid [%s] and pubKey [%s]", uid, pubKey)); } @@ -216,7 +216,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe return uniqueResult; } - public Identity getIdentity(long currencyId, String uid, String pubKey) { + public Identity getIdentity(String currencyId, String uid, String pubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Get identity by uid [%s] and pubKey [%s]", uid, pubKey)); } @@ -228,7 +228,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe return toIdentity(lookupUid); } - public Identity getIdentity(long currencyId, String pubKey) { + public Identity getIdentity(String currencyId, String pubKey) { // Log.d(TAG, String.format("Get identity by uid [%s] and pubKey [%s]", uid, pubKey)); WotLookup.Uid lookupUid = find(currencyId, pubKey); @@ -253,7 +253,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe return toIdentity(lookupUid); } - public Collection<Certification> getCertifications(long currencyId, String uid, String pubkey, boolean isMember) { + public Collection<Certification> getCertifications(String currencyId, String uid, String pubkey, boolean isMember) { Preconditions.checkNotNull(uid); Preconditions.checkNotNull(pubkey); @@ -266,7 +266,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe } - public WotCertification getCertifiedBy(long currencyId, String uid) { + public WotCertification getCertifiedBy(String currencyId, String uid) { if (log.isDebugEnabled()) { log.debug(String.format("Try to get certifications done by uid: %s", uid)); } @@ -279,7 +279,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe } - public int countValidCertifiers(long currencyId, String pubkey) { + public int countValidCertifiers(String currencyId, String pubkey) { if (log.isDebugEnabled()) { log.debug(String.format("Try to count valid certifications done by pubkey: %s", pubkey)); } @@ -302,7 +302,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe } - public WotCertification getCertifiersOf(long currencyId, String uid) { + public WotCertification getCertifiersOf(String currencyId, String uid) { if (log.isDebugEnabled()) { log.debug(String.format("Try to get certifications done to uid: %s", uid)); } @@ -315,7 +315,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe } - public void sendIdentity(long currencyId, byte[] pubKey, byte[] secKey, String userId, String blockUid) { + public void sendIdentity(String currencyId, byte[] pubKey, byte[] secKey, String userId, String blockUid) { // http post /wot/add HttpPost httpPost = new HttpPost(getPath(currencyId, URL_ADD)); @@ -373,7 +373,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe identity.getSignature()); } - public String sendCertification(long currencyId, + public String sendCertification(String currencyId, byte[] pubKey, byte[] secKey, String uid, String timestamp, String userUid, String userPubKeyHash, @@ -419,11 +419,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe return selfResult; } - public void addAllIdentities(List<Identity> result, WotLookup lookupResults, Long currencyId) { - String currencyName = null; - if (currencyId != null) { - currencyName = ServiceLocator.instance().getCurrencyService().getCurrencyNameById(currencyId); - } + public void addAllIdentities(List<Identity> result, WotLookup lookupResults, String currencyName) { for (WotLookup.Result lookupResult: lookupResults.getResults()) { String pubKey = lookupResult.getPubkey(); @@ -437,8 +433,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe // Fill currency id and name // TODO - //target.setCurrencyId(currencyId); - //target.setCurrency(currencyName); + target.setCurrencyId(currencyName); result.add(target); } @@ -476,7 +471,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe /* -- Internal methods -- */ - protected Collection<Certification> getCertificationsByPubkeyForMember(long currencyId, String pubkey, boolean onlyCertifiersOf) { + protected Collection<Certification> getCertificationsByPubkeyForMember(String currencyId, String pubkey, boolean onlyCertifiersOf) { BlockchainParameters bcParameter = bcService.getParameters(currencyId, true); BlockchainBlock currentBlock = bcService.getCurrentBlock(currencyId, true); @@ -557,10 +552,10 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe return result; } - protected Collection<Certification> getCertificationsByPubkeyForNonMember(long currencyId, final String uid, final String pubkey) { + protected Collection<Certification> getCertificationsByPubkeyForNonMember(String currencyId, final String uid, final String pubkey) { // Ordered list, by uid/pubkey/cert time - Collection<Certification> result = new TreeSet<Certification>(ModelUtils.newWotCertificationComparatorByUid()); + Collection<Certification> result = new TreeSet<>(ModelUtils.newWotCertificationComparatorByUid()); if (log.isDebugEnabled()) { log.debug(String.format("Get non member WOT, by uid [%s] and pubKey [%s]", uid, pubkey)); @@ -574,7 +569,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe WotLookup.Uid lookupUId = getUidByUidAndPublicKey(lookupResults, uid, pubkey); // Read certifiers, if any - Map<String, Certification> certifierByPubkeys = new HashMap<String, Certification>(); + Map<String, Certification> certifierByPubkeys = new HashMap<>(); if (lookupUId != null && lookupUId.getOthers() != null) { for(WotLookup.OtherSignature lookupSignature: lookupUId.getOthers()) { Collection<Certification> certifiers = toCertifierCertifications(lookupSignature, currencyId); @@ -749,7 +744,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe return null; } - private Certification toCertification(final WotCertification.Certification source, final long currencyId) { + private Certification toCertification(final WotCertification.Certification source, final String currencyId) { Certification target = new Certification(); target.setPubkey(source.getPubkey()); target.setUid(source.getUid()); @@ -759,7 +754,7 @@ public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRe return target; } - private Collection<Certification> toCertifierCertifications(final WotLookup.OtherSignature source, final long currencyId) { + private Collection<Certification> toCertifierCertifications(final WotLookup.OtherSignature source, final String currencyId) { List<Certification> result = new ArrayList<Certification>(); // If only one uid if (source.getUids().length == 1) { diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/CurrencyService.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/CurrencyService.java index f9cbe365dfb7a0cd06de41f09eae007717013fc9..45b2e7916a66befe2b2640d4ec964ec19db1b2f8 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/CurrencyService.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/CurrencyService.java @@ -38,27 +38,27 @@ public interface CurrencyService extends Service { List<Currency> getCurrencies(long accountId); - Currency getCurrencyById(long currencyId); + Currency getCurrencyById(String currencyId); /** * Return a (cached) currency name, by id * @param currencyId * @return */ - String getCurrencyNameById(long currencyId); + String getCurrencyNameById(String currencyId); /** * Return a currency id, by name * @param currencyName * @return */ - Long getCurrencyIdByName(String currencyName); + String getCurrencyIdByName(String currencyName); /** * Return a (cached) list of currency ids * @return */ - Set<Long> getCurrencyIds(); + Set<String> getCurrencyIds(); /** * Return a (cached) number of registered currencies @@ -77,17 +77,17 @@ public interface CurrencyService extends Service { * @param currencyId * @return */ - long getLastUD(long currencyId); + long getLastUD(String currencyId); /** * Return a map of UD (key=blockNumber, value=amount) * @return */ - Map<Integer, Long> refreshAndGetUD(long currencyId, long lastSyncBlockNumber); + Map<Integer, Long> refreshAndGetUD(String currencyId, long lastSyncBlockNumber); /** * Return a map of UD (key=blockNumber, value=amount) * @return */ - Map<Integer, Long> getAllUD(long currencyId); + Map<Integer, Long> getAllUD(String currencyId); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/CurrencyServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/CurrencyServiceImpl.java index 91b9be1f13544a194b66bc144936cdfee35c57e6..50fcb9b14daf3c10cd75f02516a400a06fc54f44 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/CurrencyServiceImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/CurrencyServiceImpl.java @@ -47,8 +47,8 @@ public class CurrencyServiceImpl implements CurrencyService, InitializingBean { private static final long UD_CACHE_TIME_MILLIS = 5 * 60 * 1000; // = 5 min - private Cache<Long, Currency> mCurrencyCache; - private Cache<Long, Long> mUDCache; + private Cache<String, Currency> mCurrencyCache; + private Cache<String, Long> mUDCache; private BlockchainRemoteService blockchainRemoteService; private CurrencyDao currencyDao; @@ -84,9 +84,6 @@ public class CurrencyServiceImpl implements CurrencyService, InitializingBean { ObjectUtils.checkNotNull(currency.getLastUD()); ObjectUtils.checkArgument(currency.getLastUD().longValue() > 0); - ObjectUtils.checkArgument((currency.getAccount() != null && currency.getAccount().getId() != null) - || currency.getAccountId() != null, "One of 'currency.account.id' or 'currency.accountId' is mandatory."); - Currency result; // Create @@ -114,7 +111,7 @@ public class CurrencyServiceImpl implements CurrencyService, InitializingBean { } - public Currency getCurrencyById(long currencyId) { + public Currency getCurrencyById(String currencyId) { return mCurrencyCache.get(currencyId); } @@ -123,7 +120,7 @@ public class CurrencyServiceImpl implements CurrencyService, InitializingBean { * @param currencyId * @return */ - public String getCurrencyNameById(long currencyId) { + public String getCurrencyNameById(String currencyId) { Currency currency = mCurrencyCache.getIfPresent(currencyId); if (currency == null) { return null; @@ -136,11 +133,11 @@ public class CurrencyServiceImpl implements CurrencyService, InitializingBean { * @param currencyName * @return */ - public Long getCurrencyIdByName(String currencyName) { + public String getCurrencyIdByName(String currencyName) { Preconditions.checkArgument(StringUtils.isNotBlank(currencyName)); // Search from currencies - for (Map.Entry<Long, Currency> entry : mCurrencyCache.entrySet()) { + for (Map.Entry<String, Currency> entry : mCurrencyCache.entrySet()) { Currency currency = entry.getValue(); if (ObjectUtils.equals(currencyName, currency.getCurrencyName())) { return entry.getKey(); @@ -153,7 +150,7 @@ public class CurrencyServiceImpl implements CurrencyService, InitializingBean { * Return a (cached) list of currency ids * @return */ - public Set<Long> getCurrencyIds() { + public Set<String> getCurrencyIds() { return mCurrencyCache.keySet(); } @@ -176,9 +173,9 @@ public class CurrencyServiceImpl implements CurrencyService, InitializingBean { List<Currency> currencies = getCurrencies(accountId); if (mCurrencyCache == null) { - mCurrencyCache = new SimpleCache<Long, Currency>() { + mCurrencyCache = new SimpleCache<String, Currency>() { @Override - public Currency load(Long currencyId) { + public Currency load(String currencyId) { return currencyDao.getById(currencyId); } }; @@ -192,9 +189,9 @@ public class CurrencyServiceImpl implements CurrencyService, InitializingBean { // Create the UD cache if (mUDCache == null) { - mUDCache = new SimpleCache<Long, Long>(UD_CACHE_TIME_MILLIS) { + mUDCache = new SimpleCache<String, Long>(UD_CACHE_TIME_MILLIS) { @Override - public Long load(final Long currencyId) { + public Long load(final String currencyId) { // Retrieve the last UD from the blockchain final Long lastUD = blockchainRemoteService.getLastUD(currencyId); @@ -217,7 +214,7 @@ public class CurrencyServiceImpl implements CurrencyService, InitializingBean { * @param currencyId * @return */ - public long getLastUD(long currencyId) { + public long getLastUD(String currencyId) { return mUDCache.get(currencyId); } @@ -225,7 +222,7 @@ public class CurrencyServiceImpl implements CurrencyService, InitializingBean { * Return a map of UD (key=blockNumber, value=amount) * @return */ - public Map<Integer, Long> refreshAndGetUD(long currencyId, long lastSyncBlockNumber) { + public Map<Integer, Long> refreshAndGetUD(String currencyId, long lastSyncBlockNumber) { // Retrieve new UDs from blockchain Map<Integer, Long> newUDs = blockchainRemoteService.getUDs(currencyId, lastSyncBlockNumber + 1); @@ -242,7 +239,7 @@ public class CurrencyServiceImpl implements CurrencyService, InitializingBean { * Return a map of UD (key=blockNumber, value=amount) * @return */ - public Map<Integer, Long> getAllUD(long currencyId) { + public Map<Integer, Long> getAllUD(String currencyId) { return currencyDao.getAllUD(currencyId); } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java index 7aacd7313a7f0c7ec208c0aea3b1ef5af040c231..648f83b87d55d10e40b5aa5bec18a114acea6154 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java @@ -48,8 +48,10 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.*; import java.util.concurrent.*; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.stream.Collectors; /** @@ -277,101 +279,140 @@ public class NetworkServiceImpl extends BaseRemoteServiceImpl implements Network final Comparator<Peer> peerComparator = peerComparator(sort); final ExecutorService pool = (executor != null) ? executor : ForkJoinPool.commonPool(); - // Manage new block event - blockchainRemoteService.addBlockListener(mainPeer, json -> { + /*Runnable initCacheRunnable = () -> { if (threadLock.isLocked()) return; synchronized (threadLock) { threadLock.lock(); } try { - BlockchainBlock block = readValue(json, BlockchainBlock.class); - String blockBuid = buid(block); - boolean isNewBlock = (blockBuid != null && !knownBlocks.contains(blockBuid)); + // TODO : load all peers from DAO, then fill list ? + } + catch(Exception e) { + log.error("Error while loading all peers: " + e.getMessage(), e); + } + finally { + synchronized (threadLock) { + threadLock.unlock(); + } + } + };*/ - // If new block + wait 5s for network propagation - if (isNewBlock && waitSafe(5000)) { - List<Peer> updatedPeers = getPeers(mainPeer, filter, sort); - - knownPeers.clear(); - updatedPeers.stream().forEach(peer -> { - String buid = buid(peer.getStats()); - if (!knownBlocks.contains(buid)) { - knownBlocks.add(buid); - } - knownPeers.put(peer.toString(), peer); - }); + Runnable getPeersRunnable = () -> { + if (threadLock.isLocked()) return; + synchronized (threadLock) { + threadLock.lock(); + } + try { + List<Peer> updatedPeers = getPeers(mainPeer, filter, sort); - result.clear(); - result.addAll(updatedPeers); - listener.onChanged(result); - } - } catch(IOException e) { - log.error("Could not parse peer received by WS: " + e.getMessage(), e); + knownPeers.clear(); + updatedPeers.stream().forEach(peer -> { + String buid = buid(peer.getStats()); + if (!knownBlocks.contains(buid)) { + knownBlocks.add(buid); + } + knownPeers.put(peer.toString(), peer); + }); + + result.clear(); + result.addAll(updatedPeers); + listener.onChanged(result); + } + catch(Exception e) { + log.error("Error while loading all peers: " + e.getMessage(), e); } finally { synchronized (threadLock) { threadLock.unlock(); } } - }, autoreconnect); + }; - // Manage new peer event - networkRemoteService.addPeerListener(mainPeer, json -> { + Consumer<NetworkPeers.Peer> refreshPeerConsumer = (bmaPeer) -> { if (threadLock.isLocked()) return; synchronized (threadLock) { threadLock.lock(); } - try { - NetworkPeers.Peer bmaPeer = readValue(json, NetworkPeers.Peer.class); final List<Peer> newPeers = new ArrayList<>(); addEndpointsAsPeers(bmaPeer, newPeers, null); CompletableFuture<List<CompletableFuture<Peer>>> jobs = CompletableFuture.supplyAsync(() -> wotRemoteService.getMembersUids(mainPeer), pool) - .thenApply(memberUids -> - newPeers.stream().map(peer -> - asyncRefreshPeer(peer, memberUids, pool)) - .collect(Collectors.toList()) - ); + .thenApply(memberUids -> + newPeers.stream().map(peer -> + asyncRefreshPeer(peer, memberUids, pool)) + .collect(Collectors.toList()) + ); jobs.thenCompose(refreshedPeersFuture -> CompletableFutures.allOfToList(refreshedPeersFuture, refreshedPeer -> { - boolean exists = knownPeers.containsKey(refreshedPeer.toString()); - boolean include = peerFilter.test(refreshedPeer); - if (!include && exists) { - Peer removedPeer = knownPeers.remove(refreshedPeer.toString()); - result.remove(removedPeer); - } - else if (include && exists) { - result.remove(knownPeers.get(refreshedPeer.toString())); - } - return include; - })) - .thenApply(addedPeers -> { - result.addAll(addedPeers); - fillPeerStatsConsensus(result); - result.sort(peerComparator); - - result.stream().forEach(peer -> { - String buid = buid(peer.getStats()); - if (!knownBlocks.contains(buid)) { - knownBlocks.add(buid); - } - knownPeers.put(peer.toString(), peer); + boolean exists = knownPeers.containsKey(refreshedPeer.toString()); + boolean include = peerFilter.test(refreshedPeer); + if (!include && exists) { + Peer removedPeer = knownPeers.remove(refreshedPeer.toString()); + result.remove(removedPeer); + } + else if (include && exists) { + result.remove(knownPeers.get(refreshedPeer.toString())); + } + return include; + })) + .thenApply(addedPeers -> { + result.addAll(addedPeers); + fillPeerStatsConsensus(result); + result.sort(peerComparator); + + result.stream().forEach(peer -> { + String buid = buid(peer.getStats()); + if (!knownBlocks.contains(buid)) { + knownBlocks.add(buid); + } + knownPeers.put(peer.toString(), peer); + }); + + listener.onChanged(result); + return result; }); - - listener.onChanged(result); - return result; - }); - - } catch(IOException e) { - log.error("Could not parse peer received by WS: " + e.getMessage(), e); + } + catch(Exception e) { + log.error("Error while refreshing a peer: " + e.getMessage(), e); } finally { synchronized (threadLock) { threadLock.unlock(); } } + }; + + // Load all peers + pool.submit(getPeersRunnable); + + // Manage new block event + blockchainRemoteService.addBlockListener(mainPeer, json -> { + + log.debug("Received new block event"); + try { + BlockchainBlock block = readValue(json, BlockchainBlock.class); + String blockBuid = buid(block); + boolean isNewBlock = (blockBuid != null && !knownBlocks.contains(blockBuid)); + // If new block + wait 5s for network propagation + if (!isNewBlock) return; + } catch(IOException e) { + log.error("Could not parse peer received by WS: " + e.getMessage(), e); + } + schedule(getPeersRunnable, pool, 5000); + }, autoreconnect); + + // Manage new peer event + networkRemoteService.addPeerListener(mainPeer, json -> { + + log.debug("Received new peer event"); + try { + final NetworkPeers.Peer bmaPeer = readValue(json, NetworkPeers.Peer.class); + pool.submit(() -> refreshPeerConsumer.accept(bmaPeer)); + } catch(IOException e) { + log.error("Could not parse peer received by WS: " + e.getMessage(), e); + } }, autoreconnect); } @@ -624,12 +665,18 @@ public class NetworkServiceImpl extends BaseRemoteServiceImpl implements Network return block.getNumber() + "-" + block.getHash(); } - protected boolean waitSafe(long duration) { - try { - Thread.sleep(duration); - return true; - } catch (InterruptedException e) { - return false; + protected void schedule(Runnable command, ExecutorService pool, long delayInMs) { + if (pool instanceof ScheduledExecutorService) { + ((ScheduledExecutorService)pool).schedule(command, delayInMs, TimeUnit.MILLISECONDS); + } + else { + pool.submit(() -> { + try { + Thread.sleep(delayInMs); + command.run(); + } catch (InterruptedException e) { + } + }); } } } diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerService.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerService.java index 103e8a2b0d4f61fa3f2279b8d776430f7633b30e..f800a82916d2ecf4fa69f0391eda6b6f6560ca39 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerService.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerService.java @@ -34,21 +34,19 @@ public interface PeerService extends Service { Peer save(final Peer peer); - Peer getPeerById(long peerId); - /** * Return a (cached) active peer, by currency id * @param currencyId * @return */ - Peer getActivePeerByCurrencyId(long currencyId); + Peer getActivePeerByCurrencyId(String currencyId); /** * Return a (cached) peer list, by currency id * @param currencyId * @return */ - List<Peer> getPeersByCurrencyId(long currencyId); + List<Peer> getPeersByCurrencyId(String currencyId); /** * Fill all cache need for currencies diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerServiceImpl.java index f724f1576d1f9f65ac86da21037fbabeb5b45027..ad9f7b736c4b816fe20058b7a3bf8f8082f28512 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerServiceImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/PeerServiceImpl.java @@ -44,8 +44,8 @@ import java.util.List; */ public class PeerServiceImpl implements PeerService, InitializingBean { - private Cache<Long, List<Peer>> peersByCurrencyIdCache; - private Cache<Long, Peer> activePeerByCurrencyIdCache; + private Cache<String, List<Peer>> peersByCurrencyIdCache; + private Cache<String, Peer> activePeerByCurrencyIdCache; private CurrencyService currencyService; private PeerDao peerDao; @@ -70,7 +70,7 @@ public class PeerServiceImpl implements PeerService, InitializingBean { public Peer save(final Peer peer) { Preconditions.checkNotNull(peer); - Preconditions.checkNotNull(peer.getCurrencyId()); + Preconditions.checkNotNull(peer.getCurrency()); Preconditions.checkArgument(StringUtils.isNotBlank(peer.getHost())); Preconditions.checkArgument(peer.getPort() >= 0); @@ -88,10 +88,10 @@ public class PeerServiceImpl implements PeerService, InitializingBean { // update cache (if already loaded) if (peersByCurrencyIdCache != null) { - List<Peer> peers = peersByCurrencyIdCache.get(peer.getCurrencyId()); + List<Peer> peers = peersByCurrencyIdCache.get(peer.getCurrency()); if (peers == null) { - peers = new ArrayList<Peer>(); - peersByCurrencyIdCache.put(peer.getCurrencyId(), peers); + peers = new ArrayList<>(); + peersByCurrencyIdCache.put(peer.getCurrency(), peers); peers.add(peer); } else if (!peers.contains(peer)) { @@ -102,23 +102,18 @@ public class PeerServiceImpl implements PeerService, InitializingBean { return result; } - - public Peer getPeerById(long peerId) { - return peerDao.getById(peerId); - } - - /** + /** * Return a (cached) active peer, by currency id * @param currencyId * @return */ - public Peer getActivePeerByCurrencyId(long currencyId) { + public Peer getActivePeerByCurrencyId(String currency) { // Check if cache as been loaded if (activePeerByCurrencyIdCache == null) { - activePeerByCurrencyIdCache = new SimpleCache<Long, Peer>() { + activePeerByCurrencyIdCache = new SimpleCache<String, Peer>() { @Override - public Peer load(Long currencyId) { + public Peer load(String currencyId) { List<Peer> peers = peerDao.getPeersByCurrencyId(currencyId); if (CollectionUtils.isEmpty(peers)) { String currencyName = currencyService.getCurrencyNameById(currencyId); @@ -132,7 +127,7 @@ public class PeerServiceImpl implements PeerService, InitializingBean { }; } - return activePeerByCurrencyIdCache.get(currencyId); + return activePeerByCurrencyIdCache.get(currency); } /** @@ -140,7 +135,7 @@ public class PeerServiceImpl implements PeerService, InitializingBean { * @param currencyId * @return */ - public List<Peer> getPeersByCurrencyId(long currencyId) { + public List<Peer> getPeersByCurrencyId(String currencyId) { // Check if cache as been loaded if (peersByCurrencyIdCache == null) { throw new TechnicalException("Cache not initialize. Please call loadCache() before getPeersByCurrencyId()."); @@ -158,9 +153,9 @@ public class PeerServiceImpl implements PeerService, InitializingBean { return; } - peersByCurrencyIdCache = new SimpleCache<Long, List<Peer>>() { + peersByCurrencyIdCache = new SimpleCache<String, List<Peer>>() { @Override - public List<Peer> load(Long currencyId) { + public List<Peer> load(String currencyId) { return peerDao.getPeersByCurrencyId(currencyId); } }; diff --git a/duniter4j-core-client/src/test/java/org/duniter/core/client/TestFixtures.java b/duniter4j-core-client/src/test/java/org/duniter/core/client/TestFixtures.java index a08ab9ca7130c8d85bcd5e5c508c86739a9e08b8..f57e8a39845def9fe0a3e6b7783c3b597f507e87 100644 --- a/duniter4j-core-client/src/test/java/org/duniter/core/client/TestFixtures.java +++ b/duniter4j-core-client/src/test/java/org/duniter/core/client/TestFixtures.java @@ -25,8 +25,7 @@ package org.duniter.core.client; public class TestFixtures extends org.duniter.core.test.TestFixtures { - - public long getDefaultCurrencyId() { - return -1; + public String getDefaultCurrency() { + return "g1"; } } diff --git a/duniter4j-core-client/src/test/java/org/duniter/core/client/TestResource.java b/duniter4j-core-client/src/test/java/org/duniter/core/client/TestResource.java index 1029dadb1f1e56926ef3f159918356cf17304957..7c8301261e62e827713a6f3eb1d2bbcef19ac9cf 100644 --- a/duniter4j-core-client/src/test/java/org/duniter/core/client/TestResource.java +++ b/duniter4j-core-client/src/test/java/org/duniter/core/client/TestResource.java @@ -105,7 +105,6 @@ public class TestResource extends org.duniter.core.test.TestResource { * Convenience methods that could be override to initialize other configuration * * @param configFilename - * @param configArgs */ protected void initConfiguration(String configFilename) { String[] configArgs = getConfigArgs(); @@ -159,7 +158,7 @@ public class TestResource extends org.duniter.core.test.TestResource { .setHost(config.getNodeHost()) .setPort(config.getNodePort()) .build(); - peer.setCurrencyId(fixtures.getDefaultCurrencyId()); + peer.setCurrency(fixtures.getDefaultCurrency()); ServiceLocator.instance().getPeerService().save(peer); diff --git a/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/TransactionRemoteServiceTest.java b/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/TransactionRemoteServiceTest.java index 3118e7f373661f1c9060abbd5182999c69510331..42f6bb05cbd76bf60a573ae44e296b100529611c 100644 --- a/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/TransactionRemoteServiceTest.java +++ b/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/TransactionRemoteServiceTest.java @@ -84,7 +84,7 @@ public class TransactionRemoteServiceTest { CryptoUtils.decodeBase58(resource.getFixtures().getUserPublicKey()), CryptoUtils.decodeBase58(resource.getFixtures().getUserSecretKey())); - wallet.setCurrencyId(resource.getFixtures().getDefaultCurrencyId()); + wallet.setCurrencyId(resource.getFixtures().getDefaultCurrency()); return wallet; } diff --git a/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/WotRemoteServiceTest.java b/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/WotRemoteServiceTest.java index 3415c584aa530991466f2f7a1580f82fa7eb9362..bb6ea3a72b4593968c9d5c8f9ff8ba490e50b629 100644 --- a/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/WotRemoteServiceTest.java +++ b/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/WotRemoteServiceTest.java @@ -60,8 +60,8 @@ public class WotRemoteServiceTest { @Test public void getIdentity() throws Exception { - Set<Long> currencyIds = new HashSet<>(); - currencyIds.add(resource.getFixtures().getDefaultCurrencyId()); + Set<String> currencyIds = new HashSet<>(); + currencyIds.add(resource.getFixtures().getDefaultCurrency()); List<Identity> result = service .findIdentities(currencyIds, resource.getFixtures().getUid()); Assert.assertNotNull(result); @@ -71,7 +71,7 @@ public class WotRemoteServiceTest { @Test public void findByUid() throws Exception { WotLookup.Uid result = service - .findByUid(resource.getFixtures().getDefaultCurrencyId(), + .findByUid(resource.getFixtures().getDefaultCurrency(), resource.getFixtures().getUid()); Assert.assertNotNull(result); } @@ -82,7 +82,7 @@ public class WotRemoteServiceTest { public void getCertifiedBy() throws Exception { WotRemoteService service = ServiceLocator.instance().getWotRemoteService(); WotCertification result = service.getCertifiedBy( - resource.getFixtures().getDefaultCurrencyId(), + resource.getFixtures().getDefaultCurrency(), resource.getFixtures().getUid()); Assert.assertNotNull(result); @@ -111,7 +111,7 @@ public class WotRemoteServiceTest { // FIXME: user 'gab' has no certification public void getCertifiersOf() throws Exception { WotCertification result = service.getCertifiersOf( - resource.getFixtures().getDefaultCurrencyId(), + resource.getFixtures().getDefaultCurrency(), resource.getFixtures().getUid()); Assert.assertNotNull(result); diff --git a/duniter4j-core-shared/pom.xml b/duniter4j-core-shared/pom.xml index f826f638bebf7a4043a5a65f45775f4d288a7037..ebad633fad0b2fd6886fe70e1614c9e227228faf 100644 --- a/duniter4j-core-shared/pom.xml +++ b/duniter4j-core-shared/pom.xml @@ -7,7 +7,6 @@ <version>0.9.2-SNAPSHOT</version> </parent> - <groupId>org.duniter</groupId> <artifactId>duniter4j-core-shared</artifactId> <packaging>jar</packaging> <name>Duniter4j :: Core Shared</name> diff --git a/duniter4j-core-shared/src/main/java/org/duniter/core/beans/BeanCreationException.java b/duniter4j-core-shared/src/main/java/org/duniter/core/beans/BeanCreationException.java new file mode 100644 index 0000000000000000000000000000000000000000..624ce7e40e9c96c4af9c8cfe1ac5bffbc5382291 --- /dev/null +++ b/duniter4j-core-shared/src/main/java/org/duniter/core/beans/BeanCreationException.java @@ -0,0 +1,28 @@ +package org.duniter.core.beans; + +import org.duniter.core.exception.TechnicalException; + +/** + * Created by blavenie on 31/03/17. + */ +public class BeanCreationException extends TechnicalException { + + public BeanCreationException() { + } + + public BeanCreationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public BeanCreationException(String message, Throwable cause) { + super(message, cause); + } + + public BeanCreationException(String message) { + super(message); + } + + public BeanCreationException(Throwable cause) { + super(cause); + } +} diff --git a/duniter4j-core-shared/src/main/java/org/duniter/core/beans/BeanFactory.java b/duniter4j-core-shared/src/main/java/org/duniter/core/beans/BeanFactory.java index 4567cea4a8f677485eb0249ba5a7ed16f1293b80..7284e9f15b721f033614f3db4afdfe9abe8b358e 100644 --- a/duniter4j-core-shared/src/main/java/org/duniter/core/beans/BeanFactory.java +++ b/duniter4j-core-shared/src/main/java/org/duniter/core/beans/BeanFactory.java @@ -61,23 +61,34 @@ public class BeanFactory implements Closeable{ beansCache.put(clazz, bean); // Call initialization + initBean(bean); + + return bean; + } + + public <S extends Bean, B extends S> void setBean(B bean, Class<S> clazz) { + if (!beansCache.containsKey(clazz)) { + beansCache.put(clazz, bean); + } + } + + /* -- protected methods -- */ + + protected <S extends Bean> void initBean(S bean) { if (bean instanceof InitializingBean){ if (log.isDebugEnabled()) { - log.debug(String.format("Initializing bean of type [%s]", clazz.getName())); + log.debug(String.format("Initializing bean of type [%s]", bean.getClass().getName())); } try { - ((InitializingBean) bean).afterPropertiesSet(); + ((InitializingBean)bean).afterPropertiesSet(); } catch(Exception e) { - throw new TechnicalException(String.format("Unable to initialize bean of type [%s]", clazz.getName()), e); + throw new TechnicalException(String.format("Unable to initialize bean of type [%s]", bean.getClass().getName()), e); } } - - return bean; } - - public <S extends Bean> S newBean(Class<S> clazz) { + protected <S extends Bean> S newBean(Class<S> clazz) { if (log.isTraceEnabled()) { log.trace(String.format("Asking bean on type [%s]...", clazz.getName())); } @@ -106,18 +117,21 @@ public class BeanFactory implements Closeable{ Class<? extends Bean> beanDefClass = beanDef.getValue(); try { - Bean bean = beanDefClass.newInstance(); - if (clazz.isInstance(bean)) { + if (clazz.isAssignableFrom(beanDefClass)) { return (S) beanDefClass.newInstance(); } } catch (Exception e) { // skip + if (log.isDebugEnabled()) { + log.debug(String.format("Unable to create the bean of type [%s] with class [%s]", clazz.getName(), beanDef.getValue().getName()), e); + } + } } } } - throw new TechnicalException(String.format("Unable to create bean with type [%s]: not configured for the service loader [%s]", clazz.getName(), Bean.class.getCanonicalName())); + throw new BeanCreationException(String.format("Unable to create bean with type [%s]: not configured for the service loader [%s]", clazz.getName(), Bean.class.getCanonicalName())); } @Override diff --git a/duniter4j-core-shared/src/main/java/org/duniter/core/util/websocket/WebsocketClientEndpoint.java b/duniter4j-core-shared/src/main/java/org/duniter/core/util/websocket/WebsocketClientEndpoint.java index 2f127d8dae2b8f279b75fabcad23c8bec343163a..71f3c54674151d5a9b4b749a00240fa29628ae0f 100644 --- a/duniter4j-core-shared/src/main/java/org/duniter/core/util/websocket/WebsocketClientEndpoint.java +++ b/duniter4j-core-shared/src/main/java/org/duniter/core/util/websocket/WebsocketClientEndpoint.java @@ -110,18 +110,20 @@ public class WebsocketClientEndpoint implements Closeable { */ @OnMessage public void onMessage(final String message) { + if (CollectionUtils.isNotEmpty(messageListeners)) { if (log.isDebugEnabled()) { log.debug("[%s] Received message: " + message); } - - messageListeners.stream().forEach(messageListener -> { - try { - messageListener.onMessage(message); - } catch (Exception e) { - log.error(String.format("[%s] Error during message handling: %s", endpointURI, e.getMessage()), e); - } - }); + synchronized (messageListeners) { + messageListeners.stream().forEach(messageListener -> { + try { + messageListener.onMessage(message); + } catch (Exception e) { + log.error(String.format("[%s] Error during message handling: %s", endpointURI, e.getMessage()), e); + } + }); + } } } diff --git a/duniter4j-es-assembly/pom.xml b/duniter4j-es-assembly/pom.xml index e12752f15b9e94853f17dc2922c26961fd90ea71..e89d4dd9270c82e4154463cd1f35bf96af369e4a 100644 --- a/duniter4j-es-assembly/pom.xml +++ b/duniter4j-es-assembly/pom.xml @@ -7,10 +7,9 @@ <version>0.9.2-SNAPSHOT</version> </parent> - <groupId>org.duniter</groupId> <artifactId>duniter4j-es-assembly</artifactId> - <packaging>pom</packaging> <name>Duniter4j :: ElasticSearch Assembly</name> + <packaging>pom</packaging> <properties> <!-- bundle configuration --> diff --git a/duniter4j-es-assembly/src/main/assembly/config/logging.yml b/duniter4j-es-assembly/src/main/assembly/config/logging.yml index 82d2ed08061ae5b56335db6048aeb3906777b52b..1218c97d7b571db79ac3d0510811680e7020c1de 100644 --- a/duniter4j-es-assembly/src/main/assembly/config/logging.yml +++ b/duniter4j-es-assembly/src/main/assembly/config/logging.yml @@ -15,6 +15,7 @@ logger: com.amazonaws.metrics.AwsSdkMetrics: ERROR org.apache.http: INFO + org.apache.http.client: ERROR org.duniter: INFO @@ -23,6 +24,8 @@ logger: org.nuiton.i18n: WARN org.nuiton.config: ERROR org.nuiton.converter: WARN + org.glassfish.tyrus: WARN + org.apache.http.client: ERROR # gateway #gateway: DEBUG diff --git a/duniter4j-es-assembly/src/test/es-home/config/logging.yml b/duniter4j-es-assembly/src/test/es-home/config/logging.yml index 6b4d5cab048a436cf77afde842d50e52b2e5ab35..c9b46939f307aa5433a2217e0e80075467c8e6b2 100644 --- a/duniter4j-es-assembly/src/test/es-home/config/logging.yml +++ b/duniter4j-es-assembly/src/test/es-home/config/logging.yml @@ -15,10 +15,13 @@ logger: com.amazonaws.metrics.AwsSdkMetrics: ERROR org.apache.http: INFO + org.apache.http.client: ERROR org.duniter: INFO + org.duniter.core.beans: DEBUG org.duniter.elasticsearch: DEBUG + org.duniter.elasticsearch.service: DEBUG org.duniter.core.client.service: DEBUG duniter : DEBUG diff --git a/duniter4j-es-core/pom.xml b/duniter4j-es-core/pom.xml index e6c1a758bffdb70edc996c3ff2ccbc8858405971..5b1e16e44415b9a471109439965dc588019c3c15 100644 --- a/duniter4j-es-core/pom.xml +++ b/duniter4j-es-core/pom.xml @@ -7,7 +7,6 @@ <version>0.9.2-SNAPSHOT</version> </parent> - <groupId>org.duniter</groupId> <artifactId>duniter4j-es-core</artifactId> <packaging>jar</packaging> <name>Duniter4j :: ElasticSearch Core plugin</name> diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/Plugin.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/Plugin.java index ae86d980b2a59fb3d8f5d3a46f73fdcb434057c3..4c8b851d4b7da36e83f02eb1e3c946c40694fa1a 100644 --- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/Plugin.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/Plugin.java @@ -23,6 +23,7 @@ package org.duniter.elasticsearch; */ import com.google.common.collect.Lists; +import org.duniter.elasticsearch.dao.DaoModule; import org.duniter.elasticsearch.rest.RestModule; import org.duniter.elasticsearch.security.SecurityModule; import org.duniter.elasticsearch.service.ServiceModule; @@ -69,6 +70,7 @@ public class Plugin extends org.elasticsearch.plugins.Plugin { modules.add(new WebSocketModule()); modules.add(new RestModule()); + modules.add(new DaoModule()); modules.add(new ServiceModule()); return modules; 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 index 0468f58696aacd214c7358abade834b43e5bb700..d91277ecc49c437e4978e99ee960ec19720659b8 100644 --- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginInit.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginInit.java @@ -27,7 +27,7 @@ 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.service.EndpointService; +import org.duniter.elasticsearch.service.PeerService; import org.duniter.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.health.ClusterHealthStatus; @@ -101,7 +101,8 @@ public class PluginInit extends AbstractLifecycleComponent<PluginInit> { logger.info("Checking Duniter core indices..."); } - injector.getInstance(CurrencyService.class).createIndexIfNotExists(); + injector.getInstance(CurrencyService.class) + .createIndexIfNotExists(); if (logger.isInfoEnabled()) { logger.info("Checking Duniter core indices... [OK]"); @@ -115,21 +116,22 @@ public class PluginInit extends AbstractLifecycleComponent<PluginInit> { Peer peer = pluginSettings.checkAndGetPeer(); // Index (or refresh) node's currency - Currency currency = injector.getInstance(CurrencyService.class).indexCurrencyFromPeer(peer, true); + Currency currency = injector.getInstance(CurrencyService.class) + .indexCurrencyFromPeer(peer, true); // Add access to currency/block index injector.getInstance(RestSecurityController.class).allowIndexType(RestRequest.Method.GET, - currency.getCurrency(), + currency.getCurrencyName(), BlockchainService.BLOCK_TYPE); injector.getInstance(RestSecurityController.class).allowPostSearchIndexType( - currency.getCurrency(), + currency.getCurrencyName(), BlockchainService.BLOCK_TYPE); // Add access to currency/peer index injector.getInstance(RestSecurityController.class).allowIndexType(RestRequest.Method.GET, - currency.getCurrency(), + currency.getCurrencyName(), BlockchainService.PEER_TYPE); injector.getInstance(RestSecurityController.class).allowPostSearchIndexType( - currency.getCurrency(), + currency.getCurrencyName(), BlockchainService.PEER_TYPE); // Index blocks (and listen if new block appear) @@ -138,9 +140,9 @@ public class PluginInit extends AbstractLifecycleComponent<PluginInit> { .listenAndIndexNewBlock(peer); // Index peers (and listen if new peer appear) - injector.getInstance(EndpointService.class) - .indexAllEndpoints(peer)/* - .listenAndIndexNewPeer(peer)*/; + injector.getInstance(PeerService.class) + //.indexAllPeers(peer) + .listenAndIndexPeers(peer); } } } diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/beans/ESBeanFactory.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/beans/ESBeanFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..fa8ed45ff059c0c8326e62c545c987cd9115ecd9 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/beans/ESBeanFactory.java @@ -0,0 +1,40 @@ +package org.duniter.elasticsearch.beans; + +import org.duniter.core.beans.Bean; +import org.duniter.core.beans.BeanCreationException; +import org.duniter.core.beans.BeanFactory; +import org.elasticsearch.common.inject.Injector; + +/** + * Created by blavenie on 31/03/17. + */ +public class ESBeanFactory extends BeanFactory { + + private Injector injector = null; + + public void setInjector(Injector injector) { + this.injector = injector; + } + + @Override + protected <S extends Bean> void initBean(S bean) { + super.initBean(bean); + if (injector != null) { + injector.injectMembers(bean); + } + } + + @Override + protected <S extends Bean> S newBean(Class<S> clazz) { + try { + return super.newBean(clazz); + } + catch(BeanCreationException e) { + // try using injector, if exists + if (injector != null) { + return injector.getBinding(clazz).getProvider().get(); + } + throw e; + } + } +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/client/Duniter4jClient.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/client/Duniter4jClient.java new file mode 100644 index 0000000000000000000000000000000000000000..a00760357cb0f36a2d023db2d616d0208a5dd20b --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/client/Duniter4jClient.java @@ -0,0 +1,72 @@ +package org.duniter.elasticsearch.client; + +import org.duniter.core.beans.Bean; +import org.duniter.elasticsearch.dao.handler.StringReaderHandler; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.bulk.BulkRequestBuilder; +import org.elasticsearch.client.Client; + +import java.io.File; +import java.io.InputStream; +import java.util.Map; +import java.util.Set; + +/** + * Created by blavenie on 03/04/17. + */ +public interface Duniter4jClient extends Bean, Client { + + boolean existsIndex(String index); + + void deleteIndexIfExists(String indexName); + + Object getFieldById(String index, String type, String docId, String fieldName); + + Map<String, Object> getFieldByIds(String index, String type, Set<String> ids, String fieldName); + + Map<String, Object> getFieldsById(String index, String type, String docId, String... fieldNames); + + <T> T getTypedFieldById(String index, String type, String docId, String fieldName); + + Map<String, Object> getMandatoryFieldsById(String index, String type, String docId, String... fieldNames); + + String indexDocumentFromJson(String index, String type, String json); + + void updateDocumentFromJson(String index, String type, String id, String json); + + void checkSameDocumentField(String index, String type, String id, String fieldName, String expectedvalue) throws ElasticsearchException; + + void checkSameDocumentIssuer(String index, String type, String id, String expectedIssuer); + + boolean isDocumentExists(String index, String type, String id) throws ElasticsearchException; + + void checkDocumentExists(String index, String type, String id) throws ElasticsearchException; + + /** + * Retrieve a document by id (safe mode) + * @param docId + * @return + */ + <T extends Object> T getSourceByIdOrNull(String index, String type, String docId, Class<T> classOfT, String... fieldNames); + + /** + * Retrieve a document by id + * @param docId + * @return + */ + <T extends Object> T getSourceById(String index, String type, String docId, Class<T> classOfT, String... fieldNames); + + void bulkFromClasspathFile(String classpathFile, String indexName, String indexType); + + void bulkFromClasspathFile(String classpathFile, String indexName, String indexType, StringReaderHandler handler); + + void bulkFromFile(File file, String indexName, String indexType); + + void bulkFromFile(File file, String indexName, String indexType, StringReaderHandler handler); + + void bulkFromStream(InputStream is, String indexName, String indexType); + + void bulkFromStream(InputStream is, String indexName, String indexType, StringReaderHandler handler); + + void flushDeleteBulk(final String index, final String type, BulkRequestBuilder bulkRequest); +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/client/Duniter4jClientImpl.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/client/Duniter4jClientImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..158b86b4b3cecbfedca3fcebf5da1ceaeb5210fb --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/client/Duniter4jClientImpl.java @@ -0,0 +1,952 @@ +package org.duniter.elasticsearch.client; + +/* + * #%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.databind.ObjectMapper; +import com.google.common.base.Joiner; +import org.apache.commons.collections4.MapUtils; +import org.duniter.core.client.model.bma.jackson.JacksonUtils; +import org.duniter.core.client.model.elasticsearch.Record; +import org.duniter.core.exception.TechnicalException; +import org.duniter.core.util.CollectionUtils; +import org.duniter.core.util.ObjectUtils; +import org.duniter.core.util.Preconditions; +import org.duniter.core.util.StringUtils; +import org.duniter.elasticsearch.dao.AbstractDao; +import org.duniter.elasticsearch.dao.handler.StringReaderHandler; +import org.duniter.elasticsearch.exception.AccessDeniedException; +import org.duniter.elasticsearch.exception.NotFoundException; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.*; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequestBuilder; +import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequestBuilder; +import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse; +import org.elasticsearch.action.bulk.BulkItemResponse; +import org.elasticsearch.action.bulk.BulkRequest; +import org.elasticsearch.action.bulk.BulkRequestBuilder; +import org.elasticsearch.action.bulk.BulkResponse; +import org.elasticsearch.action.count.CountRequest; +import org.elasticsearch.action.count.CountRequestBuilder; +import org.elasticsearch.action.count.CountResponse; +import org.elasticsearch.action.delete.DeleteRequest; +import org.elasticsearch.action.delete.DeleteRequestBuilder; +import org.elasticsearch.action.delete.DeleteResponse; +import org.elasticsearch.action.exists.ExistsRequest; +import org.elasticsearch.action.exists.ExistsRequestBuilder; +import org.elasticsearch.action.exists.ExistsResponse; +import org.elasticsearch.action.explain.ExplainRequest; +import org.elasticsearch.action.explain.ExplainRequestBuilder; +import org.elasticsearch.action.explain.ExplainResponse; +import org.elasticsearch.action.fieldstats.FieldStatsRequest; +import org.elasticsearch.action.fieldstats.FieldStatsRequestBuilder; +import org.elasticsearch.action.fieldstats.FieldStatsResponse; +import org.elasticsearch.action.get.*; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.indexedscripts.delete.DeleteIndexedScriptRequest; +import org.elasticsearch.action.indexedscripts.delete.DeleteIndexedScriptRequestBuilder; +import org.elasticsearch.action.indexedscripts.delete.DeleteIndexedScriptResponse; +import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptRequest; +import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptRequestBuilder; +import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptResponse; +import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptRequest; +import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptRequestBuilder; +import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptResponse; +import org.elasticsearch.action.percolate.*; +import org.elasticsearch.action.search.*; +import org.elasticsearch.action.suggest.SuggestRequest; +import org.elasticsearch.action.suggest.SuggestRequestBuilder; +import org.elasticsearch.action.suggest.SuggestResponse; +import org.elasticsearch.action.termvectors.*; +import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.action.update.UpdateRequestBuilder; +import org.elasticsearch.action.update.UpdateResponse; +import org.elasticsearch.client.AdminClient; +import org.elasticsearch.client.Client; +import org.elasticsearch.client.Requests; +import org.elasticsearch.client.support.Headers; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHitField; +import org.elasticsearch.threadpool.ThreadPool; + +import java.io.*; +import java.util.*; + +/** + * Created by Benoit on 08/04/2015. + */ +public class Duniter4jClientImpl implements Duniter4jClient { + + private final static ESLogger logger = Loggers.getLogger("duniter.client"); + + private final Client client; + + private final ObjectMapper objectMapper; + + @Inject + public Duniter4jClientImpl(Client client) { + super(); + this.client = client; + this.objectMapper = JacksonUtils.newObjectMapper(); + } + + @Override + public boolean existsIndex(String indexes) { + IndicesExistsRequestBuilder requestBuilder = client.admin().indices().prepareExists(indexes); + IndicesExistsResponse response = requestBuilder.execute().actionGet(); + return response.isExists(); + } + + @Override + public void deleteIndexIfExists(String indexName){ + if (!existsIndex(indexName)) { + return; + } + if (logger.isInfoEnabled()) { + logger.info(String.format("Deleting index [%s]", indexName)); + } + + DeleteIndexRequestBuilder deleteIndexRequestBuilder = client.admin().indices().prepareDelete(indexName); + deleteIndexRequestBuilder.execute().actionGet(); + } + + @Override + public String indexDocumentFromJson(String index, String type, String json) { + IndexResponse response = client.prepareIndex(index, type) + .setSource(json) + .setRefresh(true) + .execute().actionGet(); + return response.getId(); + } + + @Override + public void updateDocumentFromJson(String index, String type, String id, String json) { + // Execute indexBlocksFromNode + client.prepareUpdate(index, type, id) + .setRefresh(true) + .setDoc(json) + .execute().actionGet(); + } + + @Override + public void checkSameDocumentField(String index, String type, String id, String fieldName, String expectedvalue) throws ElasticsearchException { + + GetResponse response = client.prepareGet(index, type, id) + .setFields(fieldName) + .execute().actionGet(); + boolean failed = !response.isExists(); + if (failed) { + throw new NotFoundException(String.format("Document [%s/%s/%s] not exists.", index, type, id)); + } else { + String docValue = (String)response.getFields().get(fieldName).getValue(); + if (!Objects.equals(expectedvalue, docValue)) { + throw new AccessDeniedException(String.format("Could not delete this document: not same [%s].", fieldName)); + } + } + } + + @Override + public boolean isDocumentExists(String index, String type, String id) throws ElasticsearchException { + GetResponse response = client.prepareGet(index, type, id) + .setFetchSource(false) + .execute().actionGet(); + return response.isExists(); + } + + @Override + public void checkDocumentExists(String index, String type, String id) throws ElasticsearchException { + if (!isDocumentExists(index, type, id)) { + throw new NotFoundException(String.format("Document [%s/%s/%s] not exists.", index, type, id)); + } + } + + + @Override + public void checkSameDocumentIssuer(String index, String type, String id, String expectedIssuer) { + String issuer = getMandatoryFieldsById(index, type, id, Record.PROPERTY_ISSUER).get(Record.PROPERTY_ISSUER).toString(); + if (!ObjectUtils.equals(expectedIssuer, issuer)) { + throw new TechnicalException("Not same issuer"); + } + } + + /** + * Retrieve some field from a document id, and check if all field not null + * @param docId + * @return + */ + @Override + public Map<String, Object> getMandatoryFieldsById(String index, String type, String docId, String... fieldNames) { + Map<String, Object> fields = getFieldsById(index, type, docId, fieldNames); + if (MapUtils.isEmpty(fields)) throw new NotFoundException(String.format("Document [%s/%s/%s] not exists.", index, type, docId)); + Arrays.stream(fieldNames).forEach((fieldName) -> { + if (!fields.containsKey(fieldName)) throw new NotFoundException(String.format("Document [%s/%s/%s] should have the mandatory field [%s].", index, type, docId, fieldName)); + }); + return fields; + } + + /** + * Retrieve some field from a document id + * @param docId + * @return + */ + @Override + public Map<String, Object> getFieldsById(String index, String type, String docId, String... fieldNames) { + // Prepare request + SearchRequestBuilder searchRequest = client + .prepareSearch(index) + .setTypes(type) + .setSearchType(SearchType.DFS_QUERY_THEN_FETCH); + + searchRequest.setQuery(QueryBuilders.idsQuery().ids(docId)); + searchRequest.addFields(fieldNames); + + // Execute query + try { + SearchResponse response = searchRequest.execute().actionGet(); + + Map<String, Object> result = new HashMap<>(); + // Read query result + SearchHit[] searchHits = response.getHits().getHits(); + for (SearchHit searchHit : searchHits) { + Map<String, SearchHitField> hitFields = searchHit.getFields(); + for(String fieldName: hitFields.keySet()) { + result.put(fieldName, hitFields.get(fieldName).getValue()); + } + break; + } + return result; + } + catch(SearchPhaseExecutionException e) { + // Failed or no item on index + throw new TechnicalException(String.format("[%s/%s] Unable to retrieve fields [%s] for id [%s]", + index, type, + Joiner.on(',').join(fieldNames).toString(), + docId), e); + } + } + + /** + * Retrieve some field from a document id + * @param index + * @param type + * @param ids + * @param fieldName + * @return + */ + @Override + public Map<String, Object> getFieldByIds(String index, String type, Set<String> ids, String fieldName) { + // Prepare request + SearchRequestBuilder searchRequest = client + .prepareSearch(index) + .setTypes(type) + .setSearchType(SearchType.DFS_QUERY_THEN_FETCH); + + searchRequest.setQuery(QueryBuilders.idsQuery().ids(ids)); + searchRequest.addFields(fieldName); + + // Execute query + try { + SearchResponse response = searchRequest.execute().actionGet(); + + Map<String, Object> result = new HashMap<>(); + // Read query result + SearchHit[] searchHits = response.getHits().getHits(); + for (SearchHit searchHit : searchHits) { + Map<String, SearchHitField> hitFields = searchHit.getFields(); + if (hitFields.get(fieldName) != null) { + result.put(searchHit.getId(), hitFields.get(fieldName).getValue()); + } + } + return result; + } + catch(SearchPhaseExecutionException e) { + // Failed or no item on index + throw new TechnicalException(String.format("[%s/%s] Unable to retrieve field [%s] for ids [%s]", + index, type, fieldName, + Joiner.on(',').join(ids).toString()), e); + } + } + + /** + * Retrieve a field from a document id + * @param docId + * @return + */ + @Override + public Object getFieldById(String index, String type, String docId, String fieldName) { + + Map<String, Object> result = getFieldsById(index, type, docId, fieldName); + if (MapUtils.isEmpty(result)) { + return null; + } + return result.get(fieldName); + } + + @Override + public <T> T getTypedFieldById(String index, String type, String docId, String fieldName) { + return (T)getFieldById(index, type, docId, fieldName); + } + + /** + * Retrieve a document by id (safe mode) + * @param docId + * @return + */ + @Override + public <T extends Object> T getSourceByIdOrNull(String index, String type, String docId, Class<T> classOfT, String... fieldNames) { + try { + return getSourceById(index, type, docId, classOfT, fieldNames); + } + catch(TechnicalException e) { + return null; // not found + } + } + + /** + * Retrieve a document by id + * @param docId + * @return + */ + @Override + public <T extends Object> T getSourceById(String index, String type, String docId, Class<T> classOfT, String... fieldNames) { + + // Prepare request + SearchRequestBuilder searchRequest = client + .prepareSearch(index) + .setSearchType(SearchType.QUERY_AND_FETCH); + + searchRequest.setQuery(QueryBuilders.idsQuery(type).ids(docId)); + if (CollectionUtils.isNotEmpty(fieldNames)) { + searchRequest.setFetchSource(fieldNames, null); + } + else { + searchRequest.setFetchSource(true); // full source + } + + // Execute query + try { + SearchResponse response = searchRequest.execute().actionGet(); + + if (response.getHits().getTotalHits() == 0) return null; + + // Read query result + SearchHit[] searchHits = response.getHits().getHits(); + + for (SearchHit searchHit : searchHits) { + if (searchHit.source() != null) { + return objectMapper.readValue(searchHit.source(), classOfT); + } + break; + } + return null; + } + catch(SearchPhaseExecutionException | IOException e) { + // Failed to get source + throw new TechnicalException(String.format("[%s/%s] Error while getting [%s]", + index, type, + docId), e); + } + } + + @Override + public void bulkFromClasspathFile(String classpathFile, String indexName, String indexType) { + bulkFromClasspathFile(classpathFile, indexName, indexType, null); + } + + @Override + public void bulkFromClasspathFile(String classpathFile, String indexName, String indexType, StringReaderHandler handler) { + InputStream is = null; + try { + is = getClass().getClassLoader().getResourceAsStream(classpathFile); + if (is == null) { + throw new TechnicalException(String.format("Could not retrieve data file [%s] need to fill index [%s]: ", classpathFile, indexName)); + } + + bulkFromStream(is, indexName, indexType, handler); + } + finally { + if (is != null) { + try { + is.close(); + } + catch(IOException e) { + // Silent is gold + } + } + } + } + + @Override + public void bulkFromFile(File file, String indexName, String indexType) { + bulkFromFile(file, indexName, indexType, null); + } + + @Override + public void bulkFromFile(File file, String indexName, String indexType, StringReaderHandler handler) { + Preconditions.checkNotNull(file); + Preconditions.checkArgument(file.exists()); + + InputStream is = null; + try { + is = new BufferedInputStream(new FileInputStream(file)); + bulkFromStream(is, indexName, indexType, handler); + } + catch(FileNotFoundException e) { + throw new TechnicalException(String.format("[%s] Could not find file %s", indexName, file.getPath()), e); + } + finally { + if (is != null) { + try { + is.close(); + } + catch(IOException e) { + // Silent is gold + } + } + } + } + + @Override + public void bulkFromStream(InputStream is, String indexName, String indexType) { + bulkFromStream(is, indexName, indexType, null); + } + + @Override + public void bulkFromStream(InputStream is, String indexName, String indexType, StringReaderHandler handler) { + Preconditions.checkNotNull(is); + BulkRequest bulkRequest = Requests.bulkRequest(); + + BufferedReader br = null; + + try { + br = new BufferedReader(new InputStreamReader(is)); + + String line = br.readLine(); + StringBuilder builder = new StringBuilder(); + while(line != null) { + line = line.trim(); + if (StringUtils.isNotBlank(line)) { + if (logger.isTraceEnabled()) { + logger.trace(String.format("[%s] Add to bulk: %s", indexName, line)); + } + if (handler != null) { + line = handler.onReadLine(line.trim()); + } + builder.append(line).append('\n'); + } + line = br.readLine(); + } + + byte[] data = builder.toString().getBytes(); + bulkRequest.add(new BytesArray(data), indexName, indexType, false); + + } catch(Exception e) { + throw new TechnicalException(String.format("[%s] Error while inserting rows into %s", indexName, indexType), e); + } + finally { + if (br != null) { + try { + br.close(); + } + catch(IOException e) { + // Silent is gold + } + } + } + + try { + client.bulk(bulkRequest).actionGet(); + } catch(Exception e) { + throw new TechnicalException(String.format("[%s] Error while inserting rows into %s", indexName, indexType), e); + } + } + + @Override + public void flushDeleteBulk(final String index, final String type, BulkRequestBuilder bulkRequest) { + if (bulkRequest.numberOfActions() > 0) { + + BulkResponse bulkResponse = bulkRequest.execute().actionGet(); + // If failures, continue but save missing blocks + if (bulkResponse.hasFailures()) { + // process failures by iterating through each bulk response item + for (BulkItemResponse itemResponse : bulkResponse) { + boolean skip = !itemResponse.isFailed(); + if (!skip) { + logger.debug(String.format("[%s/%s] Error while deleting doc [%s]: %s. Skipping this deletion.", index, type, itemResponse.getId(), itemResponse.getFailureMessage())); + } + } + } + } + } + + /* delegate methods */ + + @Override + public AdminClient admin() { + return client.admin(); + } + + @Override + public ActionFuture<IndexResponse> index(IndexRequest request) { + return client.index(request); + } + + @Override + public void index(IndexRequest request, ActionListener<IndexResponse> listener) { + client.index(request, listener); + } + + @Override + public IndexRequestBuilder prepareIndex() { + return client.prepareIndex(); + } + + @Override + public ActionFuture<UpdateResponse> update(UpdateRequest request) { + return client.update(request); + } + + @Override + public void update(UpdateRequest request, ActionListener<UpdateResponse> listener) { + client.update(request, listener); + } + + @Override + public UpdateRequestBuilder prepareUpdate() { + return client.prepareUpdate(); + } + + @Override + public UpdateRequestBuilder prepareUpdate(String index, String type, String id) { + return client.prepareUpdate(index, type, id); + } + + @Override + public IndexRequestBuilder prepareIndex(String index, String type) { + return client.prepareIndex(index, type); + } + + @Override + public IndexRequestBuilder prepareIndex(String index, String type, @Nullable String id) { + return client.prepareIndex(index, type, id); + } + + @Override + public ActionFuture<DeleteResponse> delete(DeleteRequest request) { + return client.delete(request); + } + + @Override + public void delete(DeleteRequest request, ActionListener<DeleteResponse> listener) { + client.delete(request, listener); + } + + @Override + public DeleteRequestBuilder prepareDelete() { + return client.prepareDelete(); + } + + @Override + public DeleteRequestBuilder prepareDelete(String index, String type, String id) { + return client.prepareDelete(index, type, id); + } + + @Override + public ActionFuture<BulkResponse> bulk(BulkRequest request) { + return client.bulk(request); + } + + @Override + public void bulk(BulkRequest request, ActionListener<BulkResponse> listener) { + client.bulk(request, listener); + } + + @Override + public BulkRequestBuilder prepareBulk() { + return client.prepareBulk(); + } + + @Override + public ActionFuture<GetResponse> get(GetRequest request) { + return client.get(request); + } + + @Override + public void get(GetRequest request, ActionListener<GetResponse> listener) { + client.get(request, listener); + } + + @Override + public GetRequestBuilder prepareGet() { + return client.prepareGet(); + } + + @Override + public GetRequestBuilder prepareGet(String index, @Nullable String type, String id) { + return client.prepareGet(index, type, id); + } + + @Override + public PutIndexedScriptRequestBuilder preparePutIndexedScript() { + return client.preparePutIndexedScript(); + } + + @Override + public PutIndexedScriptRequestBuilder preparePutIndexedScript(@Nullable String scriptLang, String id, String source) { + return client.preparePutIndexedScript(scriptLang, id, source); + } + + @Override + public void deleteIndexedScript(DeleteIndexedScriptRequest request, ActionListener<DeleteIndexedScriptResponse> listener) { + client.deleteIndexedScript(request, listener); + } + + @Override + public ActionFuture<DeleteIndexedScriptResponse> deleteIndexedScript(DeleteIndexedScriptRequest request) { + return client.deleteIndexedScript(request); + } + + @Override + public DeleteIndexedScriptRequestBuilder prepareDeleteIndexedScript() { + return client.prepareDeleteIndexedScript(); + } + + @Override + public DeleteIndexedScriptRequestBuilder prepareDeleteIndexedScript(@Nullable String scriptLang, String id) { + return client.prepareDeleteIndexedScript(scriptLang, id); + } + + @Override + public void putIndexedScript(PutIndexedScriptRequest request, ActionListener<PutIndexedScriptResponse> listener) { + client.putIndexedScript(request, listener); + } + + @Override + public ActionFuture<PutIndexedScriptResponse> putIndexedScript(PutIndexedScriptRequest request) { + return client.putIndexedScript(request); + } + + @Override + public GetIndexedScriptRequestBuilder prepareGetIndexedScript() { + return client.prepareGetIndexedScript(); + } + + @Override + public GetIndexedScriptRequestBuilder prepareGetIndexedScript(@Nullable String scriptLang, String id) { + return client.prepareGetIndexedScript(scriptLang, id); + } + + @Override + public void getIndexedScript(GetIndexedScriptRequest request, ActionListener<GetIndexedScriptResponse> listener) { + client.getIndexedScript(request, listener); + } + + @Override + public ActionFuture<GetIndexedScriptResponse> getIndexedScript(GetIndexedScriptRequest request) { + return client.getIndexedScript(request); + } + + @Override + public ActionFuture<MultiGetResponse> multiGet(MultiGetRequest request) { + return client.multiGet(request); + } + + @Override + public void multiGet(MultiGetRequest request, ActionListener<MultiGetResponse> listener) { + client.multiGet(request, listener); + } + + @Override + public MultiGetRequestBuilder prepareMultiGet() { + return client.prepareMultiGet(); + } + + @Override + @Deprecated + public ActionFuture<CountResponse> count(CountRequest request) { + return client.count(request); + } + + @Override + @Deprecated + public void count(CountRequest request, ActionListener<CountResponse> listener) { + client.count(request, listener); + } + + @Override + @Deprecated + public CountRequestBuilder prepareCount(String... indices) { + return client.prepareCount(indices); + } + + @Override + @Deprecated + public ActionFuture<ExistsResponse> exists(ExistsRequest request) { + return client.exists(request); + } + + @Override + @Deprecated + public void exists(ExistsRequest request, ActionListener<ExistsResponse> listener) { + client.exists(request, listener); + } + + @Override + @Deprecated + public ExistsRequestBuilder prepareExists(String... indices) { + return client.prepareExists(indices); + } + + @Override + public ActionFuture<SuggestResponse> suggest(SuggestRequest request) { + return client.suggest(request); + } + + @Override + public void suggest(SuggestRequest request, ActionListener<SuggestResponse> listener) { + client.suggest(request, listener); + } + + @Override + public SuggestRequestBuilder prepareSuggest(String... indices) { + return client.prepareSuggest(indices); + } + + @Override + public ActionFuture<SearchResponse> search(SearchRequest request) { + return client.search(request); + } + + @Override + public void search(SearchRequest request, ActionListener<SearchResponse> listener) { + client.search(request, listener); + } + + @Override + public SearchRequestBuilder prepareSearch(String... indices) { + return client.prepareSearch(indices); + } + + @Override + public ActionFuture<SearchResponse> searchScroll(SearchScrollRequest request) { + return client.searchScroll(request); + } + + @Override + public void searchScroll(SearchScrollRequest request, ActionListener<SearchResponse> listener) { + client.searchScroll(request, listener); + } + + @Override + public SearchScrollRequestBuilder prepareSearchScroll(String scrollId) { + return client.prepareSearchScroll(scrollId); + } + + @Override + public ActionFuture<MultiSearchResponse> multiSearch(MultiSearchRequest request) { + return client.multiSearch(request); + } + + @Override + public void multiSearch(MultiSearchRequest request, ActionListener<MultiSearchResponse> listener) { + client.multiSearch(request, listener); + } + + @Override + public MultiSearchRequestBuilder prepareMultiSearch() { + return client.prepareMultiSearch(); + } + + @Override + public ActionFuture<TermVectorsResponse> termVectors(TermVectorsRequest request) { + return client.termVectors(request); + } + + @Override + public void termVectors(TermVectorsRequest request, ActionListener<TermVectorsResponse> listener) { + client.termVectors(request, listener); + } + + @Override + public TermVectorsRequestBuilder prepareTermVectors() { + return client.prepareTermVectors(); + } + + @Override + public TermVectorsRequestBuilder prepareTermVectors(String index, String type, String id) { + return client.prepareTermVectors(index, type, id); + } + + @Override + @Deprecated + public ActionFuture<TermVectorsResponse> termVector(TermVectorsRequest request) { + return client.termVector(request); + } + + @Override + @Deprecated + public void termVector(TermVectorsRequest request, ActionListener<TermVectorsResponse> listener) { + client.termVector(request, listener); + } + + @Override + @Deprecated + public TermVectorsRequestBuilder prepareTermVector() { + return client.prepareTermVector(); + } + + @Override + @Deprecated + public TermVectorsRequestBuilder prepareTermVector(String index, String type, String id) { + return client.prepareTermVector(index, type, id); + } + + @Override + public ActionFuture<MultiTermVectorsResponse> multiTermVectors(MultiTermVectorsRequest request) { + return client.multiTermVectors(request); + } + + @Override + public void multiTermVectors(MultiTermVectorsRequest request, ActionListener<MultiTermVectorsResponse> listener) { + client.multiTermVectors(request, listener); + } + + @Override + public MultiTermVectorsRequestBuilder prepareMultiTermVectors() { + return client.prepareMultiTermVectors(); + } + + @Override + public ActionFuture<PercolateResponse> percolate(PercolateRequest request) { + return client.percolate(request); + } + + @Override + public void percolate(PercolateRequest request, ActionListener<PercolateResponse> listener) { + client.percolate(request, listener); + } + + @Override + public PercolateRequestBuilder preparePercolate() { + return client.preparePercolate(); + } + + @Override + public ActionFuture<MultiPercolateResponse> multiPercolate(MultiPercolateRequest request) { + return client.multiPercolate(request); + } + + @Override + public void multiPercolate(MultiPercolateRequest request, ActionListener<MultiPercolateResponse> listener) { + client.multiPercolate(request, listener); + } + + @Override + public MultiPercolateRequestBuilder prepareMultiPercolate() { + return client.prepareMultiPercolate(); + } + + @Override + public ExplainRequestBuilder prepareExplain(String index, String type, String id) { + return client.prepareExplain(index, type, id); + } + + @Override + public ActionFuture<ExplainResponse> explain(ExplainRequest request) { + return client.explain(request); + } + + @Override + public void explain(ExplainRequest request, ActionListener<ExplainResponse> listener) { + client.explain(request, listener); + } + + @Override + public ClearScrollRequestBuilder prepareClearScroll() { + return client.prepareClearScroll(); + } + + @Override + public ActionFuture<ClearScrollResponse> clearScroll(ClearScrollRequest request) { + return client.clearScroll(request); + } + + @Override + public void clearScroll(ClearScrollRequest request, ActionListener<ClearScrollResponse> listener) { + client.clearScroll(request, listener); + } + + @Override + public FieldStatsRequestBuilder prepareFieldStats() { + return client.prepareFieldStats(); + } + + @Override + public ActionFuture<FieldStatsResponse> fieldStats(FieldStatsRequest request) { + return client.fieldStats(request); + } + + @Override + public void fieldStats(FieldStatsRequest request, ActionListener<FieldStatsResponse> listener) { + client.fieldStats(request, listener); + } + + @Override + public Settings settings() { + return client.settings(); + } + + @Override + public Headers headers() { + return client.headers(); + } + + public <Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder>> ActionFuture<Response> execute(Action<Request, Response, RequestBuilder> action, Request request) { + return client.execute(action, request); + } + + public <Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder>> void execute(Action<Request, Response, RequestBuilder> action, Request request, ActionListener<Response> listener) { + client.execute(action, request, listener); + } + + public <Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder>> RequestBuilder prepareExecute(Action<Request, Response, RequestBuilder> action) { + return client.prepareExecute(action); + } + + public ThreadPool threadPool() { + return client.threadPool(); + } + + public void close() { + client.close(); + } +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/AbstractDao.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/AbstractDao.java new file mode 100644 index 0000000000000000000000000000000000000000..0fe773e93eacefc8f0aea1c2b839ad92d0387c76 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/AbstractDao.java @@ -0,0 +1,105 @@ +package org.duniter.elasticsearch.dao; + +/* + * #%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.databind.ObjectMapper; +import com.google.common.base.Joiner; +import org.apache.commons.collections4.MapUtils; +import org.duniter.core.beans.Bean; +import org.duniter.core.client.model.bma.jackson.JacksonUtils; +import org.duniter.core.exception.TechnicalException; +import org.duniter.core.service.CryptoService; +import org.duniter.core.util.CollectionUtils; +import org.duniter.core.util.Preconditions; +import org.duniter.core.util.StringUtils; +import org.duniter.elasticsearch.PluginSettings; +import org.duniter.elasticsearch.client.Duniter4jClient; +import org.duniter.elasticsearch.dao.handler.StringReaderHandler; +import org.duniter.elasticsearch.exception.AccessDeniedException; +import org.duniter.elasticsearch.exception.NotFoundException; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequestBuilder; +import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequestBuilder; +import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse; +import org.elasticsearch.action.bulk.BulkItemResponse; +import org.elasticsearch.action.bulk.BulkRequest; +import org.elasticsearch.action.bulk.BulkRequestBuilder; +import org.elasticsearch.action.bulk.BulkResponse; +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.search.SearchPhaseExecutionException; +import org.elasticsearch.action.search.SearchRequestBuilder; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.search.SearchType; +import org.elasticsearch.client.Client; +import org.elasticsearch.client.Requests; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHitField; + +import java.io.*; +import java.util.*; + +/** + * Created by Benoit on 08/04/2015. + */ +public abstract class AbstractDao implements Bean { + + + protected final ESLogger logger; + protected final ObjectMapper objectMapper; + + protected Duniter4jClient client; + protected CryptoService cryptoService; + protected PluginSettings pluginSettings; + + public AbstractDao(String loggerName) { + super(); + this.logger = Loggers.getLogger(loggerName); + this.objectMapper = JacksonUtils.newObjectMapper(); + } + + @Inject + public void setClient(Duniter4jClient client) { + this.client = client; + } + + @Inject + public void setCryptoService(CryptoService cryptoService) { + this.cryptoService = cryptoService; + } + + @Inject + public void setPluginSettings(PluginSettings pluginSettings) { + this.pluginSettings = pluginSettings; + } + + /* -- protected methods -- */ + + +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/AbstractIndexDao.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/AbstractIndexDao.java new file mode 100644 index 0000000000000000000000000000000000000000..7979af65309455e0e2ec3b032c87589a41dd300d --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/AbstractIndexDao.java @@ -0,0 +1,91 @@ +package org.duniter.elasticsearch.dao; + +/* + * #%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 org.duniter.core.exception.TechnicalException; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequestBuilder; + +/** + * Created by Benoit on 08/04/2015. + */ +public abstract class AbstractIndexDao<T extends IndexDao> extends AbstractDao implements IndexDao<T> { + + private final String index; + + public AbstractIndexDao(String index) { + super("duniter.dao."+index); + this.index = index; + } + + /** + * Create index + * @throws JsonProcessingException + */ + protected abstract void createIndex() throws JsonProcessingException; + + @Override + public String getIndex() { + return index; + } + + @Override + public T createIndexIfNotExists() { + try { + if (!client.existsIndex(index)) { + createIndex(); + } + } + catch(JsonProcessingException e) { + throw new TechnicalException(String.format("Error while creating index [%s]", index)); + } + return (T)this; + } + + @Override + public T deleteIndex() { + client.deleteIndexIfExists(index); + return (T)this; + } + + @Override + public boolean existsIndex() { + return client.existsIndex(index); + } + + + /* -- protected methods -- */ + + protected void deleteIndexIfExists(){ + if (!client.existsIndex(index)) { + return; + } + if (logger.isInfoEnabled()) { + logger.info(String.format("Deleting index [%s]", index)); + } + + DeleteIndexRequestBuilder deleteIndexRequestBuilder = client.admin().indices().prepareDelete(index); + deleteIndexRequestBuilder.execute().actionGet(); + } +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/AbstractIndexTypeDao.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/AbstractIndexTypeDao.java new file mode 100644 index 0000000000000000000000000000000000000000..4f1bf5f64014b0c7fc6551c810bbc6508f5daf3c --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/AbstractIndexTypeDao.java @@ -0,0 +1,178 @@ +package org.duniter.elasticsearch.dao; + +/* + * #%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 org.duniter.core.exception.TechnicalException; +import org.duniter.elasticsearch.dao.handler.StringReaderHandler; +import org.elasticsearch.action.bulk.BulkRequestBuilder; + +import java.io.File; +import java.io.InputStream; +import java.util.Map; + +/** + * Created by Benoit on 08/04/2015. + */ +public abstract class AbstractIndexTypeDao<T extends IndexTypeDao> extends AbstractDao implements IndexTypeDao<T> { + + private final String index; + private final String type; + + public AbstractIndexTypeDao(String index, String type) { + super("duniter.dao."+index); + this.index = index; + this.type = type; + } + + /** + * Create index + * @throws JsonProcessingException + */ + protected abstract void createIndex() throws JsonProcessingException; + + @Override + public String getIndex() { + return index; + } + + @Override + public String getType() { + return type; + } + + @Override + public T createIndexIfNotExists() { + try { + if (!client.existsIndex(index)) { + createIndex(); + } + } + catch(JsonProcessingException e) { + throw new TechnicalException(String.format("Error while creating index [%s]", index)); + } + return (T)this; + } + + @Override + public T deleteIndex() { + client.deleteIndexIfExists(index); + return (T)this; + } + + @Override + public boolean isExists(String docId) { + return client.isDocumentExists(index, type, docId); + } + + public String create(final String json) { + return client.indexDocumentFromJson(index, type, json); + } + + public void update(final String id, final String json) { + client.updateDocumentFromJson(index, type, id, json); + } + + public String indexDocumentFromJson(String json) { + return client.indexDocumentFromJson(index, type, json); + } + + public void updateDocumentFromJson(String id, String json) { + client.updateDocumentFromJson(index, type, id, json); + } + + /** + * Retrieve a field from a document id + * @param docId + * @return + */ + public Object getFieldById(String docId, String fieldName) { + return client.getFieldById(index, type, docId, fieldName); + } + + public <T> T getTypedFieldById(String docId, String fieldName) { + return client.getTypedFieldById(index, type, docId, fieldName); + } + + @Override + public Map<String, Object> getMandatoryFieldsById(String docId, String... fieldNames) { + return client.getMandatoryFieldsById(index, type, docId, fieldNames); + } + + @Override + public Map<String, Object> getFieldsById(String docId, String... fieldNames) { + return client.getFieldsById(index, type, docId, fieldNames); + } + + /** + * Retrieve a document by id (safe mode) + * @param docId + * @return + */ + public <T extends Object> T getSourceByIdOrNull(String docId, Class<T> classOfT, String... fieldNames) { + return client.getSourceByIdOrNull(index, type, docId, classOfT, fieldNames); + } + + /** + * Retrieve a document by id + * @param docId + * @return + */ + public <T extends Object> T getSourceById(String docId, Class<T> classOfT, String... fieldNames) { + return client.getSourceById(index, type, docId, classOfT, fieldNames); + } + + public void bulkFromClasspathFile(String classpathFile) { + client.bulkFromClasspathFile(classpathFile, index, type, null); + } + + public void bulkFromClasspathFile(String classpathFile, StringReaderHandler handler) { + client.bulkFromClasspathFile(classpathFile, index, type, handler); + } + + public void bulkFromFile(File file) { + client.bulkFromFile(file, index, type, null); + } + + public void bulkFromFile(File file, StringReaderHandler handler) { + client.bulkFromFile(file, index, type, handler); + } + + public void bulkFromStream(InputStream is) { + client.bulkFromStream(is, index, type, null); + } + + public void bulkFromStream(InputStream is, StringReaderHandler handler) { + client.bulkFromStream(is, index, type, handler); + } + + public void flushDeleteBulk(BulkRequestBuilder bulkRequest) { + client.flushDeleteBulk(index, type, bulkRequest); + } + + @Override + public boolean existsIndex() { + return client.existsIndex(index); + } +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/BlockDao.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/BlockDao.java new file mode 100644 index 0000000000000000000000000000000000000000..34d72245f4c047066e16f982a53ba23598d2ddb5 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/BlockDao.java @@ -0,0 +1,42 @@ +package org.duniter.elasticsearch.dao; + +import org.duniter.core.beans.Bean; +import org.duniter.core.client.model.bma.BlockchainBlock; + +import java.util.List; + +/** + * Created by blavenie on 03/04/17. + */ +public interface BlockDao extends Bean, TypeDao<BlockDao> { + + void create(BlockchainBlock block, boolean wait); + + /** + * + * @param currencyName + * @param number the block number + * @param json block as JSON + */ + void create(String currencyName, String id, byte[] json, boolean wait); + + boolean isExists(String currencyName, String id); + + void update(BlockchainBlock block, boolean wait); + + /** + * + * @param currencyName + * @param number the block number, or -1 for current + * @param json block as JSON + */ + void update(String currencyName, String id, byte[] json, boolean wait); + + List<BlockchainBlock> findBlocksByHash(String currencyName, String query); + + int getMaxBlockNumber(String currencyName); + + BlockchainBlock getBlockById(String currencyName, String id); + + void deleteRange(final String currencyName, final int fromNumber, final int toNumber); +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/CurrencyExtendDao.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/CurrencyExtendDao.java new file mode 100644 index 0000000000000000000000000000000000000000..d501336180a1e1797149109c6ded023a9c204f1c --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/CurrencyExtendDao.java @@ -0,0 +1,9 @@ +package org.duniter.elasticsearch.dao; + +import org.duniter.core.client.dao.CurrencyDao; + +/** + * Created by blavenie on 03/04/17. + */ +public interface CurrencyExtendDao extends CurrencyDao, IndexTypeDao<CurrencyExtendDao> { +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/DaoModule.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/DaoModule.java new file mode 100644 index 0000000000000000000000000000000000000000..f1b870dd4fbb9f2b95aa80b6b08fc98347936f91 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/DaoModule.java @@ -0,0 +1,51 @@ +package org.duniter.elasticsearch.dao; + +/* + * #%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.beans.Bean; +import org.duniter.core.client.dao.CurrencyDao; +import org.duniter.core.client.dao.PeerDao; +import org.duniter.elasticsearch.client.Duniter4jClient; +import org.duniter.elasticsearch.client.Duniter4jClientImpl; +import org.duniter.elasticsearch.service.ServiceLocator; +import org.elasticsearch.common.inject.AbstractModule; +import org.elasticsearch.common.inject.Module; + +public class DaoModule extends AbstractModule implements Module { + + @Override protected void configure() { + + bind(Duniter4jClient.class).to(Duniter4jClientImpl.class).asEagerSingleton(); + + bindWithLocator(BlockDao.class); + bindWithLocator(PeerDao.class); + bindWithLocator(CurrencyDao.class); + } + + /* protected methods */ + + protected <T extends Bean> void bindWithLocator(Class<T> clazz) { + bind(clazz).toProvider(new ServiceLocator.Provider<>(clazz)); + } + +} \ No newline at end of file diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/IndexDao.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/IndexDao.java new file mode 100644 index 0000000000000000000000000000000000000000..5d5bb1c809b5578746aa80344db1609516c4dd1e --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/IndexDao.java @@ -0,0 +1,18 @@ +package org.duniter.elasticsearch.dao; + +import org.duniter.elasticsearch.dao.handler.StringReaderHandler; + +/** + * Created by blavenie on 30/03/17. + */ + +public interface IndexDao<T extends IndexDao> { + + T createIndexIfNotExists(); + + T deleteIndex(); + + String getIndex(); + + boolean existsIndex(); +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/IndexTypeDao.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/IndexTypeDao.java new file mode 100644 index 0000000000000000000000000000000000000000..047280fbf0b7c93504df0eb68e8aea7b8403ef3a --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/IndexTypeDao.java @@ -0,0 +1,38 @@ +package org.duniter.elasticsearch.dao; + +import org.duniter.elasticsearch.dao.handler.StringReaderHandler; +import org.elasticsearch.common.xcontent.XContentBuilder; + +import java.util.Map; + +/** + * Created by blavenie on 03/04/17. + */ +public interface IndexTypeDao<T extends IndexTypeDao> { + + T createIndexIfNotExists(); + + T deleteIndex(); + + String getIndex(); + + boolean existsIndex(); + + XContentBuilder createTypeMapping(); + + String getType(); + + boolean isExists(String docId); + + Object getFieldById(String docId, String fieldName); + + Map<String, Object> getFieldsById(String docId, String... fieldNames); + + <B> B getTypedFieldById(String docId, String fieldName); + + Map<String, Object> getMandatoryFieldsById(String docId, String... fieldNames); + + void bulkFromClasspathFile(String classpathFile); + + void bulkFromClasspathFile(String classpathFile, StringReaderHandler handler); +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/TypeDao.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/TypeDao.java new file mode 100644 index 0000000000000000000000000000000000000000..d03df4cfa753b73e5228dd2fe641e2cd110cbc22 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/TypeDao.java @@ -0,0 +1,16 @@ +package org.duniter.elasticsearch.dao; + +import org.elasticsearch.common.xcontent.XContentBuilder; + +import java.util.Map; + +/** + * Created by blavenie on 30/03/17. + */ + +public interface TypeDao<T extends TypeDao> { + + XContentBuilder createTypeMapping(); + + String getType(); +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/handler/AddSequenceAttributeHandler.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/handler/AddSequenceAttributeHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..517ce2b45737d0a65ff06f1250eaa81d2b46881e --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/handler/AddSequenceAttributeHandler.java @@ -0,0 +1,26 @@ +package org.duniter.elasticsearch.dao.handler; + +import java.util.regex.Pattern; + +public class AddSequenceAttributeHandler implements StringReaderHandler { + private int order; + private final String attributeName; + private final Pattern filterPattern; + public AddSequenceAttributeHandler(String attributeName, String filterRegex, int startValue) { + this.order = startValue; + this.attributeName = attributeName; + this.filterPattern = Pattern.compile(filterRegex); + } + + @Override + public String onReadLine(String line) { + // add 'order' field into + if (filterPattern.matcher(line).matches()) { + return String.format("%s, \"%s\": %d}", + line.substring(0, line.length()-1), + attributeName, + order++); + } + return line; + } + } \ No newline at end of file diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/handler/StringReaderHandler.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/handler/StringReaderHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..46082040768a6ea51685baac903559a39a0a4432 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/handler/StringReaderHandler.java @@ -0,0 +1,6 @@ +package org.duniter.elasticsearch.dao.handler; + +public interface StringReaderHandler { + + String onReadLine(String line); + } \ No newline at end of file diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/BlockDaoImpl.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/BlockDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..2964b8dab7c19a0d9d6931d1cf727466393d00d8 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/BlockDaoImpl.java @@ -0,0 +1,388 @@ +package org.duniter.elasticsearch.dao.impl; + +/* + * #%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.common.collect.Lists; +import org.duniter.core.client.model.bma.BlockchainBlock; +import org.duniter.core.exception.TechnicalException; +import org.duniter.core.util.Preconditions; +import org.duniter.core.util.StringUtils; +import org.duniter.core.util.json.JsonSyntaxException; +import org.duniter.elasticsearch.PluginSettings; +import org.duniter.elasticsearch.dao.AbstractDao; +import org.duniter.elasticsearch.dao.BlockDao; +import org.elasticsearch.action.ActionRequestBuilder; +import org.elasticsearch.action.bulk.BulkRequestBuilder; +import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchRequestBuilder; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.search.SearchType; +import org.elasticsearch.action.update.UpdateRequestBuilder; +import org.elasticsearch.common.inject.Inject; +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.SearchHitField; +import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.aggregations.metrics.max.Max; +import org.elasticsearch.search.highlight.HighlightField; +import org.elasticsearch.search.sort.SortOrder; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * Created by Benoit on 30/03/2015. + */ +public class BlockDaoImpl extends AbstractDao implements BlockDao { + + public static final String BLOCK_TYPE = "block"; + + public BlockDaoImpl(){ + super("duniter.dao.block"); + } + + @Override + public String getType() { + return BLOCK_TYPE; + } + + public void create(BlockchainBlock block, boolean wait) { + Preconditions.checkNotNull(block); + Preconditions.checkArgument(StringUtils.isNotBlank(block.getCurrency())); + Preconditions.checkNotNull(block.getHash()); + Preconditions.checkNotNull(block.getNumber()); + + // Serialize into JSON + // WARN: must use GSON, to have same JSON result (e.g identities and joiners field must be converted into String) + try { + String json = objectMapper.writeValueAsString(block); + + // Preparing + IndexRequestBuilder request = client.prepareIndex(block.getCurrency(), BLOCK_TYPE) + .setId(block.getNumber().toString()) + .setSource(json); + + // Execute + safeExecute(request, wait); + } + catch(JsonProcessingException e) { + throw new TechnicalException(e); + } + } + + /** + * + * @param currencyName + * @param id the block id + * @param json block as JSON + */ + public void create(String currencyName, String id, byte[] json, boolean wait) { + Preconditions.checkNotNull(currencyName); + Preconditions.checkNotNull(id); + Preconditions.checkNotNull(json); + Preconditions.checkArgument(json.length > 0); + + // Preparing indexBlocksFromNode + IndexRequestBuilder request = client.prepareIndex(currencyName, BLOCK_TYPE) + .setId(id) + .setRefresh(true) + .setSource(json); + + // Execute + safeExecute(request, wait); + } + + public boolean isExists(String currencyName, String id) { + return client.isDocumentExists(currencyName, BLOCK_TYPE, id); + } + + public void update(BlockchainBlock block, boolean wait) { + Preconditions.checkNotNull(block); + Preconditions.checkArgument(StringUtils.isNotBlank(block.getCurrency())); + Preconditions.checkNotNull(block.getHash()); + Preconditions.checkNotNull(block.getNumber()); + + // Serialize into JSON + // WARN: must use GSON, to have same JSON result (e.g identities and joiners field must be converted into String) + try { + String json = objectMapper.writeValueAsString(block); + + // Preparing + UpdateRequestBuilder request = client.prepareUpdate(block.getCurrency(), BLOCK_TYPE, block.getNumber().toString()) + .setRefresh(true) + .setDoc(json); + + // Execute + safeExecute(request, wait); + } + catch(JsonProcessingException e) { + throw new TechnicalException(e); + } + } + + /** + * + * @param currencyName + * @param id the block id + * @param json block as JSON + */ + public void update(String currencyName, String id, byte[] json, boolean wait) { + Preconditions.checkNotNull(currencyName); + Preconditions.checkNotNull(json); + Preconditions.checkArgument(json.length > 0); + + // Preparing indexBlocksFromNode + UpdateRequestBuilder request = client.prepareUpdate(currencyName, BLOCK_TYPE, id) + .setRefresh(true) + .setDoc(json); + + // Execute + safeExecute(request, wait); + } + + public List<BlockchainBlock> findBlocksByHash(String currencyName, String query) { + String[] queryParts = query.split("[\\t ]+"); + + // Prepare request + SearchRequestBuilder searchRequest = client + .prepareSearch(currencyName) + .setTypes(BLOCK_TYPE) + .setSearchType(SearchType.DFS_QUERY_THEN_FETCH); + + // If only one term, search as prefix + if (queryParts.length == 1) { + searchRequest.setQuery(QueryBuilders.prefixQuery("hash", query)); + } + + // If more than a word, search on terms match + else { + searchRequest.setQuery(QueryBuilders.matchQuery("hash", query)); + } + + // Sort as score/memberCount + searchRequest.addSort("_score", SortOrder.DESC) + .addSort("number", SortOrder.DESC); + + // Highlight matched words + searchRequest.setHighlighterTagsSchema("styled") + .addHighlightedField("hash") + .addFields("hash") + .addFields("*", "_source"); + + // Execute query + SearchResponse searchResponse = searchRequest.execute().actionGet(); + + // Read query result + return toBlocks(searchResponse, true); + } + + public int getMaxBlockNumber(String currencyName) { + // Prepare request + SearchRequestBuilder searchRequest = client + .prepareSearch(currencyName) + .setTypes(BLOCK_TYPE) + .setSearchType(SearchType.DFS_QUERY_THEN_FETCH); + + // Get max(number) + searchRequest.addAggregation(AggregationBuilders.max("max_number").field("number")); + + // Execute query + SearchResponse searchResponse = searchRequest.execute().actionGet(); + + // Read query result + Max result = searchResponse.getAggregations().get("max_number"); + if (result == null) { + return -1; + } + + return (result.getValue() == Double.NEGATIVE_INFINITY) + ? -1 + : (int)result.getValue(); + } + + @Override + public XContentBuilder createTypeMapping() { + try { + XContentBuilder mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject(BLOCK_TYPE) + .startObject("properties") + + // block number + .startObject("number") + .field("type", "integer") + .endObject() + + // hash + .startObject("hash") + .field("type", "string") + .endObject() + + // issuer + .startObject("issuer") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // previous hash + .startObject("previousHash") + .field("type", "string") + .endObject() + + // membercount + .startObject("memberCount") + .field("type", "integer") + .endObject() + + // membersChanges + .startObject("membersChanges") + .field("type", "string") + .endObject() + + // unitbase + .startObject("unitbase") + .field("type", "integer") + .endObject() + + // membersChanges + .startObject("monetaryMass") + .field("type", "long") + .endObject() + + // dividend + .startObject("dividend") + .field("type", "integer") + .endObject() + + // identities: + //.startObject("identities") + //.endObject() + + .endObject() + .endObject().endObject(); + + return mapping; + } + catch(IOException ioe) { + throw new TechnicalException("Error while getting mapping for block index: " + ioe.getMessage(), ioe); + } + } + + public BlockchainBlock getBlockById(String currencyName, String id) { + return client.getSourceById(currencyName, BLOCK_TYPE, id, BlockchainBlock.class); + } + + /** + * Delete blocks from a start number (using bulk) + * @param currencyName + * @param fromNumber + */ + public void deleteRange(final String currencyName, final int fromNumber, final int toNumber) { + + int bulkSize = pluginSettings.getIndexBulkSize(); + + BulkRequestBuilder bulkRequest = client.prepareBulk(); + for (int number=fromNumber; number<=toNumber; number++) { + + bulkRequest.add( + client.prepareDelete(currencyName, BLOCK_TYPE, String.valueOf(number)) + ); + + // Flush the bulk if not empty + if ((fromNumber - number % bulkSize) == 0) { + client.flushDeleteBulk(currencyName, BLOCK_TYPE, bulkRequest); + bulkRequest = client.prepareBulk(); + } + } + + // last flush + client.flushDeleteBulk(currencyName, BLOCK_TYPE, bulkRequest); + } + + /* -- Internal methods -- */ + + protected List<BlockchainBlock> toBlocks(SearchResponse response, boolean withHighlight) { + // Read query result + List<BlockchainBlock> result = Lists.newArrayList(); + response.getHits().forEach(searchHit -> { + BlockchainBlock block; + if (searchHit.source() != null) { + String jsonString = new String(searchHit.source()); + try { + block = objectMapper.readValue(jsonString, BlockchainBlock.class); + } catch(Exception e) { + if (logger.isDebugEnabled()) { + logger.debug("Error while parsing block from JSON:\n" + jsonString); + } + throw new JsonSyntaxException("Error while read block from JSON: " + e.getMessage(), e); + } + } + else { + block = new BlockchainBlock(); + SearchHitField field = searchHit.getFields().get("hash"); + block.setHash(field.getValue()); + } + result.add(block); + + // If possible, use highlights + if (withHighlight) { + Map<String, HighlightField> fields = searchHit.getHighlightFields(); + for (HighlightField field : fields.values()) { + String blockNameHighLight = field.getFragments()[0].string(); + block.setHash(blockNameHighLight); + } + } + }); + + return result; + } + + protected void safeExecute(ActionRequestBuilder<?, ?, ?> request, boolean wait) { + // Execute in a pool + if (!wait) { + boolean acceptedInPool = false; + while(!acceptedInPool) + try { + request.execute(); + acceptedInPool = true; + } + catch(EsRejectedExecutionException e) { + // not accepted, so wait + try { + Thread.sleep(1000); // 1s + } + catch(InterruptedException e2) { + // silent + } + } + + } else { + request.execute().actionGet(); + } + } +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/CurrencyDaoImpl.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/CurrencyDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..a077e54d6a37f6ffbceaafac44062d8ff78705f5 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/CurrencyDaoImpl.java @@ -0,0 +1,245 @@ +package org.duniter.elasticsearch.dao.impl; + +/* + * #%L + * UCoin Java :: 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 com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.collect.Lists; +import org.duniter.core.client.dao.CurrencyDao; +import org.duniter.core.client.model.local.Currency; +import org.duniter.core.exception.TechnicalException; +import org.duniter.core.util.Preconditions; +import org.duniter.core.util.StringUtils; +import org.duniter.elasticsearch.dao.AbstractIndexTypeDao; +import org.duniter.elasticsearch.dao.CurrencyExtendDao; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; +import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.update.UpdateRequestBuilder; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; + +import java.io.IOException; +import java.util.*; + +/** + * Created by blavenie on 29/12/15. + */ +public class CurrencyDaoImpl extends AbstractIndexTypeDao<CurrencyExtendDao> implements CurrencyExtendDao { + + protected static final String REGEX_WORD_SEPARATOR = "[-\\t@# _]+"; + + public static final String INDEX = "currency"; + public static final String RECORD_TYPE = "record"; + + public CurrencyDaoImpl(){ + super(INDEX, RECORD_TYPE); + } + + @Override + public org.duniter.core.client.model.local.Currency create(final org.duniter.core.client.model.local.Currency currency) { + + try { + + if (currency instanceof org.duniter.core.client.model.elasticsearch.Currency) { + fillTags((org.duniter.core.client.model.elasticsearch.Currency)currency); + } + + // Serialize into JSON + byte[] json = objectMapper.writeValueAsBytes(currency); + + System.out.println(objectMapper.writeValueAsString(currency)); + + // Preparing indexBlocksFromNode + IndexRequestBuilder indexRequest = client.prepareIndex(INDEX, RECORD_TYPE) + .setId(currency.getId()) + .setSource(json); + + // Execute indexBlocksFromNode + indexRequest + .setRefresh(true) + .execute().actionGet(); + + } catch(JsonProcessingException e) { + throw new TechnicalException(e); + } + + return currency; + } + + @Override + public org.duniter.core.client.model.local.Currency update(final org.duniter.core.client.model.local.Currency currency) { + try { + + if (currency instanceof org.duniter.core.client.model.elasticsearch.Currency) { + fillTags((org.duniter.core.client.model.elasticsearch.Currency)currency); + } + + // Serialize into JSON + byte[] json = objectMapper.writeValueAsBytes(currency); + + UpdateRequestBuilder updateRequest = client.prepareUpdate(INDEX, RECORD_TYPE, currency.getId()) + .setDoc(json); + + // Execute indexBlocksFromNode + updateRequest + .setRefresh(true) + .execute(); + + } catch(JsonProcessingException e) { + throw new TechnicalException(e); + } + + + return currency; + } + + @Override + public void remove(final org.duniter.core.client.model.local.Currency currency) { + Preconditions.checkNotNull(currency); + Preconditions.checkArgument(StringUtils.isNotBlank(currency.getId())); + + // Delete the document + client.prepareDelete(INDEX, RECORD_TYPE, currency.getId()).execute().actionGet(); + } + + @Override + public org.duniter.core.client.model.local.Currency getById(String currencyId) { + return client.getSourceByIdOrNull(INDEX, RECORD_TYPE, currencyId, org.duniter.core.client.model.elasticsearch.Currency.class); + } + + @Override + public List<Currency> getCurrencies(long accountId) { + throw new TechnicalException("Not implemented yet"); + } + + @Override + public long getLastUD(String currencyId) { + org.duniter.core.client.model.local.Currency currency = getById(currencyId); + if (currency == null) { + return -1; + } + return currency.getLastUD(); + } + + @Override + public Map<Integer, Long> getAllUD(String currencyId) { + + throw new TechnicalException("Not implemented yet"); + } + + @Override + public void insertUDs(String currencyId, Map<Integer, Long> newUDs) { + throw new TechnicalException("Not implemented yet"); + } + + public boolean existsIndex() { + return client.existsIndex(INDEX); + } + + @Override + public XContentBuilder createTypeMapping() { + try { + XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(RECORD_TYPE) + .startObject("properties") + + // currency + .startObject("currency") + .field("type", "string") + .endObject() + + // firstBlockSignature + .startObject("firstBlockSignature") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // member count + .startObject("membersCount") + .field("type", "long") + .endObject() + + // lastUD + .startObject("lastUD") + .field("type", "long") + .endObject() + + // unitbase + .startObject("unitbase") + .field("type", "integer") + .endObject() + + // issuer + .startObject("issuer") + .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_TYPE, ioe.getMessage()), ioe); + } + } + + /* -- internal methods -- */ + + @Override + protected void 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_TYPE, createTypeMapping()); + createIndexRequestBuilder.execute().actionGet(); + } + + protected void fillTags(org.duniter.core.client.model.elasticsearch.Currency currency) { + String currencyName = currency.getCurrencyName(); + String[] tags = currencyName.split(REGEX_WORD_SEPARATOR); + List<String> tagsList = Lists.newArrayList(tags); + + // Convert as a sentence (replace separator with a space) + String sentence = currencyName.replaceAll(REGEX_WORD_SEPARATOR, " "); + if (!tagsList.contains(sentence)) { + tagsList.add(sentence); + } + + currency.setTags(tagsList.toArray(new String[tagsList.size()])); + } + +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/PeerDaoImpl.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/PeerDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..d80d3f0d0f34a47e3b8f53edd31330d5076f2767 --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/dao/impl/PeerDaoImpl.java @@ -0,0 +1,195 @@ +package org.duniter.elasticsearch.dao.impl; + +/* + * #%L + * UCoin Java :: 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 com.fasterxml.jackson.core.JsonProcessingException; +import org.duniter.core.client.dao.PeerDao; +import org.duniter.core.client.model.local.Peer; +import org.duniter.core.exception.TechnicalException; +import org.duniter.core.util.Preconditions; +import org.duniter.core.util.StringUtils; +import org.duniter.elasticsearch.dao.AbstractDao; +import org.duniter.elasticsearch.dao.TypeDao; +import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.update.UpdateRequestBuilder; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; + +import java.io.IOException; +import java.util.List; + +/** + * Created by blavenie on 29/12/15. + */ +public class PeerDaoImpl extends AbstractDao implements PeerDao, TypeDao<PeerDaoImpl> { + + + public static final String PEER_TYPE = "peer"; + + public PeerDaoImpl(){ + super("duniter.dao.peer"); + } + + @Override + public String getType() { + return PEER_TYPE; + } + + @Override + public Peer create(Peer peer) { + Preconditions.checkNotNull(peer); + Preconditions.checkArgument(StringUtils.isNotBlank(peer.getId())); + Preconditions.checkArgument(StringUtils.isNotBlank(peer.getCurrency())); + Preconditions.checkNotNull(peer.getHash()); + Preconditions.checkNotNull(peer.getHost()); + Preconditions.checkNotNull(peer.getApi()); + + // Serialize into JSON + // WARN: must use GSON, to have same JSON result (e.g identities and joiners field must be converted into String) + try { + String json = objectMapper.writeValueAsString(peer); + + // Preparing indexBlocksFromNode + IndexRequestBuilder indexRequest = client.prepareIndex(peer.getCurrency(), PEER_TYPE) + .setId(peer.getId()) + .setSource(json); + + // Execute indexBlocksFromNode + indexRequest + .setRefresh(true) + .execute(); + } + catch(JsonProcessingException e) { + throw new TechnicalException(e); + } + return peer; + } + + @Override + public Peer update(Peer peer) { + Preconditions.checkNotNull(peer); + Preconditions.checkArgument(StringUtils.isNotBlank(peer.getId())); + Preconditions.checkArgument(StringUtils.isNotBlank(peer.getCurrency())); + Preconditions.checkNotNull(peer.getHash()); + Preconditions.checkNotNull(peer.getHost()); + Preconditions.checkNotNull(peer.getApi()); + + // Serialize into JSON + try { + String json = objectMapper.writeValueAsString(peer); + + // Preparing indexBlocksFromNode + UpdateRequestBuilder updateRequest = client.prepareUpdate(peer.getCurrency(), PEER_TYPE, peer.getId()) + .setDoc(json); + + // Execute indexBlocksFromNode + updateRequest + .setRefresh(true) + .execute(); + } + catch(JsonProcessingException e) { + throw new TechnicalException(e); + } + return peer; + } + + @Override + public Peer getById(String id) { + throw new TechnicalException("not implemented"); + } + + @Override + public void remove(Peer peer) { + Preconditions.checkNotNull(peer); + Preconditions.checkArgument(StringUtils.isNotBlank(peer.getId())); + Preconditions.checkArgument(StringUtils.isNotBlank(peer.getCurrency())); + + // Delete the document + client.prepareDelete(peer.getCurrency(), PEER_TYPE, peer.getId()).execute().actionGet(); + } + + @Override + public List<Peer> getPeersByCurrencyId(String currencyId) { + throw new TechnicalException("no implemented: loading all peers may be unsafe for memory..."); + } + + @Override + public boolean isExists(String currencyId, String peerId) { + return client.isDocumentExists(currencyId, PEER_TYPE, peerId); + } + + @Override + public XContentBuilder createTypeMapping() { + try { + XContentBuilder mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject(PEER_TYPE) + .startObject("properties") + + // currency + .startObject("currency") + .field("type", "string") + .endObject() + + // pubkey + .startObject("pubkey") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // api + .startObject("api") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // uid + .startObject("uid") + .field("type", "string") + .endObject() + + // dns + .startObject("dns") + .field("type", "string") + .endObject() + + // ipv4 + .startObject("ipv4") + .field("type", "string") + .endObject() + + // ipv6 + .startObject("ipv6") + .field("type", "string") + .endObject() + + .endObject() + .endObject().endObject(); + + return mapping; + } + catch(IOException ioe) { + throw new TechnicalException("Error while getting mapping for peer index: " + ioe.getMessage(), ioe); + } + } +} 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 index 7957f63d1d1e7e2c7125b07f6cdd3d772796e5e9..fa523902afec82c88d27b34c4019b94cac7d168f 100644 --- 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 @@ -49,7 +49,7 @@ public class RestModule extends AbstractModule implements Module { bind(RestImageAttachmentAction.class).asEagerSingleton(); // Currency - bind(RestCurrencyIndexAction.class).asEagerSingleton(); + //bind(RestCurrencyIndexAction.class).asEagerSingleton(); } } \ No newline at end of file diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/currency/RestCurrencyIndexAction.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/currency/RestCurrencyIndexAction.java index d10d70e394561980b2655fa9e89733bf693c82e4..674b7e720af4139b95600cb7f05b832b30ed3e3e 100644 --- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/currency/RestCurrencyIndexAction.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/rest/currency/RestCurrencyIndexAction.java @@ -22,6 +22,7 @@ package org.duniter.elasticsearch.rest.currency; * #L% */ +import org.duniter.core.exception.TechnicalException; import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.service.CurrencyService; @@ -41,7 +42,9 @@ public class RestCurrencyIndexAction extends AbstractRestPostIndexAction { RestSecurityController securityController, CurrencyService currencyService) { super(settings, controller, client, securityController, CurrencyService.INDEX, CurrencyService.RECORD_TYPE, - (json) -> currencyService.indexCurrencyFromJson(json)); + (json) -> { + throw new TechnicalException("Not implemented yet");/*currencyService.indexCurrencyFromJson(json)*/ + }); } } \ No newline at end of file diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java index eb115021d5cb3c4d88d681423286a220c97c32bb..f4e61aacbed844b003bf4c0cae2552c1a27a28e3 100644 --- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java @@ -23,152 +23,91 @@ package org.duniter.elasticsearch.service; */ -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Joiner; import com.google.common.collect.ImmutableSet; -import org.apache.commons.collections4.MapUtils; import org.duniter.core.beans.Bean; import org.duniter.core.client.model.bma.jackson.JacksonUtils; import org.duniter.core.client.model.elasticsearch.Record; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; -import org.duniter.core.util.CollectionUtils; -import org.duniter.core.util.Preconditions; -import org.duniter.core.util.StringUtils; import org.duniter.elasticsearch.PluginSettings; -import org.duniter.elasticsearch.exception.AccessDeniedException; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.exception.InvalidFormatException; import org.duniter.elasticsearch.exception.InvalidSignatureException; -import org.duniter.elasticsearch.exception.NotFoundException; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequestBuilder; -import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequestBuilder; -import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse; -import org.elasticsearch.action.bulk.BulkItemResponse; -import org.elasticsearch.action.bulk.BulkRequest; -import org.elasticsearch.action.bulk.BulkRequestBuilder; -import org.elasticsearch.action.bulk.BulkResponse; -import org.elasticsearch.action.get.GetResponse; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.search.SearchPhaseExecutionException; -import org.elasticsearch.action.search.SearchRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.action.search.SearchType; import org.elasticsearch.client.Client; -import org.elasticsearch.client.Requests; -import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.Loggers; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.search.SearchHit; -import org.elasticsearch.search.SearchHitField; import org.nuiton.i18n.I18n; -import java.io.*; -import java.util.*; -import java.util.regex.Pattern; +import java.io.IOException; +import java.util.Set; /** * Created by Benoit on 08/04/2015. */ public abstract class AbstractService implements Bean { - protected final ESLogger logger; - protected final Client client; - protected final PluginSettings pluginSettings; protected final ObjectMapper objectMapper; - protected final CryptoService cryptoService; + + protected Duniter4jClient client; + protected PluginSettings pluginSettings; + protected CryptoService cryptoService; protected final int retryCount; protected final int retryWaitDuration; - public AbstractService(String loggerName, Client client, PluginSettings pluginSettings) { + public AbstractService(String loggerName, Duniter4jClient client, PluginSettings pluginSettings) { this(loggerName, client, pluginSettings, null); } - public AbstractService(Client client, PluginSettings pluginSettings) { + public AbstractService(Duniter4jClient client, PluginSettings pluginSettings) { this(client, pluginSettings, null); } - public AbstractService(Client client, PluginSettings pluginSettings, CryptoService cryptoService) { + public AbstractService(Duniter4jClient client, PluginSettings pluginSettings, CryptoService cryptoService) { this("duniter", client, pluginSettings, cryptoService); } - public AbstractService(String loggerName, Client client, PluginSettings pluginSettings, CryptoService cryptoService) { + public AbstractService(String loggerName, Duniter4jClient client, PluginSettings pluginSettings, CryptoService cryptoService) { + super(); this.logger = Loggers.getLogger(loggerName); this.client = client; + this.objectMapper = JacksonUtils.newObjectMapper(); this.pluginSettings = pluginSettings; this.cryptoService = cryptoService; - this.objectMapper = new ObjectMapper(); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); this.retryCount = pluginSettings.getNodeRetryCount(); this.retryWaitDuration = pluginSettings.getNodeRetryWaitDuration(); } - /* -- protected methods -- */ - - protected boolean existsIndex(String indexes) { - IndicesExistsRequestBuilder requestBuilder = client.admin().indices().prepareExists(indexes); - IndicesExistsResponse response = requestBuilder.execute().actionGet(); - return response.isExists(); - } - - protected void deleteIndexIfExists(String indexName){ - if (!existsIndex(indexName)) { - return; - } - if (logger.isInfoEnabled()) { - logger.info(String.format("Deleting index [%s]", indexName)); - } - - DeleteIndexRequestBuilder deleteIndexRequestBuilder = client.admin().indices().prepareDelete(indexName); - deleteIndexRequestBuilder.execute().actionGet(); - } - - protected String checkIssuerAndIndexDocumentFromJson(String index, String type, String json) { - - JsonNode actualObj = readAndVerifyIssuerSignature(json); - String issuer = getIssuer(actualObj); - + /* -- protected methods --*/ - if (logger.isDebugEnabled()) { - logger.debug(String.format("Indexing a %s from issuer [%s]", type, issuer.substring(0, 8))); - } - - return indexDocumentFromJson(index, type, json); - } - - protected String indexDocumentFromJson(String index, String type, String json) { - IndexResponse response = client.prepareIndex(index, type) - .setSource(json) - .setRefresh(true) - .execute().actionGet(); - return response.getId(); - } - protected void checkIssuerAndUpdateDocumentFromJson(String index, String type, String id, String json) { + protected <T> T executeWithRetry(RetryFunction<T> retryFunction) throws TechnicalException{ + int retry = 0; + while (retry < retryCount) { + try { + return retryFunction.execute(); + } catch (TechnicalException e) { + retry++; - JsonNode actualObj = readAndVerifyIssuerSignature(json); - String issuer = getIssuer(actualObj); + if (retry == retryCount) { + throw e; + } - // Check same document issuer - checkSameDocumentIssuer(index, type, id, issuer); + if (logger.isDebugEnabled()) { + logger.debug(I18n.t("duniter4j.removeServiceUtils.waitThenRetry", e.getMessage(), retry, retryCount)); + } - if (logger.isDebugEnabled()) { - logger.debug(String.format("Updating %s [%s] from issuer [%s]", type, id, issuer.substring(0, 8))); + try { + Thread.sleep(retryWaitDuration); // waiting + } catch (InterruptedException e2) { + throw new TechnicalException(e2); + } + } } - updateDocumentFromJson(index, type, id, json); - } - - protected void updateDocumentFromJson(String index, String type, String id, String json) { - // Execute indexBlocksFromNode - client.prepareUpdate(index, type, id) - .setRefresh(true) - .setDoc(json) - .execute().actionGet(); + throw new TechnicalException("Error while trying to execute a function with retry"); } protected JsonNode readAndVerifyIssuerSignature(String recordJson) throws ElasticsearchException { @@ -204,38 +143,6 @@ public abstract class AbstractService implements Bean { // TODO: check issuer is in the WOT ? } - protected void checkSameDocumentIssuer(String index, String type, String id, String expectedIssuer) throws ElasticsearchException { - checkSameDocumentField(index, type, id, Record.PROPERTY_ISSUER, expectedIssuer); - } - - protected void checkSameDocumentField(String index, String type, String id, String fieldName, String expectedvalue) throws ElasticsearchException { - - GetResponse response = client.prepareGet(index, type, id) - .setFields(fieldName) - .execute().actionGet(); - boolean failed = !response.isExists(); - if (failed) { - throw new NotFoundException(String.format("Document [%s/%s/%s] not exists.", index, type, id)); - } else { - String docValue = (String)response.getFields().get(fieldName).getValue(); - if (!Objects.equals(expectedvalue, docValue)) { - throw new AccessDeniedException(String.format("Could not delete this document: not same [%s].", fieldName)); - } - } - } - - protected boolean isDocumentExists(String index, String type, String id) throws ElasticsearchException { - GetResponse response = client.prepareGet(index, type, id) - .setFetchSource(false) - .execute().actionGet(); - return response.isExists(); - } - - protected void checkDocumentExists(String index, String type, String id) throws ElasticsearchException { - if (!isDocumentExists(index, type, id)) { - throw new NotFoundException(String.format("Document [%s/%s/%s] not exists.", index, type, id)); - } - } protected String getIssuer(JsonNode actualObj) { return getMandatoryField(actualObj, Record.PROPERTY_ISSUER).asText(); @@ -249,360 +156,8 @@ public abstract class AbstractService implements Bean { return value; } - /** - * Retrieve some field from a document id, and check if all field not null - * @param docId - * @return - */ - protected Map<String, Object> getMandatoryFieldsById(String index, String type, String docId, String... fieldNames) { - Map<String, Object> fields = getFieldsById(index, type, docId, fieldNames); - if (MapUtils.isEmpty(fields)) throw new NotFoundException(String.format("Document [%s/%s/%s] not exists.", index, type, docId)); - Arrays.stream(fieldNames).forEach((fieldName) -> { - if (!fields.containsKey(fieldName)) throw new NotFoundException(String.format("Document [%s/%s/%s] should have the mandatory field [%s].", index, type, docId, fieldName)); - }); - return fields; - } - - /** - * Retrieve some field from a document id - * @param docId - * @return - */ - protected Map<String, Object> getFieldsById(String index, String type, String docId, String... fieldNames) { - // Prepare request - SearchRequestBuilder searchRequest = client - .prepareSearch(index) - .setTypes(type) - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH); - - searchRequest.setQuery(QueryBuilders.idsQuery().ids(docId)); - searchRequest.addFields(fieldNames); - - // Execute query - try { - SearchResponse response = searchRequest.execute().actionGet(); - - Map<String, Object> result = new HashMap<>(); - // Read query result - SearchHit[] searchHits = response.getHits().getHits(); - for (SearchHit searchHit : searchHits) { - Map<String, SearchHitField> hitFields = searchHit.getFields(); - for(String fieldName: hitFields.keySet()) { - result.put(fieldName, hitFields.get(fieldName).getValue()); - } - break; - } - return result; - } - catch(SearchPhaseExecutionException e) { - // Failed or no item on index - throw new TechnicalException(String.format("[%s/%s] Unable to retrieve fields [%s] for id [%s]", - index, type, - Joiner.on(',').join(fieldNames).toString(), - docId), e); - } - } - - /** - * Retrieve some field from a document id - * @param index - * @param type - * @param ids - * @param fieldName - * @return - */ - protected Map<String, Object> getFieldByIds(String index, String type, Set<String> ids, String fieldName) { - // Prepare request - SearchRequestBuilder searchRequest = client - .prepareSearch(index) - .setTypes(type) - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH); - - searchRequest.setQuery(QueryBuilders.idsQuery().ids(ids)); - searchRequest.addFields(fieldName); - - // Execute query - try { - SearchResponse response = searchRequest.execute().actionGet(); - - Map<String, Object> result = new HashMap<>(); - // Read query result - SearchHit[] searchHits = response.getHits().getHits(); - for (SearchHit searchHit : searchHits) { - Map<String, SearchHitField> hitFields = searchHit.getFields(); - if (hitFields.get(fieldName) != null) { - result.put(searchHit.getId(), hitFields.get(fieldName).getValue()); - } - } - return result; - } - catch(SearchPhaseExecutionException e) { - // Failed or no item on index - throw new TechnicalException(String.format("[%s/%s] Unable to retrieve field [%s] for ids [%s]", - index, type, fieldName, - Joiner.on(',').join(ids).toString()), e); - } - } - - /** - * Retrieve a field from a document id - * @param docId - * @return - */ - protected Object getFieldById(String index, String type, String docId, String fieldName) { - - Map<String, Object> result = getFieldsById(index, type, docId, fieldName); - if (MapUtils.isEmpty(result)) { - return null; - } - return result.get(fieldName); - } - - protected <T> T getTypedFieldById(String index, String type, String docId, String fieldName) { - return (T)getFieldById(index, type, docId, fieldName); - } - - /** - * Retrieve a document by id (safe mode) - * @param docId - * @return - */ - public <T extends Object> T getSourceByIdOrNull(String index, String type, String docId, Class<T> classOfT, String... fieldNames) { - try { - return getSourceById(index, type, docId, classOfT, fieldNames); - } - catch(TechnicalException e) { - return null; // not found - } - } - - /** - * Retrieve a document by id - * @param docId - * @return - */ - public <T extends Object> T getSourceById(String index, String type, String docId, Class<T> classOfT, String... fieldNames) { - - // Prepare request - SearchRequestBuilder searchRequest = client - .prepareSearch(index) - .setSearchType(SearchType.QUERY_AND_FETCH); - - searchRequest.setQuery(QueryBuilders.idsQuery(type).ids(docId)); - if (CollectionUtils.isNotEmpty(fieldNames)) { - searchRequest.setFetchSource(fieldNames, null); - } - else { - searchRequest.setFetchSource(true); // full source - } - - // Execute query - try { - SearchResponse response = searchRequest.execute().actionGet(); - - if (response.getHits().getTotalHits() == 0) return null; - - // Read query result - SearchHit[] searchHits = response.getHits().getHits(); - - for (SearchHit searchHit : searchHits) { - if (searchHit.source() != null) { - return objectMapper.readValue(searchHit.source(), classOfT); - } - break; - } - return null; - } - catch(SearchPhaseExecutionException | IOException e) { - // Failed to get source - throw new TechnicalException(String.format("[%s/%s] Error while getting [%s]", - index, type, - docId), e); - } - } - - protected void bulkFromClasspathFile(String classpathFile, String indexName, String indexType) { - bulkFromClasspathFile(classpathFile, indexName, indexType, null); - } - - protected void bulkFromClasspathFile(String classpathFile, String indexName, String indexType, StringReaderHandler handler) { - InputStream is = null; - try { - is = getClass().getClassLoader().getResourceAsStream(classpathFile); - if (is == null) { - throw new TechnicalException(String.format("Could not retrieve data file [%s] need to fill index [%s]: ", classpathFile, indexName)); - } - - bulkFromStream(is, indexName, indexType, handler); - } - finally { - if (is != null) { - try { - is.close(); - } - catch(IOException e) { - // Silent is gold - } - } - } - } - - protected void bulkFromFile(File file, String indexName, String indexType) { - bulkFromFile(file, indexName, indexType, null); - } - - protected void bulkFromFile(File file, String indexName, String indexType, StringReaderHandler handler) { - Preconditions.checkNotNull(file); - Preconditions.checkArgument(file.exists()); - - InputStream is = null; - try { - is = new BufferedInputStream(new FileInputStream(file)); - bulkFromStream(is, indexName, indexType, handler); - } - catch(FileNotFoundException e) { - throw new TechnicalException(String.format("[%s] Could not find file %s", indexName, file.getPath()), e); - } - finally { - if (is != null) { - try { - is.close(); - } - catch(IOException e) { - // Silent is gold - } - } - } - } - - protected void bulkFromStream(InputStream is, String indexName, String indexType) { - bulkFromStream(is, indexName, indexType, null); - } - - protected void bulkFromStream(InputStream is, String indexName, String indexType, StringReaderHandler handler) { - Preconditions.checkNotNull(is); - BulkRequest bulkRequest = Requests.bulkRequest(); - - BufferedReader br = null; - - try { - br = new BufferedReader(new InputStreamReader(is)); - - String line = br.readLine(); - StringBuilder builder = new StringBuilder(); - while(line != null) { - line = line.trim(); - if (StringUtils.isNotBlank(line)) { - if (logger.isTraceEnabled()) { - logger.trace(String.format("[%s] Add to bulk: %s", indexName, line)); - } - if (handler != null) { - line = handler.onReadLine(line.trim()); - } - builder.append(line).append('\n'); - } - line = br.readLine(); - } - - byte[] data = builder.toString().getBytes(); - bulkRequest.add(new BytesArray(data), indexName, indexType, false); - - } catch(Exception e) { - throw new TechnicalException(String.format("[%s] Error while inserting rows into %s", indexName, indexType), e); - } - finally { - if (br != null) { - try { - br.close(); - } - catch(IOException e) { - // Silent is gold - } - } - } - - try { - client.bulk(bulkRequest).actionGet(); - } catch(Exception e) { - throw new TechnicalException(String.format("[%s] Error while inserting rows into %s", indexName, indexType), e); - } - } - - protected void flushDeleteBulk(final String index, final String type, BulkRequestBuilder bulkRequest) { - if (bulkRequest.numberOfActions() > 0) { - - BulkResponse bulkResponse = bulkRequest.execute().actionGet(); - // If failures, continue but save missing blocks - if (bulkResponse.hasFailures()) { - // process failures by iterating through each bulk response item - for (BulkItemResponse itemResponse : bulkResponse) { - boolean skip = !itemResponse.isFailed(); - if (!skip) { - logger.debug(String.format("[%s/%s] Error while deleting doc [%s]: %s. Skipping this deletion.", index, type, itemResponse.getId(), itemResponse.getFailureMessage())); - } - } - } - } - } - - public interface StringReaderHandler { - - String onReadLine(String line); - } - - public class AddSequenceAttributeHandler implements StringReaderHandler { - private int order; - private final String attributeName; - private final Pattern filterPattern; - public AddSequenceAttributeHandler(String attributeName, String filterRegex, int startValue) { - this.order = startValue; - this.attributeName = attributeName; - this.filterPattern = Pattern.compile(filterRegex); - } - - @Override - public String onReadLine(String line) { - // add 'order' field into - if (filterPattern.matcher(line).matches()) { - return String.format("%s, \"%s\": %d}", - line.substring(0, line.length()-1), - attributeName, - order++); - } - return line; - } - } - - protected <T> T executeWithRetry(RetryFunction<T> retryFunction) throws TechnicalException{ - int retry = 0; - while (retry < retryCount) { - try { - return retryFunction.execute(); - } catch (TechnicalException e) { - retry++; - - if (retry == retryCount) { - throw e; - } - - if (logger.isDebugEnabled()) { - logger.debug(I18n.t("duniter4j.removeServiceUtils.waitThenRetry", e.getMessage(), retry, retryCount)); - } - - try { - Thread.sleep(retryWaitDuration); // waiting - } catch (InterruptedException e2) { - throw new TechnicalException(e2); - } - } - } - - throw new TechnicalException("Error while trying to execute a function with retry"); - } - public interface RetryFunction<T> { T execute() throws TechnicalException; } - } diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractSynchroService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractSynchroService.java index 1ef7130bf95d048234d75e52c6dabf7f2909ee56..906872b71d511febf4b82d2836ead95cf3a80b86 100644 --- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractSynchroService.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractSynchroService.java @@ -36,6 +36,7 @@ import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; import org.duniter.core.util.StringUtils; import org.duniter.elasticsearch.PluginSettings; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.exception.InvalidFormatException; import org.duniter.elasticsearch.model.SynchroResult; import org.duniter.elasticsearch.service.AbstractService; @@ -66,7 +67,9 @@ public abstract class AbstractSynchroService extends AbstractService { protected HttpService httpService; @Inject - public AbstractSynchroService(Client client, PluginSettings settings, CryptoService cryptoService, + public AbstractSynchroService(Duniter4jClient client, + PluginSettings settings, + CryptoService cryptoService, ThreadPool threadPool, final ServiceLocator serviceLocator) { super("duniter.network.p2p", client, settings,cryptoService); threadPool.scheduleOnStarted(() -> { @@ -78,7 +81,7 @@ public abstract class AbstractSynchroService extends AbstractService { 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()); + Peer peer = Peer.newBuilder().setHost(pluginSettings.getDataSyncHost()).setPort(pluginSettings.getDataSyncPort()).build(); return peer; } diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java index b4b67c3cec7e95bdaca7de9db55f429cf114d4bf..b1934f161ef9523a2542c848d7a0374e1ae41aed 100644 --- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java @@ -23,7 +23,6 @@ package org.duniter.elasticsearch.service; */ -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; @@ -32,12 +31,12 @@ import org.duniter.core.client.model.bma.BlockchainBlock; import org.duniter.core.client.model.bma.BlockchainParameters; import org.duniter.core.client.model.bma.EndpointApi; import org.duniter.core.client.model.local.Peer; +import org.duniter.core.util.ObjectUtils; import org.duniter.core.util.json.JsonAttributeParser; import org.duniter.core.client.model.bma.jackson.JacksonUtils; 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.util.json.JsonSyntaxException; import org.duniter.core.exception.TechnicalException; import org.duniter.core.model.NullProgressionModel; import org.duniter.core.model.ProgressionModel; @@ -47,30 +46,16 @@ import org.duniter.core.util.Preconditions; import org.duniter.core.util.StringUtils; import org.duniter.core.util.websocket.WebsocketClientEndpoint; import org.duniter.elasticsearch.PluginSettings; +import org.duniter.elasticsearch.client.Duniter4jClient; +import org.duniter.elasticsearch.dao.BlockDao; +import org.duniter.elasticsearch.dao.impl.BlockDaoImpl; import org.duniter.elasticsearch.exception.DuplicateIndexIdException; import org.duniter.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.action.ActionFuture; -import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.bulk.BulkItemResponse; import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkResponse; -import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.search.SearchRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.action.search.SearchType; import org.elasticsearch.client.Client; -import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.common.inject.Inject; -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.SearchHitField; -import org.elasticsearch.search.aggregations.AggregationBuilders; -import org.elasticsearch.search.aggregations.metrics.max.Max; -import org.elasticsearch.search.highlight.HighlightField; -import org.elasticsearch.search.sort.SortOrder; import org.nuiton.i18n.I18n; import java.io.IOException; @@ -100,14 +85,19 @@ public class BlockchainService extends AbstractService { private final JsonAttributeParser blockHashParser = new JsonAttributeParser("hash"); private final JsonAttributeParser blockPreviousHashParser = new JsonAttributeParser("previousHash"); - private ObjectMapper objectMapper; + private Client client; + private BlockDao blockDao; @Inject - public BlockchainService(Client client, PluginSettings settings, ThreadPool threadPool, + public BlockchainService(Duniter4jClient client, + PluginSettings settings, + ThreadPool threadPool, + BlockDao blockDao, final ServiceLocator serviceLocator){ super("duniter.blockchain", client, settings); - this.objectMapper = JacksonUtils.newObjectMapper(); this.threadPool = threadPool; + this.client = client; + this.blockDao = blockDao; threadPool.scheduleOnStarted(() -> { blockchainRemoteService = serviceLocator.getBlockchainRemoteService(); }); @@ -140,7 +130,7 @@ public class BlockchainService extends AbstractService { } public BlockchainService listenAndIndexNewBlock(final Peer peer){ - WebsocketClientEndpoint wsEndPoint = blockchainRemoteService.addBlockListener(peer, message -> indexLastBlockFromJson(peer, message)); + WebsocketClientEndpoint wsEndPoint = blockchainRemoteService.addBlockListener(peer, message -> indexLastBlockFromJson(peer, message), true /*autoreconnect*/); wsEndPoint.registerListener(dispatchConnectionListener); return this; } @@ -170,14 +160,6 @@ public class BlockchainService extends AbstractService { progressionModel.setTask(I18n.t("duniter4j.blockIndexerService.indexLastBlocks.task", currencyName, peer)); logger.info(I18n.t("duniter4j.blockIndexerService.indexLastBlocks.task", currencyName, peer)); - // Create index blockchain if need - if (!currencyService.isCurrencyExists(currencyName)) { - currencyService.indexCurrencyFromPeer(peer); - } - - // Check if index exists - createIndexIfNotExists(currencyName, true/*wait cluster health*/); - // Then index allOfToList blocks BlockchainBlock peerCurrentBlock = blockchainRemoteService.getCurrentBlock(peer); @@ -205,7 +187,7 @@ public class BlockchainService extends AbstractService { // When current block not found, // try to use the max(number), because block with _id='current' may not has been indexed if (startNumber <= 1 ){ - startNumber = getMaxBlockNumber(currencyName) + 1; + startNumber = blockDao.getMaxBlockNumber(currencyName) + 1; } // If some block has been already indexed: detect and resolve fork @@ -262,42 +244,6 @@ public class BlockchainService extends AbstractService { return this; } - public BlockchainService deleteIndex(String currencyName) { - deleteIndexIfExists(currencyName); - return this; - } - - public boolean existsIndex(String currencyName) { - return super.existsIndex(currencyName); - } - - public BlockchainService createIndexIfNotExists(String currencyName, boolean waitClusterHealth) { - if (!existsIndex(currencyName)) { - createIndex(currencyName); - - // when wait cluster state - if (waitClusterHealth) { - threadPool.waitClusterHealthStatus(ClusterHealthStatus.GREEN, ClusterHealthStatus.YELLOW); - } - } - return this; - } - - public void createIndex(String currencyName) { - logger.info(String.format("Creating index [%s]", currencyName)); - - CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(currencyName); - org.elasticsearch.common.settings.Settings indexSettings = org.elasticsearch.common.settings.Settings.settingsBuilder() - .put("number_of_shards", 1) - .put("number_of_replicas", 1) - //.put("analyzer", createDefaultAnalyzer()) - .build(); - createIndexRequestBuilder.setSettings(indexSettings); - createIndexRequestBuilder.addMapping(BLOCK_TYPE, createBlockTypeMapping()); - createIndexRequestBuilder.addMapping(PEER_TYPE, EndpointService.createEndpointTypeMapping()); - createIndexRequestBuilder.execute().actionGet(); - } - /** * Create or update a block, depending on its existence and hash * @param block @@ -311,7 +257,7 @@ public class BlockchainService extends AbstractService { Preconditions.checkNotNull(block.getNumber(), "block attribute 'number' could not be null"); Preconditions.checkNotNull(block.getHash(), "block attribute 'hash' could not be null"); - BlockchainBlock existingBlock = getBlockById(block.getCurrency(), block.getNumber()); + BlockchainBlock existingBlock = blockDao.getBlockById(block.getCurrency(), getBlockId(block.getNumber())); // Currency not exists, or has changed, so create it if (existingBlock == null) { @@ -320,12 +266,12 @@ public class BlockchainService extends AbstractService { } // Create new block - indexBlock(block, wait); + blockDao.create(block, wait); } // Exists, so check the owner signature else { - boolean doUpdate = false; + boolean doUpdate; if (updateWhenSameHash) { doUpdate = true; if (logger.isTraceEnabled() && doUpdate) { @@ -346,68 +292,9 @@ public class BlockchainService extends AbstractService { // Update existing block if (doUpdate) { - indexBlock(block, wait); - } - } - } - - public void indexBlock(BlockchainBlock block, boolean wait) { - Preconditions.checkNotNull(block); - Preconditions.checkArgument(StringUtils.isNotBlank(block.getCurrency())); - Preconditions.checkNotNull(block.getHash()); - Preconditions.checkNotNull(block.getNumber()); - - // Serialize into JSON - // WARN: must use GSON, to have same JSON result (e.g identities and joiners field must be converted into String) - try { - String json = objectMapper.writeValueAsString(block); - - // Preparing indexBlocksFromNode - IndexRequestBuilder indexRequest = client.prepareIndex(block.getCurrency(), BLOCK_TYPE) - .setId(block.getNumber().toString()) - .setSource(json); - - // Execute indexBlocksFromNode - ActionFuture<IndexResponse> futureResponse = indexRequest - .setRefresh(true) - .execute(); - - if (wait) { - futureResponse.actionGet(); + blockDao.update(block, wait); } } - catch(JsonProcessingException e) { - throw new TechnicalException(e); - } - - - } - - /** - * - * @param currencyName - * @param number the block number - * @param json block as JSON - */ - public BlockchainService indexBlockFromJson(String currencyName, int number, byte[] json, boolean refresh, boolean wait) { - Preconditions.checkNotNull(json); - Preconditions.checkArgument(json.length > 0); - - // Preparing indexBlocksFromNode - IndexRequestBuilder indexRequest = client.prepareIndex(currencyName, BLOCK_TYPE) - .setId(String.valueOf(number)) - .setRefresh(refresh) - .setSource(json); - - // Execute indexBlocksFromNode - if (!wait) { - indexRequest.execute(); - } - else { - indexRequest.execute().actionGet(); - } - - return this; } /** @@ -419,7 +306,7 @@ public class BlockchainService extends AbstractService { Preconditions.checkNotNull(json); Preconditions.checkArgument(json.length() > 0); - indexBlockFromJson(peer, json, true /*refresh*/, true /*is current*/, true/*check fork*/, true/*wait*/); + indexBlockFromJson(peer, json, true /*is current*/, true/*check fork*/, true/*wait*/); return this; } @@ -427,10 +314,9 @@ public class BlockchainService extends AbstractService { /** * * @param json block as json - * @param refresh Could be an existing block ? * @param wait need to wait until processed ? */ - public BlockchainService indexBlockFromJson(Peer peer, String json, boolean refresh, boolean isCurrent, boolean detectFork, boolean wait) { + public BlockchainService indexBlockFromJson(Peer peer, String json, boolean isCurrent, boolean detectFork, boolean wait) { Preconditions.checkNotNull(json); Preconditions.checkArgument(json.length() > 0); @@ -455,18 +341,7 @@ public class BlockchainService extends AbstractService { } // Preparing indexBlocksFromNode - IndexRequestBuilder indexRequest = client.prepareIndex(currencyName, BLOCK_TYPE) - .setId(String.valueOf(number)) - .setRefresh(refresh) - .setSource(json); - - // Execute indexBlocksFromNode - if (!wait) { - indexRequest.execute(); - } - else { - indexRequest.execute().actionGet(); - } + blockDao.create(currencyName, getBlockId(number), json.getBytes(), wait); // Update current if (isCurrent) { @@ -510,239 +385,25 @@ public class BlockchainService extends AbstractService { Preconditions.checkArgument(StringUtils.isNotBlank(currencyName)); // Preparing indexBlocksFromNode - IndexRequestBuilder indexRequest = client.prepareIndex(currencyName, BLOCK_TYPE) - .setId(CURRENT_BLOCK_ID) - .setRefresh(true) - .setSource(json); - - // Execute in a pool - if (!wait) { - boolean acceptedInPool = false; - while(!acceptedInPool) - try { - indexRequest.execute(); - acceptedInPool = true; - } - catch(EsRejectedExecutionException e) { - // not accepted, so wait - try { - Thread.sleep(1000); // 1s - } - catch(InterruptedException e2) { - // silent - } - } - - } else { - indexRequest.execute().actionGet(); + if (blockDao.isExists(currencyName, CURRENT_BLOCK_ID)) { + blockDao.update(currencyName, CURRENT_BLOCK_ID, json.getBytes(), wait); } - } - - public List<BlockchainBlock> findBlocksByHash(String currencyName, String query) { - String[] queryParts = query.split("[\\t ]+"); - - // Prepare request - SearchRequestBuilder searchRequest = client - .prepareSearch(currencyName) - .setTypes(BLOCK_TYPE) - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH); - - // If only one term, search as prefix - if (queryParts.length == 1) { - searchRequest.setQuery(QueryBuilders.prefixQuery("hash", query)); - } - - // If more than a word, search on terms match else { - searchRequest.setQuery(QueryBuilders.matchQuery("hash", query)); - } - - // Sort as score/memberCount - searchRequest.addSort("_score", SortOrder.DESC) - .addSort("number", SortOrder.DESC); - - // Highlight matched words - searchRequest.setHighlighterTagsSchema("styled") - .addHighlightedField("hash") - .addFields("hash") - .addFields("*", "_source"); - - // Execute query - SearchResponse searchResponse = searchRequest.execute().actionGet(); - - // Read query result - return toBlocks(searchResponse, true); - } - - public int getMaxBlockNumber(String currencyName) { - // Prepare request - SearchRequestBuilder searchRequest = client - .prepareSearch(currencyName) - .setTypes(BLOCK_TYPE) - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH); - - // Get max(number) - searchRequest.addAggregation(AggregationBuilders.max("max_number").field("number")); - - // Execute query - SearchResponse searchResponse = searchRequest.execute().actionGet(); - - // Read query result - Max result = searchResponse.getAggregations().get("max_number"); - if (result == null) { - return -1; + blockDao.create(currencyName, CURRENT_BLOCK_ID, json.getBytes(), wait); } - - return (result.getValue() == Double.NEGATIVE_INFINITY) - ? -1 - : (int)result.getValue(); } public BlockchainBlock getBlockById(String currencyName, int number) { - return getBlockByIdStr(currencyName, String.valueOf(number)); + return blockDao.getBlockById(currencyName, String.valueOf(number)); } public BlockchainBlock getCurrentBlock(String currencyName) { - return getBlockByIdStr(currencyName, CURRENT_BLOCK_ID); + return blockDao.getBlockById(currencyName, CURRENT_BLOCK_ID); } /* -- Internal methods -- */ - - public XContentBuilder createBlockTypeMapping() { - try { - XContentBuilder mapping = XContentFactory.jsonBuilder() - .startObject() - .startObject(BLOCK_TYPE) - .startObject("properties") - - // block number - .startObject("number") - .field("type", "integer") - .endObject() - - // hash - .startObject("hash") - .field("type", "string") - .endObject() - - // issuer - .startObject("issuer") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // previous hash - .startObject("previousHash") - .field("type", "string") - .endObject() - - // membercount - .startObject("memberCount") - .field("type", "integer") - .endObject() - - // membersChanges - .startObject("membersChanges") - .field("type", "string") - .endObject() - - // unitbase - .startObject("unitbase") - .field("type", "integer") - .endObject() - - // membersChanges - .startObject("monetaryMass") - .field("type", "long") - .endObject() - - // dividend - .startObject("dividend") - .field("type", "integer") - .endObject() - - // identities: - //.startObject("identities") - //.endObject() - - .endObject() - .endObject().endObject(); - - return mapping; - } - catch(IOException ioe) { - throw new TechnicalException("Error while getting mapping for block index: " + ioe.getMessage(), ioe); - } - } - - - public BlockchainBlock getBlockByIdStr(String currencyName, String blockId) { - - // Prepare request - SearchRequestBuilder searchRequest = client - .prepareSearch(currencyName) - .setTypes(BLOCK_TYPE) - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH); - - // If more than a word, search on terms match - searchRequest.setQuery(QueryBuilders.matchQuery("_id", blockId)); - - // Execute query - try { - SearchResponse searchResponse = searchRequest.execute().actionGet(); - List<BlockchainBlock> blocks = toBlocks(searchResponse, false); - if (CollectionUtils.isEmpty(blocks)) { - return null; - } - - // Return the unique result - return CollectionUtils.extractSingleton(blocks); - } - catch(JsonSyntaxException e) { - throw new TechnicalException(String.format("Error while getting indexed block #%s for blockchain [%s]", blockId, currencyName), e); - } - - } - - protected List<BlockchainBlock> toBlocks(SearchResponse response, boolean withHighlight) { - // Read query result - List<BlockchainBlock> result = Lists.newArrayList(); - response.getHits().forEach(searchHit -> { - BlockchainBlock block; - if (searchHit.source() != null) { - String jsonString = new String(searchHit.source()); - try { - block = objectMapper.readValue(jsonString, BlockchainBlock.class); - } catch(Exception e) { - if (logger.isDebugEnabled()) { - logger.debug("Error while parsing block from JSON:\n" + jsonString); - } - throw new JsonSyntaxException("Error while read block from JSON: " + e.getMessage(), e); - } - } - else { - block = new BlockchainBlock(); - SearchHitField field = searchHit.getFields().get("hash"); - block.setHash(field.getValue()); - } - result.add(block); - - // If possible, use highlights - if (withHighlight) { - Map<String, HighlightField> fields = searchHit.getHighlightFields(); - for (HighlightField field : fields.values()) { - String blockNameHighLight = field.getFragments()[0].string(); - block.setHash(blockNameHighLight); - } - } - }); - - return result; - } - - - public Collection<String> indexBlocksNoBulk(Peer peer, String currencyName, int firstNumber, int lastNumber, ProgressionModel progressionModel) { + protected Collection<String> indexBlocksNoBulk(Peer peer, String currencyName, int firstNumber, int lastNumber, ProgressionModel progressionModel) { Set<String> missingBlockNumbers = new LinkedHashSet<>(); for (int curNumber = firstNumber; curNumber <= lastNumber; curNumber++) { @@ -763,7 +424,7 @@ public class BlockchainService extends AbstractService { try { String blockAsJson = blockchainRemoteService.getBlockAsJson(peer, curNumber); - indexBlockFromJson(currencyName, curNumber, blockAsJson.getBytes(), false, true /*wait*/); + blockDao.create(currencyName, getBlockId(curNumber), blockAsJson.getBytes(), true /*wait*/); // If last block if (curNumber == lastNumber - 1) { @@ -780,7 +441,7 @@ public class BlockchainService extends AbstractService { return missingBlockNumbers; } - public Collection<String> indexBlocksUsingBulk(Peer peer, String currencyName, int firstNumber, int lastNumber, ProgressionModel progressionModel) { + protected Collection<String> indexBlocksUsingBulk(Peer peer, String currencyName, int firstNumber, int lastNumber, ProgressionModel progressionModel) { Set<String> missingBlockNumbers = new LinkedHashSet<>(); boolean debug = logger.isDebugEnabled(); @@ -954,7 +615,7 @@ public class BlockchainService extends AbstractService { } // Index the missing block - indexBlockFromJson(currencyName, blockNumber, blockAsJson.getBytes(), false/*refresh*/, true/*wait*/); + blockDao.create(currencyName, getBlockId(blockNumber), blockAsJson.getBytes(), true/*wait*/); // Remove this block number from the final missing list newMissingBlocks.remove(blockNumber); @@ -1018,13 +679,15 @@ public class BlockchainService extends AbstractService { } protected boolean isBlockIndexed(String currencyName, int number, String hash) { + Preconditions.checkNotNull(currencyName); + Preconditions.checkNotNull(hash); // Check if previous block exists - BlockchainBlock block = getBlockByIdStr(currencyName, String.valueOf(number)); + BlockchainBlock block = getBlockById(currencyName, number); boolean blockExists = block != null; if (!blockExists) { return blockExists; } - return block.getHash() != null && block.getHash().equals(hash); + return ObjectUtils.equals(block.getHash(), hash); } protected boolean detectAndResolveFork(Peer peer, final String currencyName, final String hash, final int number){ @@ -1064,7 +727,7 @@ public class BlockchainService extends AbstractService { if (forkOriginNumber < number) { logger.info(I18n.t("duniter4j.blockIndexerService.detectFork.resync", currencyName, peer, forkOriginNumber)); // Remove some previous block - deleteBlocksFromNumber(currencyName, forkOriginNumber/*from*/, number+forkResyncWindow/*to*/); + blockDao.deleteRange(currencyName, forkOriginNumber/*from*/, number+forkResyncWindow/*to*/); // Re-indexing blocks indexBlocksUsingBulk(peer, currencyName, forkOriginNumber/*from*/, number, nullProgressionModel); @@ -1073,32 +736,8 @@ public class BlockchainService extends AbstractService { return true; // sync OK } - /** - * Delete blocks from a start number (using bulk) - * @param currencyName - * @param fromNumber - */ - protected void deleteBlocksFromNumber(final String currencyName, final int fromNumber, final int toNumber) { - - int bulkSize = pluginSettings.getIndexBulkSize(); - - BulkRequestBuilder bulkRequest = client.prepareBulk(); - for (int number=fromNumber; number<=toNumber; number++) { - bulkRequest.add( - client.prepareDelete(currencyName, BLOCK_TYPE, String.valueOf(number)) - ); - - // Flush the bulk if not empty - if ((fromNumber - number % bulkSize) == 0) { - flushDeleteBulk(currencyName, BLOCK_TYPE, bulkRequest); - bulkRequest = client.prepareBulk(); - } - } - - // last flush - flushDeleteBulk(currencyName, BLOCK_TYPE, bulkRequest); + protected String getBlockId(int number) { + return number == -1 ? CURRENT_BLOCK_ID : String.valueOf(number); } - - } diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/CurrencyService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/CurrencyService.java index f1d2d180139ca509a36c34f1a2fa93cbeba3dd06..f0cdcd627c60230f010300fb423b38192608a522 100644 --- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/CurrencyService.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/CurrencyService.java @@ -24,9 +24,8 @@ package org.duniter.elasticsearch.service; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Lists; -import org.apache.commons.lang3.ArrayUtils; +import org.duniter.core.client.dao.CurrencyDao; +import org.duniter.core.client.dao.PeerDao; import org.duniter.core.client.model.bma.BlockchainBlock; import org.duniter.core.client.model.bma.BlockchainParameters; import org.duniter.core.client.model.bma.jackson.JacksonUtils; @@ -37,27 +36,27 @@ import org.duniter.core.client.service.exception.HttpConnectException; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; import org.duniter.core.util.Preconditions; -import org.duniter.core.util.StringUtils; import org.duniter.elasticsearch.PluginSettings; +import org.duniter.elasticsearch.client.Duniter4jClient; +import org.duniter.elasticsearch.dao.*; 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.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.inject.Injector; import org.elasticsearch.index.query.IdsQueryBuilder; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHitField; import java.io.IOException; -import java.util.List; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; /** @@ -65,85 +64,39 @@ import java.util.Objects; */ public class CurrencyService extends AbstractService { - protected static final String REGEX_WORD_SEPARATOR = "[-\\t@# _]+"; - public static final String INDEX = "currency"; public static final String RECORD_TYPE = "record"; - public static final String PEER_TYPE = "peer"; - private final ObjectMapper objectMapper; private BlockchainRemoteService blockchainRemoteService; + private CurrencyExtendDao currencyDao; + private Map<String, IndexDao<?>> currencyDataDaos = new HashMap<>(); + private Injector injector; @Inject - public CurrencyService(Client client, + public CurrencyService(Duniter4jClient client, PluginSettings settings, CryptoService cryptoService, - BlockchainRemoteService blockchainRemoteService) { + CurrencyDao currencyDao, + BlockchainRemoteService blockchainRemoteService, + Injector injector) { super("duniter." + INDEX, client, settings, cryptoService); - this.objectMapper = JacksonUtils.newObjectMapper(); this.blockchainRemoteService = blockchainRemoteService; + this.currencyDao = (CurrencyExtendDao)currencyDao; + this.injector = injector; } - /** - * Create index need for blockchain registry, if need - */ public CurrencyService createIndexIfNotExists() { - try { - if (!existsIndex(INDEX)) { - createIndex(); - } - } - catch(JsonProcessingException e) { - throw new TechnicalException(String.format("Error while creating index [%s]", INDEX)); - } - return this; - } - - /** - * Create index for registry - * @throws JsonProcessingException - */ - public CurrencyService 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_TYPE, createRecordTypeMapping()); - createIndexRequestBuilder.execute().actionGet(); - + currencyDao.createIndexIfNotExists(); return this; } public CurrencyService deleteIndex() { - deleteIndexIfExists(INDEX); + currencyDao.deleteIndex(); return this; } - public boolean existsIndex() { - return super.existsIndex(INDEX); - } - public boolean isCurrencyExists(String currencyName) { - String pubkey = getSenderPubkeyByCurrencyId(currencyName); - return !StringUtils.isEmpty(pubkey); - } - - - /** - * Add a new currency - * TODO : - * - add security, to allow only request from admin (check signature against settings keyring) - * - * @param json - * @return - */ - public String indexCurrencyFromJson(String json) { - throw new TechnicalException("Not implemented yet. Received JSON: " + json); + return currencyDao.isExists(currencyName); } /** @@ -186,104 +139,18 @@ public class CurrencyService extends AbstractService { Currency result = new Currency(); - result.setCurrency(parameters.getCurrency()); + result.setCurrencyName(parameters.getCurrency()); result.setFirstBlockSignature(firstBlock.getSignature()); result.setMembersCount(currentBlock.getMembersCount()); result.setLastUD(lastUD); result.setParameters(parameters); - indexCurrency(result); - - indexPeer(parameters.getCurrency(), peer); + // Save it + saveCurrency(result, pluginSettings.getKeyringPublicKey()); return result; } - /** - * Index a blockchain - * @param currency - */ - public void indexCurrency(Currency currency) { - try { - Preconditions.checkNotNull(currency.getCurrency()); - - // Fill tags - if (ArrayUtils.isEmpty(currency.getTags())) { - String currencyName = currency.getCurrency(); - String[] tags = currencyName.split(REGEX_WORD_SEPARATOR); - List<String> tagsList = Lists.newArrayList(tags); - - // Convert as a sentence (replace seprator with a space) - String sentence = currencyName.replaceAll(REGEX_WORD_SEPARATOR, " "); - if (!tagsList.contains(sentence)) { - tagsList.add(sentence); - } - - currency.setTags(tagsList.toArray(new String[tagsList.size()])); - } - - // Serialize into JSON - byte[] json = objectMapper.writeValueAsBytes(currency); - - // Preparing indexBlocksFromNode - IndexRequestBuilder indexRequest = client.prepareIndex(INDEX, RECORD_TYPE) - .setId(currency.getCurrency()) - .setSource(json); - - // Execute indexBlocksFromNode - indexRequest - .setRefresh(true) - .execute().actionGet(); - - } catch(JsonProcessingException e) { - throw new TechnicalException(e); - } - } - - public String indexPeer(String currency, Peer peer) { - Preconditions.checkNotNull(currency); - Preconditions.checkNotNull(peer); - try { - // Serialize into JSON - byte[] json = objectMapper.writeValueAsBytes(peer); - - // Preparing index - IndexRequestBuilder indexRequest = client.prepareIndex(currency, PEER_TYPE) - .setSource(json); - - // Execute index - return indexRequest - .setRefresh(true) - .execute().actionGet().getId(); - - } catch(JsonProcessingException e) { - throw new TechnicalException(e); - } - } - - /** - * Get suggestions from a string query. Useful for web autocomplete field (e.g. text full search) - * @param query - * @return - */ - /* public List<String> getSuggestions(String query) { - CompletionSuggestionBuilder suggestionBuilder = new CompletionSuggestionBuilder(INDEX_TYPE) - .text(query) - .size(10) // limit to 10 results - .field("tags"); - - // Prepare request - SuggestRequestBuilder suggestRequest = client - .prepareSuggest(INDEX_NAME) - .addSuggestion(suggestionBuilder); - - // Execute query - SuggestResponse response = suggestRequest.execute().actionGet(); - - // Read query result - return toSuggestions(response, RECORD_CATEGORY_TYPE, query); - }*/ - /** * Save a blockchain (update or create) into the blockchain index. * @param currency @@ -293,9 +160,9 @@ public class CurrencyService extends AbstractService { */ public void saveCurrency(Currency currency, String issuer) throws DuplicateIndexIdException { Preconditions.checkNotNull(currency, "currency could not be null") ; - Preconditions.checkNotNull(currency.getCurrency(), "currency attribute 'currency' could not be null"); + Preconditions.checkNotNull(currency.getId(), "currency attribute 'currency' could not be null"); - String previousIssuer = getSenderPubkeyByCurrencyId(currency.getCurrency()); + String previousIssuer = getSenderPubkeyByCurrencyId(currency.getId()); // Currency not exists, so create it if (previousIssuer == null) { @@ -303,20 +170,32 @@ public class CurrencyService extends AbstractService { currency.setIssuer(issuer); // Save it - indexCurrency(currency); + currencyDao.create(currency); + + // Create data index (delete first if exists) + getCurrencyDataDao(currency.getId()) + .deleteIndex() + .createIndexIfNotExists(); + } // Exists, so check the owner signature else { - if (!Objects.equals(issuer, previousIssuer)) { + if (issuer != null && !Objects.equals(issuer, previousIssuer)) { throw new AccessDeniedException("Could not change the currency, because it has been registered by another public key."); } // Make sure the sender is not changed - currency.setIssuer(previousIssuer); + if (issuer != null) { + currency.setIssuer(previousIssuer); + } // Save changes - indexCurrency(currency); + currencyDao.update(currency); + + // Create data index (if need) + getCurrencyDataDao(currency.getId()) + .createIndexIfNotExists(); } } @@ -342,7 +221,7 @@ public class CurrencyService extends AbstractService { try { currency = objectMapper.readValue(jsonCurrency, Currency.class); Preconditions.checkNotNull(currency); - Preconditions.checkNotNull(currency.getCurrency()); + Preconditions.checkNotNull(currency.getCurrencyName()); } catch(Throwable t) { logger.error("Error while reading blockchain JSON: " + jsonCurrency); throw new TechnicalException("Error while reading blockchain JSON: " + jsonCurrency, t); @@ -353,60 +232,7 @@ public class CurrencyService extends AbstractService { /* -- Internal methods -- */ - public XContentBuilder createRecordTypeMapping() { - try { - XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(RECORD_TYPE) - .startObject("properties") - - // currency - .startObject("currency") - .field("type", "string") - .endObject() - - // firstBlockSignature - .startObject("firstBlockSignature") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // member count - .startObject("membersCount") - .field("type", "long") - .endObject() - - // lastUD - .startObject("lastUD") - .field("type", "long") - .endObject() - - // unitbase - .startObject("unitbase") - .field("type", "integer") - .endObject() - - // issuer - .startObject("issuer") - .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_TYPE, ioe.getMessage()), ioe); - } - } + /** * Retrieve a blockchain from its name @@ -415,11 +241,12 @@ public class CurrencyService extends AbstractService { */ protected String getSenderPubkeyByCurrencyId(String currencyId) { - if (!existsIndex(currencyId)) { + if (!isCurrencyExists(currencyId)) { return null; } // Prepare request + SearchRequestBuilder searchRequest = client .prepareSearch(INDEX) .setTypes(RECORD_TYPE) @@ -451,4 +278,41 @@ public class CurrencyService extends AbstractService { return null; } + + protected IndexDao<?> getCurrencyDataDao(final String currencyId) { + // Create data + IndexDao<?> dataDao = currencyDataDaos.get(currencyId); + if (dataDao == null) { + dataDao = new AbstractIndexDao(currencyId) { + @Override + protected void createIndex() throws JsonProcessingException { + logger.info(String.format("Creating index [%s]", currencyId)); + + CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(currencyId); + 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); + + // Add peer type + TypeDao<?> peerDao = (TypeDao<?>)ServiceLocator.instance().getBean(PeerDao.class); + createIndexRequestBuilder.addMapping(peerDao.getType(), peerDao.createTypeMapping()); + + // Add block type + BlockDao blockDao = ServiceLocator.instance().getBean(BlockDao.class); + createIndexRequestBuilder.addMapping(blockDao.getType(), blockDao.createTypeMapping()); + + createIndexRequestBuilder.execute().actionGet(); + } + }; + injector.injectMembers(dataDao); + currencyDataDaos.put(currencyId, dataDao); + } + + return dataDao; + + + } } diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/EndpointService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/EndpointService.java deleted file mode 100644 index 1fb2eb9fd812b050e9f4adb40bd583387e79e091..0000000000000000000000000000000000000000 --- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/EndpointService.java +++ /dev/null @@ -1,533 +0,0 @@ -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 com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import org.duniter.core.client.model.bma.BlockchainParameters; -import org.duniter.core.client.model.bma.EndpointApi; -import org.duniter.core.client.model.bma.jackson.JacksonUtils; -import org.duniter.core.client.model.local.Peer; -import org.duniter.core.exception.TechnicalException; -import org.duniter.core.model.NullProgressionModel; -import org.duniter.core.model.ProgressionModel; -import org.duniter.core.util.CollectionUtils; -import org.duniter.core.util.Preconditions; -import org.duniter.core.util.StringUtils; -import org.duniter.core.util.concurrent.CompletableFutures; -import org.duniter.core.util.json.JsonAttributeParser; -import org.duniter.core.util.json.JsonSyntaxException; -import org.duniter.core.util.websocket.WebsocketClientEndpoint; -import org.duniter.elasticsearch.PluginSettings; -import org.duniter.elasticsearch.exception.DuplicateIndexIdException; -import org.duniter.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.action.ActionFuture; -import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.search.SearchRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.action.search.SearchType; -import org.elasticsearch.action.update.UpdateRequestBuilder; -import org.elasticsearch.action.update.UpdateResponse; -import org.elasticsearch.client.Client; -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.search.SearchHitField; -import org.elasticsearch.search.highlight.HighlightField; -import org.elasticsearch.search.sort.SortOrder; -import org.nuiton.i18n.I18n; - -import java.io.IOException; -import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; - -/** - * Created by Benoit on 30/03/2015. - */ -public class EndpointService extends AbstractService { - - public static final String ENDPOINT_TYPE = "endpoint"; - - private final ProgressionModel nullProgressionModel = new NullProgressionModel(); - - private org.duniter.core.client.service.bma.BlockchainRemoteService blockchainRemoteService; - private org.duniter.core.client.service.local.NetworkService networkService; - private ThreadPool threadPool; - private List<WebsocketClientEndpoint.ConnectionListener> connectionListeners = new ArrayList<>(); - private final WebsocketClientEndpoint.ConnectionListener dispatchConnectionListener; - - private final JsonAttributeParser blockCurrencyParser = new JsonAttributeParser("currency"); - private final JsonAttributeParser blockHashParser = new JsonAttributeParser("hash"); - - private ObjectMapper objectMapper; - - @Inject - public EndpointService(Client client, PluginSettings settings, ThreadPool threadPool, - final ServiceLocator serviceLocator){ - super("duniter.network", client, settings); - this.objectMapper = JacksonUtils.newObjectMapper(); - this.threadPool = threadPool; - threadPool.scheduleOnStarted(() -> { - this.blockchainRemoteService = serviceLocator.getBlockchainRemoteService(); - this.networkService = serviceLocator.getNetworkService(); - }); - dispatchConnectionListener = new WebsocketClientEndpoint.ConnectionListener() { - @Override - public void onSuccess() { - synchronized (connectionListeners) { - connectionListeners.stream().forEach(connectionListener -> connectionListener.onSuccess()); - } - } - @Override - public void onError(Exception e, long lastTimeUp) { - synchronized (connectionListeners) { - connectionListeners.stream().forEach(connectionListener -> connectionListener.onError(e, lastTimeUp)); - } - } - }; - } - - public void registerConnectionListener(WebsocketClientEndpoint.ConnectionListener listener) { - synchronized (connectionListeners) { - connectionListeners.add(listener); - } - } - - public EndpointService indexAllEndpoints(Peer peer) { - indexAllEndpoints(peer, nullProgressionModel); - return this; - } - - public EndpointService indexAllEndpoints(Peer peer, ProgressionModel progressionModel) { - - try { - // Get the blockchain name from node - BlockchainParameters parameter = blockchainRemoteService.getParameters(peer); - if (parameter == null) { - progressionModel.setStatus(ProgressionModel.Status.FAILED); - logger.error(I18n.t("duniter4j.es.networkService.indexPeers.remoteParametersError", peer)); - return this; - } - String currencyName = parameter.getCurrency(); - - indexPeers(currencyName, peer, progressionModel); - - } catch(Exception e) { - logger.error("Error during indexAllEndpoints: " + e.getMessage(), e); - progressionModel.setStatus(ProgressionModel.Status.FAILED); - } - - return this; - } - - - public EndpointService indexPeers(String currencyName, Peer firstPeer, ProgressionModel progressionModel) { - progressionModel.setStatus(ProgressionModel.Status.RUNNING); - progressionModel.setTotal(100); - long timeStart = System.currentTimeMillis(); - - try { - - progressionModel.setTask(I18n.t("duniter4j.es.networkService.indexPeers.task", currencyName, firstPeer)); - logger.info(I18n.t("duniter4j.es.networkService.indexPeers.task", currencyName, firstPeer)); - - // Default filter - org.duniter.core.client.service.local.NetworkService.Filter filterDef = new org.duniter.core.client.service.local.NetworkService.Filter(); - filterDef.filterType = null; - filterDef.filterStatus = Peer.PeerStatus.UP; - filterDef.filterEndpoints = ImmutableList.of(EndpointApi.BASIC_MERKLED_API.name(), EndpointApi.BMAS.name()); - - // Default sort - org.duniter.core.client.service.local.NetworkService.Sort sortDef = new org.duniter.core.client.service.local.NetworkService.Sort(); - sortDef.sortType = null; - - try { - networkService.asyncGetPeers(firstPeer, threadPool.scheduler()) - .thenCompose(CompletableFutures::allOfToList) - .thenApply(networkService::fillPeerStatsConsensus) - .thenApply(peers -> peers.stream() - // filter, then sort - .filter(networkService.peerFilter(filterDef)) - .map(peer -> savePeer(peer, false)) - .collect(Collectors.toList())) - .thenApply(peers -> { - logger.info(I18n.t("duniter4j.es.networkService.indexPeers.succeed", currencyName, firstPeer, peers.size(), (System.currentTimeMillis() - timeStart))); - progressionModel.setStatus(ProgressionModel.Status.SUCCESS); - return peers; - }); - } catch (InterruptedException | ExecutionException e) { - throw new TechnicalException("Error while loading peers: " + e.getMessage(), e); - } - } catch(Exception e) { - logger.error("Error during indexBlocksFromNode: " + e.getMessage(), e); - progressionModel.setStatus(ProgressionModel.Status.FAILED); - } - - return this; - } - -/* - public void start(Peer peer, FilterAndSortSpec networkSpec) { - Preconditions.checkNotNull(networkSpec); - this.networkSpec = networkSpec; - start(peer); - } - - public void start(Peer peer) { - Preconditions.checkNotNull(peer); - - log.debug("Starting network crawler..."); - - addListeners(peer); - - this.mainPeer = peer; - - try { - this.peers = loadPeers(this.mainPeer).get(); - } - catch(Exception e) { - throw new TechnicalException("Error during start load peers", e); - } - - isStarted = true; - log.info("Network crawler started"); - } - - public void stop() { - if (!isStarted) return; - log.debug("Stopping network crawler..."); - - removeListeners(); - - this.mainPeer = null; - this.mainPeerWsEp = null; - this.isStarted = false; - - this.executorService.shutdown(); - - log.info("Network crawler stopped"); - }*/ - - /** - * Create or update a peer, depending on its existence and hash - * @param peer - * @param wait wait indexBlocksFromNode end - * @throws DuplicateIndexIdException - */ - public Peer savePeer(final Peer peer, boolean wait) throws DuplicateIndexIdException { - Preconditions.checkNotNull(peer, "peer could not be null") ; - Preconditions.checkNotNull(peer.getCurrency(), "peer attribute 'currency' could not be null"); - Preconditions.checkNotNull(peer.getPubkey(), "peer attribute 'pubkey' could not be null"); - Preconditions.checkNotNull(peer.getHost(), "peer attribute 'host' could not be null"); - Preconditions.checkNotNull(peer.getApi(), "peer 'api' could not be null"); - - String id = cryptoService.hash(peer.computeKey()); - - boolean exists = isDocumentExists(peer.getCurrency(), ENDPOINT_TYPE, id); - - // Currency not exists, or has changed, so create it - if (!exists) { - if (logger.isTraceEnabled()) { - logger.trace(String.format("Insert new peer [%s]", peer)); - } - - // Index new peer - indexPeer(peer, id, wait); - } - - // Update existing peer - else { - logger.trace(String.format("Update peer [%s]", peer)); - updatePeer(peer, id, wait); - } - return peer; - } - - public void indexPeer(Peer peer, String id, boolean wait) { - Preconditions.checkNotNull(peer); - Preconditions.checkArgument(StringUtils.isNotBlank(peer.getCurrency())); - Preconditions.checkNotNull(peer.getHash()); - Preconditions.checkNotNull(peer.getHost()); - Preconditions.checkNotNull(peer.getApi()); - - // Serialize into JSON - // WARN: must use GSON, to have same JSON result (e.g identities and joiners field must be converted into String) - try { - String json = objectMapper.writeValueAsString(peer); - - // Preparing indexBlocksFromNode - IndexRequestBuilder indexRequest = client.prepareIndex(peer.getCurrency(), ENDPOINT_TYPE) - .setId(id) - .setSource(json); - - // Execute indexBlocksFromNode - ActionFuture<IndexResponse> futureResponse = indexRequest - .setRefresh(true) - .execute(); - - if (wait) { - futureResponse.actionGet(); - } - } - catch(JsonProcessingException e) { - throw new TechnicalException(e); - } - } - - public void updatePeer(Peer peer, String id, boolean wait) { - Preconditions.checkNotNull(peer); - Preconditions.checkArgument(StringUtils.isNotBlank(peer.getCurrency())); - Preconditions.checkNotNull(peer.getHash()); - Preconditions.checkNotNull(peer.getHost()); - Preconditions.checkNotNull(peer.getApi()); - - // Serialize into JSON - // WARN: must use GSON, to have same JSON result (e.g identities and joiners field must be converted into String) - try { - String json = objectMapper.writeValueAsString(peer); - - // Preparing indexBlocksFromNode - UpdateRequestBuilder updateRequest = client.prepareUpdate(peer.getCurrency(), ENDPOINT_TYPE, id) - .setDoc(json); - - // Execute indexBlocksFromNode - ActionFuture<UpdateResponse> futureResponse = updateRequest - .setRefresh(true) - .execute(); - - if (wait) { - futureResponse.actionGet(); - } - } - catch(JsonProcessingException e) { - throw new TechnicalException(e); - } - } - - /** - * Index the given block, as the last (current) block. This will check is a fork has occur, and apply a rollback so. - */ - public void onNetworkChanged() { - logger.info("ES network service -> peers changed: TODO: index new peers"); - } - - /** - * - * @param json block as json - * @param refresh Enable ES update with 'refresh' tag ? - * @param wait need to wait until processed ? - */ - public EndpointService indexPeer(Peer peer, String json, boolean refresh, boolean wait) { - Preconditions.checkNotNull(json); - Preconditions.checkArgument(json.length() > 0); - - String currencyName = blockCurrencyParser.getValueAsString(json); - String hash = blockHashParser.getValueAsString(json); - - logger.info(I18n.t("duniter4j.es.networkService.indexPeer", currencyName, peer)); - if (logger.isTraceEnabled()) { - logger.trace(json); - } - - - // Preparing index - IndexRequestBuilder indexRequest = client.prepareIndex(currencyName, ENDPOINT_TYPE) - .setId(hash) - .setRefresh(refresh) - .setSource(json); - - // Execute indexBlocksFromNode - if (!wait) { - indexRequest.execute(); - } - else { - indexRequest.execute().actionGet(); - } - - return this; - } - - public List<Peer> findPeersByHash(String currencyName, String query) { - String[] queryParts = query.split("[\\t ]+"); - - // Prepare request - SearchRequestBuilder searchRequest = client - .prepareSearch(currencyName) - .setTypes(ENDPOINT_TYPE) - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH); - - // If only one term, search as prefix - if (queryParts.length == 1) { - searchRequest.setQuery(QueryBuilders.prefixQuery("hash", query)); - } - - // If more than a word, search on terms match - else { - searchRequest.setQuery(QueryBuilders.matchQuery("hash", query)); - } - - // Sort as score/memberCount - searchRequest.addSort("_score", SortOrder.DESC) - .addSort("number", SortOrder.DESC); - - // Highlight matched words - searchRequest.setHighlighterTagsSchema("styled") - .addHighlightedField("hash") - .addFields("hash") - .addFields("*", "_source"); - - // Execute query - SearchResponse searchResponse = searchRequest.execute().actionGet(); - - // Read query result - return toPeers(searchResponse, true); - } - - /* -- Internal methods -- */ - - public static XContentBuilder createEndpointTypeMapping() { - try { - XContentBuilder mapping = XContentFactory.jsonBuilder() - .startObject() - .startObject(ENDPOINT_TYPE) - .startObject("properties") - - // currency - .startObject("currency") - .field("sortType", "string") - .endObject() - - // pubkey - .startObject("pubkey") - .field("sortType", "string") - .field("index", "not_analyzed") - .endObject() - - // api - .startObject("api") - .field("sortType", "string") - .field("index", "not_analyzed") - .endObject() - - // uid - .startObject("uid") - .field("sortType", "string") - .endObject() - - // dns - .startObject("dns") - .field("sortType", "string") - .endObject() - - // ipv4 - .startObject("ipv4") - .field("sortType", "string") - .endObject() - - // ipv6 - .startObject("ipv6") - .field("sortType", "string") - .endObject() - - .endObject() - .endObject().endObject(); - - return mapping; - } - catch(IOException ioe) { - throw new TechnicalException("Error while getting mapping for peer index: " + ioe.getMessage(), ioe); - } - } - - public Peer getPeerByHash(String currencyName, String id) { - - // Prepare request - SearchRequestBuilder searchRequest = client - .prepareSearch(currencyName) - .setTypes(ENDPOINT_TYPE) - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH); - - // If more than a word, search on terms match - searchRequest.setQuery(QueryBuilders.matchQuery("_id", id)); - - // Execute query - try { - SearchResponse searchResponse = searchRequest.execute().actionGet(); - List<Peer> peers = toPeers(searchResponse, false); - if (CollectionUtils.isEmpty(peers)) { - return null; - } - - // Return the unique result - return CollectionUtils.extractSingleton(peers); - } - catch(JsonSyntaxException e) { - throw new TechnicalException(String.format("Error while getting indexed peer #%s for [%s]", id, currencyName), e); - } - - } - - protected List<Peer> toPeers(SearchResponse response, boolean withHighlight) { - // Read query result - List<Peer> result = Lists.newArrayList(); - response.getHits().forEach(searchHit -> { - Peer peer; - if (searchHit.source() != null) { - String jsonString = new String(searchHit.source()); - try { - peer = objectMapper.readValue(jsonString, Peer.class); - } catch(Exception e) { - if (logger.isDebugEnabled()) { - logger.debug("Error while parsing peer from JSON:\n" + jsonString); - } - throw new JsonSyntaxException("Error while read peer from JSON: " + e.getMessage(), e); - } - } - else { - peer = new Peer(); - SearchHitField field = searchHit.getFields().get("hash"); - peer.setHash(field.getValue()); - } - result.add(peer); - - // If possible, use highlights - if (withHighlight) { - Map<String, HighlightField> fields = searchHit.getHighlightFields(); - for (HighlightField field : fields.values()) { - String blockNameHighLight = field.getFragments()[0].string(); - peer.setHash(blockNameHighLight); - } - } - }); - - return result; - } - - -} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/PeerService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/PeerService.java new file mode 100644 index 0000000000000000000000000000000000000000..e83927673b983312182b9fd5f1fc57d461fea6ed --- /dev/null +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/PeerService.java @@ -0,0 +1,258 @@ +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 com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import org.duniter.core.client.dao.PeerDao; +import org.duniter.core.client.model.bma.BlockchainParameters; +import org.duniter.core.client.model.bma.EndpointApi; +import org.duniter.core.client.model.local.Peer; +import org.duniter.core.client.service.local.NetworkService; +import org.duniter.core.exception.TechnicalException; +import org.duniter.core.model.NullProgressionModel; +import org.duniter.core.model.ProgressionModel; +import org.duniter.core.service.CryptoService; +import org.duniter.core.util.CollectionUtils; +import org.duniter.core.util.Preconditions; +import org.duniter.core.util.concurrent.CompletableFutures; +import org.duniter.core.util.json.JsonSyntaxException; +import org.duniter.elasticsearch.PluginSettings; +import org.duniter.elasticsearch.client.Duniter4jClient; +import org.duniter.elasticsearch.exception.DuplicateIndexIdException; +import org.duniter.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.search.SearchHitField; +import org.elasticsearch.search.highlight.HighlightField; +import org.nuiton.i18n.I18n; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +/** + * Created by Benoit on 30/03/2015. + */ +public class PeerService extends AbstractService { + + private final ProgressionModel nullProgressionModel = new NullProgressionModel(); + + private org.duniter.core.client.service.bma.BlockchainRemoteService blockchainRemoteService; + private org.duniter.core.client.service.local.NetworkService networkService; + private ThreadPool threadPool; + private PeerDao peerDao; + + @Inject + public PeerService(Duniter4jClient client, PluginSettings settings, ThreadPool threadPool, + CryptoService cryptoService, + PeerDao peerDao, + final ServiceLocator serviceLocator){ + super("duniter.peers", client, settings, cryptoService); + this.peerDao = peerDao; + this.threadPool = threadPool; + threadPool.scheduleOnStarted(() -> { + this.blockchainRemoteService = serviceLocator.getBlockchainRemoteService(); + this.networkService = serviceLocator.getNetworkService(); + }); + } + + public PeerService indexAllPeers(Peer peer) { + indexAllPeers(peer, nullProgressionModel); + return this; + } + + public PeerService indexAllPeers(Peer peer, ProgressionModel progressionModel) { + + try { + // Get the blockchain name from node + BlockchainParameters parameter = blockchainRemoteService.getParameters(peer); + if (parameter == null) { + progressionModel.setStatus(ProgressionModel.Status.FAILED); + logger.error(I18n.t("duniter4j.es.networkService.indexPeers.remoteParametersError", peer)); + return this; + } + String currencyName = parameter.getCurrency(); + + indexPeers(currencyName, peer, progressionModel); + + } catch(Exception e) { + logger.error("Error during indexAllPeers: " + e.getMessage(), e); + progressionModel.setStatus(ProgressionModel.Status.FAILED); + } + + return this; + } + + + public PeerService indexPeers(String currencyName, Peer firstPeer, ProgressionModel progressionModel) { + progressionModel.setStatus(ProgressionModel.Status.RUNNING); + progressionModel.setTotal(100); + long timeStart = System.currentTimeMillis(); + + try { + + progressionModel.setTask(I18n.t("duniter4j.es.networkService.indexPeers.task", currencyName, firstPeer)); + logger.info(I18n.t("duniter4j.es.networkService.indexPeers.task", currencyName, firstPeer)); + + // Default filter + org.duniter.core.client.service.local.NetworkService.Filter filterDef = new org.duniter.core.client.service.local.NetworkService.Filter(); + filterDef.filterType = null; + filterDef.filterStatus = Peer.PeerStatus.UP; + filterDef.filterEndpoints = ImmutableList.of(EndpointApi.BASIC_MERKLED_API.name(), EndpointApi.BMAS.name()); + + // Default sort + org.duniter.core.client.service.local.NetworkService.Sort sortDef = new org.duniter.core.client.service.local.NetworkService.Sort(); + sortDef.sortType = null; + + try { + networkService.asyncGetPeers(firstPeer, threadPool.scheduler()) + .thenCompose(CompletableFutures::allOfToList) + .thenApply(networkService::fillPeerStatsConsensus) + .thenApply(peers -> peers.stream() + // filter, then sort + .filter(networkService.peerFilter(filterDef)) + .map(peer -> savePeer(peer)) + .collect(Collectors.toList())) + .thenApply(peers -> { + logger.info(I18n.t("duniter4j.es.networkService.indexPeers.succeed", currencyName, firstPeer, peers.size(), (System.currentTimeMillis() - timeStart))); + progressionModel.setStatus(ProgressionModel.Status.SUCCESS); + return peers; + }); + } catch (InterruptedException | ExecutionException e) { + throw new TechnicalException("Error while loading peers: " + e.getMessage(), e); + } + } catch(Exception e) { + logger.error("Error during indexBlocksFromNode: " + e.getMessage(), e); + progressionModel.setStatus(ProgressionModel.Status.FAILED); + } + + return this; + } + + public void listenAndIndexPeers(final Peer mainPeer) { + // Get the blockchain name from node + BlockchainParameters parameter = blockchainRemoteService.getParameters(mainPeer); + if (parameter == null) { + logger.error(I18n.t("duniter4j.es.networkService.indexPeers.remoteParametersError", mainPeer)); + return; + } + String currencyName = parameter.getCurrency(); + + // Default filter + NetworkService.Filter filterDef = new NetworkService.Filter(); + filterDef.filterType = null; + filterDef.filterStatus = Peer.PeerStatus.UP; + filterDef.filterEndpoints = ImmutableList.of(EndpointApi.BASIC_MERKLED_API.name(), EndpointApi.BMAS.name()); + + // Default sort + NetworkService.Sort sortDef = new NetworkService.Sort(); + sortDef.sortType = null; + + networkService.addPeersChangeListener(mainPeer, peers -> { + if (CollectionUtils.isNotEmpty(peers)) { + logger.info(String.format("[%s] Updating peers endpoints (%s endpoints found)", currencyName, peers.size())); + peers.stream().forEach(peer -> savePeer(peer)); + } + }, filterDef, sortDef, true /*autoreconnect*/, threadPool.scheduler()); + } + + /** + * Create or update a peer, depending on its existence and hash + * @param peer + * @throws DuplicateIndexIdException + */ + public Peer savePeer(final Peer peer) throws DuplicateIndexIdException { + Preconditions.checkNotNull(peer, "peer could not be null") ; + Preconditions.checkNotNull(peer.getCurrency(), "peer attribute 'currency' could not be null"); + Preconditions.checkNotNull(peer.getPubkey(), "peer attribute 'pubkey' could not be null"); + Preconditions.checkNotNull(peer.getHost(), "peer attribute 'host' could not be null"); + Preconditions.checkNotNull(peer.getApi(), "peer 'api' could not be null"); + + String id = cryptoService.hash(peer.computeKey()); + peer.setId(id); + + boolean exists = peerDao.isExists(peer.getCurrency(), id); + + // Currency not exists, or has changed, so create it + if (!exists) { + if (logger.isTraceEnabled()) { + logger.trace(String.format("Insert new peer [%s]", peer)); + } + + // Index new peer + peer.setId(id); + peerDao.create(peer); + } + + // Update existing peer + else { + logger.trace(String.format("Update peer [%s]", peer)); + peerDao.update(peer); + } + return peer; + } + + + /* -- protected methods -- */ + + protected List<Peer> toPeers(SearchResponse response, boolean withHighlight) { + // Read query result + List<Peer> result = Lists.newArrayList(); + response.getHits().forEach(searchHit -> { + Peer peer; + if (searchHit.source() != null) { + String jsonString = new String(searchHit.source()); + try { + peer = objectMapper.readValue(jsonString, Peer.class); + } catch(Exception e) { + if (logger.isDebugEnabled()) { + logger.debug("Error while parsing peer from JSON:\n" + jsonString); + } + throw new JsonSyntaxException("Error while read peer from JSON: " + e.getMessage(), e); + } + } + else { + peer = new Peer(); + SearchHitField field = searchHit.getFields().get("hash"); + peer.setHash(field.getValue()); + } + result.add(peer); + + // If possible, use highlights + if (withHighlight) { + Map<String, HighlightField> fields = searchHit.getHighlightFields(); + for (HighlightField field : fields.values()) { + String blockNameHighLight = field.getFragments()[0].string(); + peer.setHash(blockNameHighLight); + } + } + }); + + return result; + } + + +} diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java index b264f91d209a4c23edb2f867aa50260b5879c327..7c773b054cdbe51833eed2556030a5bc96b072c3 100644 --- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java @@ -24,24 +24,28 @@ package org.duniter.elasticsearch.service; import org.duniter.core.beans.Bean; -import org.duniter.core.beans.BeanFactory; import org.duniter.core.client.dao.CurrencyDao; import org.duniter.core.client.dao.PeerDao; -import org.duniter.core.client.dao.mem.MemoryCurrencyDaoImpl; -import org.duniter.core.client.dao.mem.MemoryPeerDaoImpl; import org.duniter.core.client.service.DataContext; import org.duniter.core.client.service.HttpService; import org.duniter.core.client.service.HttpServiceImpl; import org.duniter.core.client.service.bma.*; import org.duniter.core.client.service.local.*; -import org.duniter.core.client.service.local.NetworkService; import org.duniter.core.client.service.local.CurrencyService; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; import org.duniter.core.service.Ed25519CryptoServiceImpl; import org.duniter.core.service.MailService; import org.duniter.core.service.MailServiceImpl; +import org.duniter.elasticsearch.beans.ESBeanFactory; +import org.duniter.elasticsearch.dao.BlockDao; +import org.duniter.elasticsearch.client.Duniter4jClient; +import org.duniter.elasticsearch.dao.impl.BlockDaoImpl; +import org.duniter.elasticsearch.dao.impl.CurrencyDaoImpl; +import org.duniter.elasticsearch.client.Duniter4jClientImpl; +import org.duniter.elasticsearch.dao.impl.PeerDaoImpl; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.inject.Injector; import org.elasticsearch.common.inject.Singleton; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.ESLoggerFactory; @@ -54,15 +58,18 @@ public class ServiceLocator { private static final ESLogger logger = ESLoggerFactory.getLogger("duniter.service"); - private static BeanFactory beanFactory = null; + private static ESBeanFactory beanFactory = null; @Inject - public ServiceLocator() { - super(); + public ServiceLocator(Injector injector/*, PeerDao peerDao, CurrencyDao currencyDao*/) { + super(getOrCreateBeanFactory()); if (logger.isDebugEnabled()) { logger.debug("Starting Duniter4j ServiceLocator..."); } - setBeanFactory(getOrCreateBeanFactory()); + beanFactory.setInjector(injector); +/* + beanFactory.setBean(peerDao, PeerDao.class); + beanFactory.setBean(currencyDao, CurrencyDao.class);*/ org.duniter.core.client.service.ServiceLocator.setInstance(this); } @@ -80,39 +87,45 @@ public class ServiceLocator /* -- Internal methods -- */ - protected static BeanFactory getOrCreateBeanFactory() { + protected static ESBeanFactory getOrCreateBeanFactory() { if (beanFactory != null) { return beanFactory; } - beanFactory = org.duniter.core.client.service.ServiceLocator.instance().getBeanFactory() - .bind(BlockchainRemoteService.class, BlockchainRemoteServiceImpl.class) + beanFactory = new ESBeanFactory(); + + beanFactory.bind(BlockchainRemoteService.class, BlockchainRemoteServiceImpl.class) .bind(NetworkRemoteService.class, NetworkRemoteServiceImpl.class) .bind(WotRemoteService.class, WotRemoteServiceImpl.class) .bind(TransactionRemoteService.class, TransactionRemoteServiceImpl.class) .bind(CryptoService.class, Ed25519CryptoServiceImpl.class) + .bind(org.duniter.core.client.service.local.PeerService.class, PeerServiceImpl.class) .bind(MailService.class, MailServiceImpl.class) - .bind(PeerService.class, PeerServiceImpl.class) .bind(CurrencyService.class, CurrencyServiceImpl.class) .bind(NetworkService.class, NetworkServiceImpl.class) .bind(HttpService.class, HttpServiceImpl.class) - .bind(CurrencyDao.class, MemoryCurrencyDaoImpl.class) - .bind(PeerDao.class, MemoryPeerDaoImpl.class) + // Dao + .bind(CurrencyDao.class, CurrencyDaoImpl.class) + .bind(PeerDao.class, PeerDaoImpl.class) + .bind(BlockDao.class, BlockDaoImpl.class) + .add(DataContext.class); + return beanFactory; } public static class Provider<T extends Bean> implements org.elasticsearch.common.inject.Provider<T> { private final Class<T> clazz; - private final BeanFactory beanFactory; public Provider(Class<T> clazz) { this.clazz = clazz; - this.beanFactory = getOrCreateBeanFactory(); } public T get() { - return beanFactory.getBean(clazz); + logger.debug("Loading class [" + clazz.getName() + "]..."); + T result = getOrCreateBeanFactory().getBean(clazz); + logger.debug("...end of loading [" + clazz.getName() + "]"); + return result; } } } diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceModule.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceModule.java index 7585e95790fac39616ca8ea9a5c6d6fddbb195c9..305b625195e1a7cbbd9f70bc3839b8eef64485a7 100644 --- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceModule.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceModule.java @@ -32,8 +32,8 @@ import org.duniter.core.client.service.bma.NetworkRemoteService; import org.duniter.core.client.service.bma.TransactionRemoteService; 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.core.service.MailService; import org.duniter.elasticsearch.PluginInit; import org.duniter.elasticsearch.PluginSettings; import org.duniter.elasticsearch.service.changes.ChangeService; @@ -54,23 +54,23 @@ public class ServiceModule extends AbstractModule implements Module { // blockchain indexation services bind(BlockchainService.class).asEagerSingleton(); - bind(EndpointService.class).asEagerSingleton(); + bind(PeerService.class).asEagerSingleton(); // Duniter Client API beans bindWithLocator(BlockchainRemoteService.class); bindWithLocator(NetworkRemoteService.class); bindWithLocator(WotRemoteService.class); bindWithLocator(TransactionRemoteService.class); - bindWithLocator(PeerService.class); + bindWithLocator(org.duniter.core.client.service.local.PeerService.class); bindWithLocator(CurrencyService.class); bindWithLocator(HttpService.class); - bindWithLocator(CurrencyDao.class); - bindWithLocator(PeerDao.class); + //bindWithLocator(CurrencyDao.class); + //bindWithLocator(PeerDao.class); bindWithLocator(DataContext.class); // Duniter Shared API beans bindWithLocator(CryptoService.class); - bindWithLocator(org.duniter.core.service.MailService.class); + bindWithLocator(MailService.class); } /* protected methods */ diff --git a/duniter4j-es-gchange/pom.xml b/duniter4j-es-gchange/pom.xml index dff7b07d31377710f0151b9e45b8584bdf042788..ab8ce385a9635160987197cd812a1f98779430dc 100644 --- a/duniter4j-es-gchange/pom.xml +++ b/duniter4j-es-gchange/pom.xml @@ -7,7 +7,6 @@ <version>0.9.2-SNAPSHOT</version> </parent> - <groupId>org.duniter</groupId> <artifactId>duniter4j-es-gchange</artifactId> <packaging>jar</packaging> <name>Duniter4j :: ElasticSearch GChange plugin</name> 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 index c7b1067c7cdd35bfa0b8174cdde750ad5cdcd59a..f4db995937fa9f104742f7418679abc48b200ebe 100644 --- 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 @@ -23,6 +23,7 @@ package org.duniter.elasticsearch.gchange; */ import com.google.common.collect.Lists; +import org.duniter.elasticsearch.gchange.dao.DaoModule; import org.duniter.elasticsearch.gchange.rest.RestModule; import org.duniter.elasticsearch.gchange.service.ServiceModule; import org.elasticsearch.common.component.LifecycleComponent; @@ -61,8 +62,9 @@ public class Plugin extends org.elasticsearch.plugins.Plugin { log.warn(description() + " has been disabled."); return modules; } - modules.add(new RestModule()); + modules.add(new DaoModule()); modules.add(new ServiceModule()); + modules.add(new RestModule()); return modules; } diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/AbstractCommentDaoImpl.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/AbstractCommentDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..52944b007df0f9f5232db32243a85e36361e83ab --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/AbstractCommentDaoImpl.java @@ -0,0 +1,155 @@ +package org.duniter.elasticsearch.gchange.dao; + +/* + * #%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 org.duniter.core.client.model.elasticsearch.RecordComment; +import org.duniter.core.exception.TechnicalException; +import org.duniter.elasticsearch.dao.AbstractIndexTypeDao; +import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.gchange.dao.CommentDao; +import org.elasticsearch.action.search.SearchPhaseExecutionException; +import org.elasticsearch.action.search.SearchRequestBuilder; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.search.SearchType; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.TermQueryBuilder; + +import java.io.IOException; + +/** + * Created by Benoit on 30/03/2015. + */ +public class AbstractCommentDaoImpl<T extends AbstractCommentDaoImpl> extends AbstractIndexTypeDao<T> implements CommentDao<T> { + + + protected PluginSettings pluginSettings; + + public AbstractCommentDaoImpl(String index, PluginSettings pluginSettings) { + super(index, CommentDao.TYPE); + this.pluginSettings = pluginSettings; + } + + @Override + protected void createIndex() throws JsonProcessingException { + throw new TechnicalException("not implemented"); + } + + public String create(final String json) { + return super.indexDocumentFromJson(json); + } + + public void update(final String id, final String json) { + super.updateDocumentFromJson(id, json); + } + + @Override + public long countReplies(String id) { + + // Prepare count request + SearchRequestBuilder searchRequest = client + .prepareSearch(getIndex()) + .setTypes(getType()) + .setFetchSource(false) + .setSearchType(SearchType.QUERY_AND_FETCH) + .setSize(0); + + // Query = filter on reference + TermQueryBuilder query = QueryBuilders.termQuery(RecordComment.PROPERTY_REPLY_TO_JSON, id); + searchRequest.setQuery(query); + + // Execute query + try { + SearchResponse response = searchRequest.execute().actionGet(); + return response.getHits().getTotalHits(); + } + catch(SearchPhaseExecutionException e) { + // Failed or no item on index + logger.error(String.format("Error while counting comment replies: %s", e.getMessage()), e); + } + return 1; + } + + + public XContentBuilder createTypeMapping() { + String stringAnalyzer = pluginSettings.getDefaultStringAnalyzer(); + + try { + XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(getType()) + .startObject("properties") + + // issuer + .startObject("issuer") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // time + .startObject("time") + .field("type", "integer") + .endObject() + + // message + .startObject("message") + .field("type", "string") + .field("analyzer", stringAnalyzer) + .endObject() + + // record + .startObject("record") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // reply to + .startObject("reply_to") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // aggregations + .startObject("aggregations") + .field("type", "nested") + .field("dynamic", "true") + .startObject("properties") + .startObject("reply_count") + .field("type", "integer") + .field("index", "not_analyzed") + .endObject() + .endObject() + .endObject() + + .endObject() + .endObject().endObject(); + + return mapping; + } + catch(IOException ioe) { + throw new TechnicalException(String.format("Error while getting mapping for index [%s]: %s", getType(), ioe.getMessage()), ioe); + } + } + +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/AbstractRecordDaoImpl.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/AbstractRecordDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..648137245cd7df2464ea102ac1c8384179ba770e --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/AbstractRecordDaoImpl.java @@ -0,0 +1,210 @@ +package org.duniter.elasticsearch.gchange.dao; + +/* + * #%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 org.duniter.core.client.model.elasticsearch.Record; +import org.duniter.core.exception.TechnicalException; +import org.duniter.core.util.ObjectUtils; +import org.duniter.elasticsearch.dao.AbstractIndexTypeDao; +import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.gchange.dao.RecordDao; +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 AbstractRecordDaoImpl<T extends AbstractRecordDaoImpl> extends AbstractIndexTypeDao<T> implements RecordDao<T> { + + protected PluginSettings pluginSettings; + + public AbstractRecordDaoImpl(String index, PluginSettings pluginSettings) { + super(index, RecordDao.TYPE); + this.pluginSettings = pluginSettings; + } + + @Override + protected void createIndex() throws JsonProcessingException { + throw new TechnicalException("not implemented"); + } + + @Override + public void checkSameDocumentIssuer(String id, String expectedIssuer) { + String issuer = getMandatoryFieldsById(id, Record.PROPERTY_ISSUER).get(Record.PROPERTY_ISSUER).toString(); + if (!ObjectUtils.equals(expectedIssuer, issuer)) { + throw new TechnicalException("Not same issuer"); + } + } + + public XContentBuilder createTypeMapping() { + String stringAnalyzer = pluginSettings.getDefaultStringAnalyzer(); + + try { + XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(getType()) + .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", getIndex(), getType(), ioe.getMessage()), ioe); + } + } +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/CommentDao.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/CommentDao.java new file mode 100644 index 0000000000000000000000000000000000000000..3cefe3997f7fdf4e74ed24cbba6a6a6d882c2d83 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/CommentDao.java @@ -0,0 +1,41 @@ +package org.duniter.elasticsearch.gchange.dao; + +/* + * #%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.elasticsearch.dao.IndexTypeDao; + +/** + * Created by Benoit on 30/03/2015. + */ +public interface CommentDao<T extends CommentDao> extends IndexTypeDao<T> { + + String TYPE = "comment"; + + String create(final String json); + + void update(final String id, final String json); + + long countReplies(String id); + +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/DaoModule.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/DaoModule.java new file mode 100644 index 0000000000000000000000000000000000000000..65625ab03abd553e5dbf9c4a304dad3e2bdd9d6a --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/DaoModule.java @@ -0,0 +1,52 @@ +package org.duniter.elasticsearch.gchange.dao; + +/* + * #%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.beans.Bean; +import org.duniter.elasticsearch.client.Duniter4jClientImpl; +import org.duniter.elasticsearch.gchange.dao.market.*; +import org.duniter.elasticsearch.gchange.dao.registry.*; +import org.duniter.elasticsearch.service.ServiceLocator; +import org.elasticsearch.common.inject.AbstractModule; +import org.elasticsearch.common.inject.Module; + +public class DaoModule extends AbstractModule implements Module { + + @Override protected void configure() { + + bind(RegistryIndexDao.class).to(RegistryIndexDaoImpl.class).asEagerSingleton(); + bind(RegistryCommentDao.class).to(RegistryCommentDaoImpl.class).asEagerSingleton(); + bind(RegistryRecordDao.class).to(RegistryRecordDaoImpl.class).asEagerSingleton(); + + bind(MarketIndexDao.class).to(MarketIndexDaoImpl.class).asEagerSingleton(); + bind(MarketCommentDao.class).to(MarketCommentDaoImpl.class).asEagerSingleton(); + bind(MarketRecordDao.class).to(MarketRecordDaoImpl.class).asEagerSingleton(); + } + + /* protected methods */ + + protected <T extends Bean> void bindWithLocator(Class<T> clazz) { + bind(clazz).toProvider(new ServiceLocator.Provider<>(clazz)); + } + +} \ No newline at end of file diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/RecordDao.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/RecordDao.java new file mode 100644 index 0000000000000000000000000000000000000000..3313806a9bf69254e10819135870302603557624 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/RecordDao.java @@ -0,0 +1,41 @@ +package org.duniter.elasticsearch.gchange.dao; + +/* + * #%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.elasticsearch.dao.IndexTypeDao; + +/** + * Created by Benoit on 30/03/2015. + */ +public interface RecordDao<T extends RecordDao> extends IndexTypeDao<T> { + + String TYPE = "record"; + + String create(final String json); + + void update(final String id, final String json); + + void checkSameDocumentIssuer(String id, String expectedIssuer); + +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CitiesRegistryService.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/location/CitiesLocationDaoImpl.java similarity index 96% rename from duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CitiesRegistryService.java rename to duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/location/CitiesLocationDaoImpl.java index b4b30dfb38565b4d839cdfad89567f409cb43f54..bcdf9807663fa165369a3834a3bf41bf7fc95cd9 100644 --- a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CitiesRegistryService.java +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/location/CitiesLocationDaoImpl.java @@ -1,4 +1,4 @@ -package org.duniter.elasticsearch.gchange.service; +package org.duniter.elasticsearch.gchange.dao.location; /* * #%L @@ -26,7 +26,7 @@ package org.duniter.elasticsearch.gchange.service; import com.fasterxml.jackson.core.JsonProcessingException; import org.duniter.core.exception.TechnicalException; import org.duniter.elasticsearch.PluginSettings; -import org.duniter.elasticsearch.service.AbstractService; +import org.duniter.elasticsearch.dao.AbstractDao; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; @@ -40,9 +40,9 @@ import java.io.IOException; /** * Created by Benoit on 30/03/2015. */ -public class CitiesRegistryService extends AbstractService { +public class CitiesLocationDaoImpl extends AbstractDao { - private static final ESLogger log = ESLoggerFactory.getLogger(CitiesRegistryService.class.getName()); + private static final ESLogger log = ESLoggerFactory.getLogger(CitiesLocationDaoImpl.class.getName()); private static final String CITIES_BULK_FILENAME = "registry-cities-bulk-insert.json"; @@ -50,12 +50,11 @@ public class CitiesRegistryService extends AbstractService { private static final String CITIES_SOURCE_FILE2 = "/home/blavenie/git/ucoin-io/duniter4j/duniter4j-elasticsearch/src/main/misc/geoflar-communes-2015.geojson"; - public static final String INDEX = "registry"; + public static final String INDEX = "location"; public static final String CITY_TYPE = "city"; - @Inject - public CitiesRegistryService(Client client, PluginSettings settings) { - super(client, settings); + public CitiesLocationDaoImpl() { + super("gchange.location.cities"); } /** @@ -63,12 +62,12 @@ public class CitiesRegistryService extends AbstractService { * @throws JsonProcessingException */ public void deleteIndex() throws JsonProcessingException { - deleteIndexIfExists(INDEX); + client.deleteIndexIfExists(INDEX); } public boolean existsIndex() { - return super.existsIndex(INDEX); + return client.existsIndex(INDEX); } /** @@ -76,7 +75,7 @@ public class CitiesRegistryService extends AbstractService { */ public void createIndexIfNotExists() { try { - if (!existsIndex(INDEX)) { + if (!client.existsIndex(INDEX)) { createIndex(); } } diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketCommentDao.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketCommentDao.java new file mode 100644 index 0000000000000000000000000000000000000000..a3f16efab314f737cbf942d0124c0a90faad29ef --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketCommentDao.java @@ -0,0 +1,9 @@ +package org.duniter.elasticsearch.gchange.dao.market; + +import org.duniter.elasticsearch.gchange.dao.CommentDao; + +/** + * Created by blavenie on 03/04/17. + */ +public interface MarketCommentDao extends CommentDao { +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketCommentDaoImpl.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketCommentDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..415f3ba193d83e012de37530d5ceb95f3d872038 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketCommentDaoImpl.java @@ -0,0 +1,22 @@ +package org.duniter.elasticsearch.gchange.dao.market; + +import org.duniter.core.exception.TechnicalException; +import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.gchange.dao.AbstractCommentDaoImpl; +import org.duniter.elasticsearch.gchange.dao.AbstractRecordDaoImpl; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; + +import java.io.IOException; + +/** + * Created by blavenie on 03/04/17. + */ +public class MarketCommentDaoImpl extends AbstractCommentDaoImpl implements MarketCommentDao { + + @Inject + public MarketCommentDaoImpl(PluginSettings pluginSettings) { + super(MarketIndexDao.INDEX, pluginSettings); + } +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketIndexDao.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketIndexDao.java new file mode 100644 index 0000000000000000000000000000000000000000..2e3f36177728d5f8cee36ff2230b6ba875b35ce0 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketIndexDao.java @@ -0,0 +1,11 @@ +package org.duniter.elasticsearch.gchange.dao.market; + +import org.duniter.elasticsearch.dao.IndexDao; + +/** + * Created by blavenie on 03/04/17. + */ +public interface MarketIndexDao extends IndexDao<MarketIndexDao> { + String INDEX = "market"; + String CATEGORY_TYPE = "category"; +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketIndexDaoImpl.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketIndexDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..69548e97c3b231274df263fdc2d9df79cc7af787 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketIndexDaoImpl.java @@ -0,0 +1,111 @@ +package org.duniter.elasticsearch.gchange.dao.market; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.duniter.core.exception.TechnicalException; +import org.duniter.elasticsearch.dao.AbstractIndexDao; +import org.duniter.elasticsearch.dao.AbstractIndexTypeDao; +import org.duniter.elasticsearch.dao.IndexTypeDao; +import org.duniter.elasticsearch.dao.handler.AddSequenceAttributeHandler; +import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.gchange.dao.CommentDao; +import org.duniter.elasticsearch.gchange.dao.RecordDao; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; + +import java.io.IOException; + +/** + * Created by blavenie on 03/04/17. + */ +public class MarketIndexDaoImpl extends AbstractIndexDao<MarketIndexDao> implements MarketIndexDao { + + private static final String CATEGORIES_BULK_CLASSPATH_FILE = "registry-categories-bulk-insert.json"; + + private PluginSettings pluginSettings; + private IndexTypeDao<?> categoryDao; + private RecordDao recordDao; + private CommentDao commentDao; + + @Inject + public MarketIndexDaoImpl(PluginSettings pluginSettings, MarketRecordDao recordDao, MarketCommentDao commentDao) { + super(MarketIndexDao.INDEX); + + this.pluginSettings = pluginSettings; + this.commentDao = commentDao; + this.recordDao = recordDao; + this.categoryDao = createCategoryDao(pluginSettings); + } + + + @Override + protected void 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(recordDao.getType(), recordDao.createTypeMapping()); + createIndexRequestBuilder.addMapping(commentDao.getType(), commentDao.createTypeMapping()); + createIndexRequestBuilder.addMapping(categoryDao.getType(), categoryDao.createTypeMapping()); + createIndexRequestBuilder.execute().actionGet(); + + // Fill categories + fillRecordCategories(); + } + + public void fillRecordCategories() { + if (logger.isDebugEnabled()) { + logger.debug(String.format("[%s/%s] Fill data", INDEX, MarketIndexDao.CATEGORY_TYPE)); + } + + // Insert categories + categoryDao.bulkFromClasspathFile(CATEGORIES_BULK_CLASSPATH_FILE, + // Add order attribute + new AddSequenceAttributeHandler("order", "\\{.*\"name\".*\\}", 1)); + } + + + protected IndexTypeDao<?> createCategoryDao(final PluginSettings settings) { + return new AbstractIndexTypeDao(INDEX, MarketIndexDao.CATEGORY_TYPE) { + @Override + protected void createIndex() throws JsonProcessingException { + throw new TechnicalException("not implemented"); + } + + @Override + public XContentBuilder createTypeMapping() { + try { + XContentBuilder mapping = XContentFactory.jsonBuilder().startObject() + .startObject(getType()) + .startObject("properties") + + // name + .startObject("name") + .field("type", "string") + .field("analyzer", settings.getDefaultStringAnalyzer()) + .endObject() + + // parent + .startObject("parent") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + .endObject() + .endObject().endObject(); + + return mapping; + } + catch(IOException ioe) { + throw new TechnicalException(String.format("Error while getting mapping for index [%s/%s]: %s", getIndex(), getType(), ioe.getMessage()), ioe); + } + } + }; + } +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketRecordDao.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketRecordDao.java new file mode 100644 index 0000000000000000000000000000000000000000..7b4238a9dc5dfecb935e17012f688a1bf601d271 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketRecordDao.java @@ -0,0 +1,9 @@ +package org.duniter.elasticsearch.gchange.dao.market; + +import org.duniter.elasticsearch.gchange.dao.RecordDao; + +/** + * Created by blavenie on 03/04/17. + */ +public interface MarketRecordDao extends RecordDao { +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketRecordDaoImpl.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketRecordDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..f5f855d7326e75f9ee15a6b61710835ff6349cdc --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/market/MarketRecordDaoImpl.java @@ -0,0 +1,182 @@ +package org.duniter.elasticsearch.gchange.dao.market; + +import org.duniter.core.exception.TechnicalException; +import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.gchange.dao.AbstractRecordDaoImpl; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; + +import java.io.IOException; + +/** + * Created by blavenie on 03/04/17. + */ +public class MarketRecordDaoImpl extends AbstractRecordDaoImpl implements MarketRecordDao { + + @Inject + public MarketRecordDaoImpl(PluginSettings pluginSettings) { + super(MarketIndexDao.INDEX, pluginSettings); + } + + @Override + public XContentBuilder createTypeMapping() { + String stringAnalyzer = pluginSettings.getDefaultStringAnalyzer(); + + try { + XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(getType()) + .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() + + // price + .startObject("price") + .field("type", "double") + .endObject() + + // price Unit + .startObject("unit") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // currency + .startObject("currency") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // issuer + .startObject("issuer") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // type (offer, need, ...) + .startObject("type") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + // location + .startObject("location") + .field("type", "string") + .field("analyzer", stringAnalyzer) + .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", getIndex(), getType(), ioe.getMessage()), ioe); + } + } +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryCommentDao.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryCommentDao.java new file mode 100644 index 0000000000000000000000000000000000000000..e70be6d0e371069d344a3ffc4a773a394d584758 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryCommentDao.java @@ -0,0 +1,9 @@ +package org.duniter.elasticsearch.gchange.dao.registry; + +import org.duniter.elasticsearch.gchange.dao.CommentDao; + +/** + * Created by blavenie on 03/04/17. + */ +public interface RegistryCommentDao extends CommentDao { +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryCommentDaoImpl.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryCommentDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..c9d4b28ad4f71078b88ebc76800971cdeee2671b --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryCommentDaoImpl.java @@ -0,0 +1,17 @@ +package org.duniter.elasticsearch.gchange.dao.registry; + +import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.gchange.dao.AbstractCommentDaoImpl; +import org.elasticsearch.common.inject.Inject; + +/** + * Created by blavenie on 03/04/17. + */ +public class RegistryCommentDaoImpl extends AbstractCommentDaoImpl implements RegistryCommentDao { + + + @Inject + public RegistryCommentDaoImpl(PluginSettings pluginSettings) { + super(RegistryIndexDao.INDEX, pluginSettings); + } +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryIndexDao.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryIndexDao.java new file mode 100644 index 0000000000000000000000000000000000000000..f803d1733524c1e3e163c3a5d6ba6847afacffe3 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryIndexDao.java @@ -0,0 +1,11 @@ +package org.duniter.elasticsearch.gchange.dao.registry; + +import org.duniter.elasticsearch.dao.IndexDao; + +/** + * Created by blavenie on 03/04/17. + */ +public interface RegistryIndexDao extends IndexDao<RegistryIndexDao> { + String INDEX = "registry"; + String CATEGORY_TYPE = "category"; +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryIndexDaoImpl.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryIndexDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..c62ded0962cd57c6008e7c2ba9aa88a1a9a85841 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryIndexDaoImpl.java @@ -0,0 +1,117 @@ +package org.duniter.elasticsearch.gchange.dao.registry; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.duniter.core.exception.TechnicalException; +import org.duniter.elasticsearch.dao.AbstractIndexDao; +import org.duniter.elasticsearch.dao.AbstractIndexTypeDao; +import org.duniter.elasticsearch.dao.IndexDao; +import org.duniter.elasticsearch.dao.IndexTypeDao; +import org.duniter.elasticsearch.dao.handler.AddSequenceAttributeHandler; +import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.gchange.dao.AbstractCommentDaoImpl; +import org.duniter.elasticsearch.gchange.dao.AbstractRecordDaoImpl; +import org.duniter.elasticsearch.gchange.dao.CommentDao; +import org.duniter.elasticsearch.gchange.dao.RecordDao; +import org.duniter.elasticsearch.gchange.service.RegistryService; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.inject.Injector; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; + +import java.io.IOException; + +/** + * Created by blavenie on 03/04/17. + */ +public class RegistryIndexDaoImpl extends AbstractIndexDao<RegistryIndexDao> implements RegistryIndexDao { + + + private static final String CATEGORIES_BULK_CLASSPATH_FILE = "registry-categories-bulk-insert.json"; + + private PluginSettings pluginSettings; + private IndexTypeDao<?> categoryDao; + private RecordDao recordDao; + private CommentDao commentDao; + + @Inject + public RegistryIndexDaoImpl(PluginSettings pluginSettings, RegistryRecordDao recordDao, RegistryCommentDao commentDao) { + super(RegistryIndexDao.INDEX); + + this.pluginSettings = pluginSettings; + this.commentDao = commentDao; + this.recordDao = recordDao; + this.categoryDao = createCategoryDao(pluginSettings); + } + + + @Override + protected void 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(recordDao.getType(), recordDao.createTypeMapping()); + createIndexRequestBuilder.addMapping(commentDao.getType(), commentDao.createTypeMapping()); + createIndexRequestBuilder.addMapping(categoryDao.getType(), categoryDao.createTypeMapping()); + createIndexRequestBuilder.execute().actionGet(); + + // Fill categories + fillRecordCategories(); + } + + public void fillRecordCategories() { + if (logger.isDebugEnabled()) { + logger.debug(String.format("[%s/%s] Fill data", INDEX, RegistryIndexDao.CATEGORY_TYPE)); + } + + // Insert categories + categoryDao.bulkFromClasspathFile(CATEGORIES_BULK_CLASSPATH_FILE, + // Add order attribute + new AddSequenceAttributeHandler("order", "\\{.*\"name\".*\\}", 1)); + } + + + protected IndexTypeDao<?> createCategoryDao(final PluginSettings settings) { + return new AbstractIndexTypeDao(INDEX, RegistryIndexDao.CATEGORY_TYPE) { + @Override + protected void createIndex() throws JsonProcessingException { + throw new TechnicalException("not implemented"); + } + + @Override + public XContentBuilder createTypeMapping() { + try { + XContentBuilder mapping = XContentFactory.jsonBuilder().startObject() + .startObject(getType()) + .startObject("properties") + + // name + .startObject("name") + .field("type", "string") + .field("analyzer", settings.getDefaultStringAnalyzer()) + .endObject() + + // parent + .startObject("parent") + .field("type", "string") + .field("index", "not_analyzed") + .endObject() + + .endObject() + .endObject().endObject(); + + return mapping; + } + catch(IOException ioe) { + throw new TechnicalException(String.format("Error while getting mapping for index [%s/%s]: %s", getIndex(), getType(), ioe.getMessage()), ioe); + } + } + }; + } +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryRecordDao.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryRecordDao.java new file mode 100644 index 0000000000000000000000000000000000000000..163ebea84380c11af3bb8f4a977c97b84cc68979 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryRecordDao.java @@ -0,0 +1,9 @@ +package org.duniter.elasticsearch.gchange.dao.registry; + +import org.duniter.elasticsearch.gchange.dao.RecordDao; + +/** + * Created by blavenie on 03/04/17. + */ +public interface RegistryRecordDao extends RecordDao { +} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryRecordDaoImpl.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryRecordDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..b9ae257b326024b1d88d4d197b5729326c060f70 --- /dev/null +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/dao/registry/RegistryRecordDaoImpl.java @@ -0,0 +1,16 @@ +package org.duniter.elasticsearch.gchange.dao.registry; + +import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.gchange.dao.AbstractRecordDaoImpl; +import org.elasticsearch.common.inject.Inject; + +/** + * Created by blavenie on 03/04/17. + */ +public class RegistryRecordDaoImpl extends AbstractRecordDaoImpl implements RegistryRecordDao { + + @Inject + public RegistryRecordDaoImpl(PluginSettings pluginSettings) { + super(RegistryIndexDao.INDEX, pluginSettings); + } +} 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 index 7aa19360824d38cc8b56850dcbe924169f8d2c36..f82ce5a75403b02fcede06741eb484dec6a7e4f6 100644 --- 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 @@ -22,8 +22,8 @@ package org.duniter.elasticsearch.gchange.rest.market; * #L% */ +import org.duniter.elasticsearch.gchange.dao.market.MarketIndexDao; 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; @@ -32,7 +32,7 @@ public class RestMarketCategoryAction { @Inject public RestMarketCategoryAction(RestSecurityController securityController) { // Add security rule for category - securityController.allowIndexType(RestRequest.Method.GET, MarketService.INDEX, MarketService.RECORD_CATEGORY_TYPE); + securityController.allowIndexType(RestRequest.Method.GET, MarketIndexDao.INDEX, MarketIndexDao.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 index a65f154c946d241c15f93a7416d69fc1311cd3ae..e0a39533181d76af21abf5a5c1234d73323630b1 100644 --- 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 @@ -22,6 +22,8 @@ package org.duniter.elasticsearch.gchange.rest.market; * #L% */ +import org.duniter.elasticsearch.gchange.dao.market.MarketCommentDao; +import org.duniter.elasticsearch.gchange.dao.market.MarketIndexDao; import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.gchange.service.MarketService; @@ -36,7 +38,7 @@ public class RestMarketCommentIndexAction extends AbstractRestPostIndexAction { public RestMarketCommentIndexAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, MarketService service) { super(settings, controller, client, securityController, - MarketService.INDEX, MarketService.RECORD_COMMENT_TYPE, + MarketIndexDao.INDEX, MarketCommentDao.TYPE, json -> service.indexCommentFromJson(json)); } 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 index 7872d14dd5271f519f801beee0a07fee12b6f9c9..0052f6aafa158b32eacd1d607e9e0bb8c8bb68a3 100644 --- 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 @@ -22,6 +22,8 @@ package org.duniter.elasticsearch.gchange.rest.market; * #L% */ +import org.duniter.elasticsearch.gchange.dao.market.MarketCommentDao; +import org.duniter.elasticsearch.gchange.dao.market.MarketIndexDao; import org.duniter.elasticsearch.rest.AbstractRestPostUpdateAction; import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.gchange.service.MarketService; @@ -36,7 +38,7 @@ public class RestMarketCommentUpdateAction extends AbstractRestPostUpdateAction public RestMarketCommentUpdateAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, MarketService service) { super(settings, controller, client, securityController, - MarketService.INDEX, MarketService.RECORD_COMMENT_TYPE, + MarketIndexDao.INDEX, MarketCommentDao.TYPE, (id, json) -> service.updateCommentFromJson(id, json)); } diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketImageAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketImageAction.java index 20a075618736885d36bc21f3d679a283314d46f2..dbc054d50def487ac70f8652d8ca7dd3d3f85163 100644 --- a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketImageAction.java +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/market/RestMarketImageAction.java @@ -22,6 +22,8 @@ package org.duniter.elasticsearch.gchange.rest.market; * #L% */ +import org.duniter.elasticsearch.gchange.dao.market.MarketIndexDao; +import org.duniter.elasticsearch.gchange.dao.market.MarketRecordDao; import org.duniter.elasticsearch.gchange.model.market.MarketRecord; import org.duniter.elasticsearch.gchange.model.registry.RegistryRecord; import org.duniter.elasticsearch.gchange.service.MarketService; @@ -36,7 +38,7 @@ public class RestMarketImageAction { public RestMarketImageAction(RestSecurityController securityController) { // Allow to get thumbnail - securityController.allowImageAttachment(MarketService.INDEX, MarketService.RECORD_TYPE, MarketRecord.PROPERTY_THUMBNAIL); + securityController.allowImageAttachment(MarketIndexDao.INDEX, MarketRecordDao.TYPE, MarketRecord.PROPERTY_THUMBNAIL); // TODO : allow to get pictures } 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 index 55f4c5fbf4b093e9442a14621f734656cc9f4fe2..7af1deee1241626079c0dc5238852db60bd6cac1 100644 --- 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 @@ -22,6 +22,8 @@ package org.duniter.elasticsearch.gchange.rest.market; * #L% */ +import org.duniter.elasticsearch.gchange.dao.market.MarketIndexDao; +import org.duniter.elasticsearch.gchange.dao.market.MarketRecordDao; import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.gchange.service.MarketService; @@ -36,7 +38,7 @@ public class RestMarketRecordIndexAction extends AbstractRestPostIndexAction { public RestMarketRecordIndexAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, MarketService service) { super(settings, controller, client, securityController, - MarketService.INDEX, MarketService.RECORD_TYPE, + MarketIndexDao.INDEX, MarketRecordDao.TYPE, json -> service.indexRecordFromJson(json)); } 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 index 258dc66870bf700ce07e3247acf3afa121c0c5cd..7fd0a76e0907402d6a426f24a1962d31533734ff 100644 --- 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 @@ -22,9 +22,11 @@ package org.duniter.elasticsearch.gchange.rest.market; * #L% */ +import org.duniter.elasticsearch.gchange.dao.market.MarketIndexDao; +import org.duniter.elasticsearch.gchange.dao.market.MarketRecordDao; +import org.duniter.elasticsearch.gchange.service.MarketService; 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; @@ -36,7 +38,7 @@ public class RestMarketRecordUpdateAction extends AbstractRestPostUpdateAction { public RestMarketRecordUpdateAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, MarketService service) { super(settings, controller, client, securityController, - MarketService.INDEX, MarketService.RECORD_TYPE, + MarketIndexDao.INDEX, MarketRecordDao.TYPE, (id, json) -> service.updateRecordFromJson(id, json)); } 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 index 0fdb34fa17095d187e74a24269d5c3e30f36ec7c..ff58d64bcb6b65cd5f2d321b66410147daaeaea7 100644 --- 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 @@ -22,6 +22,7 @@ package org.duniter.elasticsearch.gchange.rest.registry; * #L% */ +import org.duniter.elasticsearch.gchange.dao.registry.RegistryIndexDao; import org.duniter.elasticsearch.gchange.service.RegistryService; import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.elasticsearch.common.inject.Inject; @@ -32,7 +33,7 @@ public class RestRegistryCategoryAction { @Inject public RestRegistryCategoryAction(RestSecurityController securityController) { // Add security rule for category - securityController.allowIndexType(RestRequest.Method.GET, RegistryService.INDEX, RegistryService.RECORD_CATEGORY_TYPE); + securityController.allowIndexType(RestRequest.Method.GET, RegistryIndexDao.INDEX, RegistryIndexDao.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 index bd0047608733aca88299aa1b8a577db679aefb01..7ee815c944e65835bd32e0758803ceb53e0220f8 100644 --- 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 @@ -22,6 +22,8 @@ package org.duniter.elasticsearch.gchange.rest.registry; * #L% */ +import org.duniter.elasticsearch.gchange.dao.registry.RegistryCommentDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryIndexDao; import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.gchange.service.RegistryService; @@ -36,7 +38,7 @@ public class RestRegistryCommentIndexAction extends AbstractRestPostIndexAction public RestRegistryCommentIndexAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, RegistryService service) { super(settings, controller, client, securityController, - RegistryService.INDEX, RegistryService.RECORD_COMMENT_TYPE, + RegistryIndexDao.INDEX, RegistryCommentDao.TYPE, json -> service.indexCommentFromJson(json)); } 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 index 5b4433b4894635212b78f2d71164abb4b706fe43..e724f60bf5cef85dc8c6f4dc66c2c51040466f18 100644 --- 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 @@ -22,9 +22,11 @@ package org.duniter.elasticsearch.gchange.rest.registry; * #L% */ +import org.duniter.elasticsearch.gchange.dao.registry.RegistryCommentDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryIndexDao; +import org.duniter.elasticsearch.gchange.service.RegistryService; 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; @@ -36,7 +38,7 @@ public class RestRegistryCommentUpdateAction extends AbstractRestPostUpdateActio public RestRegistryCommentUpdateAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, RegistryService service) { super(settings, controller, client, securityController, - RegistryService.INDEX, RegistryService.RECORD_COMMENT_TYPE, + RegistryIndexDao.INDEX, RegistryCommentDao.TYPE, (id, json) -> service.updateCommentFromJson(id, json)); } diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryImageAction.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryImageAction.java index 7a2293955671bc79c62c2441fed93c3cf71bbba5..197ba6066dd9004c64e43762387ae459a583325e 100644 --- a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryImageAction.java +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/rest/registry/RestRegistryImageAction.java @@ -22,6 +22,9 @@ package org.duniter.elasticsearch.gchange.rest.registry; * #L% */ +import org.duniter.elasticsearch.gchange.dao.registry.RegistryCommentDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryIndexDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryRecordDao; import org.duniter.elasticsearch.gchange.model.registry.RegistryRecord; import org.duniter.elasticsearch.gchange.service.RegistryService; import org.duniter.elasticsearch.rest.security.RestSecurityController; @@ -35,7 +38,7 @@ public class RestRegistryImageAction { public RestRegistryImageAction(RestSecurityController securityController) { // Allow to get thumbnail - securityController.allowImageAttachment(RegistryService.INDEX, RegistryService.RECORD_TYPE, RegistryRecord.PROPERTY_THUMBNAIL); + securityController.allowImageAttachment(RegistryIndexDao.INDEX, RegistryRecordDao.TYPE, RegistryRecord.PROPERTY_THUMBNAIL); // TODO : allow to get pictures } 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 index 02c2e963b271e0ef09d21e116498284a417044dd..297562526cf4b88ee97016a223618902e545545e 100644 --- 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 @@ -22,6 +22,8 @@ package org.duniter.elasticsearch.gchange.rest.registry; * #L% */ +import org.duniter.elasticsearch.gchange.dao.registry.RegistryIndexDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryRecordDao; import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction; import org.duniter.elasticsearch.rest.security.RestSecurityController; import org.duniter.elasticsearch.gchange.service.RegistryService; @@ -37,7 +39,7 @@ public class RestRegistryRecordIndexAction extends AbstractRestPostIndexAction { public RestRegistryRecordIndexAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, RegistryService service) { super(settings, controller, client, securityController, - RegistryService.INDEX, RegistryService.RECORD_TYPE, + RegistryIndexDao.INDEX, RegistryRecordDao.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 index 9942fc7fa304dd85d21c508b6efe0ad0b0cd90e2..4131c882dac6b3142f192462c16f9611e72ef8e8 100644 --- 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 @@ -22,9 +22,11 @@ package org.duniter.elasticsearch.gchange.rest.registry; * #L% */ +import org.duniter.elasticsearch.gchange.dao.registry.RegistryIndexDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryRecordDao; +import org.duniter.elasticsearch.gchange.service.RegistryService; 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; @@ -36,7 +38,7 @@ public class RestRegistryRecordUpdateAction extends AbstractRestPostUpdateAction public RestRegistryRecordUpdateAction(Settings settings, RestController controller, Client client, RestSecurityController securityController, RegistryService service) { super(settings, controller, client, securityController, - RegistryService.INDEX, RegistryService.RECORD_TYPE, + RegistryIndexDao.INDEX, RegistryRecordDao.TYPE, (id, json) -> service.updateRecordFromJson(id, json)); } diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/AbstractService.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/AbstractService.java index e394717a2eaec4c0d0633179fc1ed4d7cd22eabf..9ac606915729fb4b9c6370e5daf2b12191dc232b 100644 --- a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/AbstractService.java +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/AbstractService.java @@ -23,6 +23,7 @@ package org.duniter.elasticsearch.gchange.service; */ import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.gchange.PluginSettings; import org.elasticsearch.client.Client; @@ -33,19 +34,19 @@ public abstract class AbstractService extends org.duniter.elasticsearch.user.ser protected PluginSettings pluginSettings; - public AbstractService(String loggerName, Client client, PluginSettings pluginSettings) { + public AbstractService(String loggerName, Duniter4jClient client, PluginSettings pluginSettings) { this(loggerName, client, pluginSettings, null); } - public AbstractService(Client client, PluginSettings pluginSettings) { + public AbstractService(Duniter4jClient client, PluginSettings pluginSettings) { this(client, pluginSettings, null); } - public AbstractService(Client client, PluginSettings pluginSettings, CryptoService cryptoService) { + public AbstractService(Duniter4jClient client, PluginSettings pluginSettings, CryptoService cryptoService) { this("duniter.gchange", client, pluginSettings, cryptoService); } - public AbstractService(String loggerName, Client client, PluginSettings pluginSettings, CryptoService cryptoService) { + public AbstractService(String loggerName, Duniter4jClient client, PluginSettings pluginSettings, CryptoService cryptoService) { super(loggerName, client, pluginSettings.getDelegate(), cryptoService); this.pluginSettings = pluginSettings; } diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CommentService.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CommentService.java deleted file mode 100644 index 8bd5dff7f200e303a38f718f3efea1d0addd13c1..0000000000000000000000000000000000000000 --- a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CommentService.java +++ /dev/null @@ -1,254 +0,0 @@ -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.databind.JsonNode; -import org.apache.commons.collections4.MapUtils; -import org.duniter.core.client.model.elasticsearch.RecordComment; -import org.duniter.core.exception.TechnicalException; -import org.duniter.core.service.CryptoService; -import org.duniter.elasticsearch.exception.DocumentNotFoundException; -import org.duniter.elasticsearch.exception.NotFoundException; -import org.duniter.elasticsearch.gchange.PluginSettings; -import org.duniter.elasticsearch.gchange.model.event.GchangeEventCodes; -import org.duniter.elasticsearch.gchange.model.market.MarketRecord; -import org.duniter.elasticsearch.threadpool.ThreadPool; -import org.duniter.elasticsearch.user.model.UserEvent; -import org.duniter.elasticsearch.user.service.HistoryService; -import org.duniter.elasticsearch.user.service.UserEventService; -import org.duniter.elasticsearch.user.service.UserService; -import org.elasticsearch.action.search.SearchPhaseExecutionException; -import org.elasticsearch.action.search.SearchRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.action.search.SearchType; -import org.elasticsearch.client.Client; -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.query.TermQueryBuilder; -import org.nuiton.i18n.I18n; - -import java.io.IOException; -import java.util.Map; - -/** - * Created by Benoit on 30/03/2015. - */ -public class CommentService extends AbstractService { - - private UserEventService userEventService; - private UserService userService; - private ThreadPool threadPool; - private HistoryService historyService; - - @Inject - public CommentService(Client client, - PluginSettings pluginSettings, - CryptoService cryptoService, - UserService userService, - UserEventService userEventService, - HistoryService historyService, - ThreadPool threadPool) { - super("gchange.comment", client, pluginSettings, cryptoService); - this.userEventService = userEventService; - this.userService = userService; - this.historyService = historyService; - this.threadPool = threadPool; - } - - - public String indexCommentFromJson(final String index, final String recordType, final String type, final String json) { - JsonNode commentObj = readAndVerifyIssuerSignature(json); - String issuer = getMandatoryField(commentObj, RecordComment.PROPERTY_ISSUER).asText(); - - // Check the record document exists - String recordId = getMandatoryField(commentObj, RecordComment.PROPERTY_RECORD).asText(); - checkDocumentExistsOrDeleted(index, recordType, recordId); - - if (logger.isDebugEnabled()) { - logger.debug(String.format("Indexing a %s from issuer [%s]", type, issuer.substring(0, 8))); - } - return indexDocumentFromJson(index, type, json); - } - - public void updateCommentFromJson(final String index, final String recordType, final String type, final String id, final String json) { - JsonNode commentObj = readAndVerifyIssuerSignature(json); - - // Check the record document exists - String recordId = getMandatoryField(commentObj, RecordComment.PROPERTY_RECORD).asText(); - checkDocumentExistsOrDeleted(index, recordType, recordId); - - if (logger.isDebugEnabled()) { - String issuer = getMandatoryField(commentObj, RecordComment.PROPERTY_ISSUER).asText(); - logger.debug(String.format("[%s] Indexing a %s from issuer [%s] on [%s]", index, type, issuer.substring(0, 8))); - } - - updateDocumentFromJson(index, type, id, json); - } - - public XContentBuilder createRecordCommentType(String index, String type) { - String stringAnalyzer = pluginSettings.getDefaultStringAnalyzer(); - - try { - XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(type) - .startObject("properties") - - // issuer - .startObject("issuer") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // time - .startObject("time") - .field("type", "integer") - .endObject() - - // message - .startObject("message") - .field("type", "string") - .field("analyzer", stringAnalyzer) - .endObject() - - // record - .startObject("record") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // reply to - .startObject("reply_to") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // aggregations - .startObject("aggregations") - .field("type", "nested") - .field("dynamic", "true") - .startObject("properties") - .startObject("reply_count") - .field("type", "integer") - .field("index", "not_analyzed") - .endObject() - .endObject() - .endObject() - - .endObject() - .endObject().endObject(); - - return mapping; - } - catch(IOException ioe) { - throw new TechnicalException(String.format("Error while getting mapping for index [%s/%s]: %s", index, type, ioe.getMessage()), ioe); - } - } - - - /* -- Internal methods -- */ - - // Check the record document exists (or has been deleted) - private void checkDocumentExistsOrDeleted(String index, String type, String id) { - boolean recordExists; - try { - recordExists = isDocumentExists(index, type, id); - } catch (NotFoundException e) { - // Check if exists in delete history - recordExists = historyService.existsInDeleteHistory(index, type, id); - } - if (!recordExists) { - throw new NotFoundException(String.format("Comment refers a non-existent document [%s/%s/%s].", index, type, id)); - } - } - - /** - * Notify user when new comment - */ - private void notifyRecordIssuerForComment(final String index, final String recordType, JsonNode actualObj, boolean isNewComment, String commentId) { - String issuer = getMandatoryField(actualObj, RecordComment.PROPERTY_ISSUER).asText(); - - // Notify issuer of record (is not same as comment writer) - String recordId = getMandatoryField(actualObj, RecordComment.PROPERTY_RECORD).asText(); - Map<String, Object> recordFields = getFieldsById(index, recordType, recordId, - MarketRecord.PROPERTY_TITLE, MarketRecord.PROPERTY_ISSUER); - if (MapUtils.isEmpty(recordFields)) { // record not found - throw new DocumentNotFoundException(I18n.t("duniter.market.error.comment.recordNotFound", recordId)); - } - String recordIssuer = recordFields.get(MarketRecord.PROPERTY_ISSUER).toString(); - - // Get user title - String issuerTitle = userService.getProfileTitle(issuer); - - String recordTitle = recordFields.get(MarketRecord.PROPERTY_TITLE).toString(); - if (!issuer.equals(recordIssuer)) { - userEventService.notifyUser( - UserEvent.newBuilder(UserEvent.EventType.INFO, GchangeEventCodes.NEW_COMMENT.name()) - .setMessage( - isNewComment ? I18n.n("duniter.market.event.newComment") : I18n.n("duniter.market.event.updateComment"), - issuer, - issuerTitle != null ? issuerTitle : issuer.substring(0, 8), - recordTitle - ) - .setRecipient(recordIssuer) - .setReference(index, recordType, recordId) - .setReferenceAnchor(commentId) - .build()); - } - } - - private void updateCommentAggregations(String index, String type, String id) { - long replyCount = countCommentReplies(index, type, id); - if (replyCount > 0) { - logger.warn("Comment [%s] has %s replies. Need to be updated", id, replyCount); - // TODO update aggregations - } - } - - private long countCommentReplies(String index, String type, String id) { - - // Prepare count request - SearchRequestBuilder searchRequest = client - .prepareSearch(index) - .setTypes(type) - .setFetchSource(false) - .setSearchType(SearchType.QUERY_AND_FETCH) - .setSize(0); - - // Query = filter on reference - TermQueryBuilder query = QueryBuilders.termQuery(RecordComment.PROPERTY_REPLY_TO_JSON, id); - searchRequest.setQuery(query); - - // Execute query - try { - SearchResponse response = searchRequest.execute().actionGet(); - return response.getHits().getTotalHits(); - } - catch(SearchPhaseExecutionException e) { - // Failed or no item on index - logger.error(String.format("Error while counting comment replies: %s", e.getMessage()), e); - } - return 1; - } -} diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CommentUserEventService.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CommentUserEventService.java index bc8bb500157a8069e4d73f839e90b81c5bca3f66..76da0a6e0267fe649aab2a9eba646a5d7f22f8c0 100644 --- a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CommentUserEventService.java +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/CommentUserEventService.java @@ -25,28 +25,28 @@ package org.duniter.elasticsearch.gchange.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import org.apache.commons.collections4.MapUtils; import org.duniter.core.client.model.ModelUtils; -import org.duniter.core.client.model.bma.jackson.JacksonUtils; import org.duniter.core.client.model.elasticsearch.RecordComment; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; import org.duniter.core.util.StringUtils; -import org.duniter.core.util.websocket.WebsocketClientEndpoint; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.gchange.PluginSettings; +import org.duniter.elasticsearch.gchange.dao.RecordDao; +import org.duniter.elasticsearch.gchange.dao.market.MarketCommentDao; +import org.duniter.elasticsearch.gchange.dao.market.MarketIndexDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryCommentDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryIndexDao; import org.duniter.elasticsearch.gchange.model.event.GchangeEventCodes; import org.duniter.elasticsearch.gchange.model.market.MarketRecord; -import org.duniter.elasticsearch.service.BlockchainService; import org.duniter.elasticsearch.service.changes.ChangeEvent; import org.duniter.elasticsearch.service.changes.ChangeService; import org.duniter.elasticsearch.service.changes.ChangeSource; import org.duniter.elasticsearch.user.model.UserEvent; -import org.duniter.elasticsearch.user.model.UserEventCodes; import org.duniter.elasticsearch.user.service.UserEventService; import org.duniter.elasticsearch.user.service.UserService; -import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; import org.nuiton.i18n.I18n; @@ -74,43 +74,30 @@ public class CommentUserEventService extends AbstractService implements ChangeSe I18n.n("duniter.registry.event.updateReplyComment"); } - public final UserService userService; - - public final UserEventService userEventService; - - public final ObjectMapper objectMapper; - - public final List<ChangeSource> changeListenSources; - - public final boolean enable; - - public final String recordType; - - public final boolean trace; + private final UserService userService; + private final UserEventService userEventService; + private final List<ChangeSource> changeListenSources; + private final String recordType; + private final boolean trace; @Inject - public CommentUserEventService(Client client, PluginSettings settings, CryptoService cryptoService, - BlockchainService blockchainService, + public CommentUserEventService(Duniter4jClient client, + PluginSettings settings, + CryptoService cryptoService, UserService userService, UserEventService userEventService) { super("duniter.user.event.comment", client, settings, cryptoService); this.userService = userService; this.userEventService = userEventService; - this.objectMapper = JacksonUtils.newObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); this.changeListenSources = ImmutableList.of( - new ChangeSource(MarketService.INDEX, MarketService.RECORD_COMMENT_TYPE), - new ChangeSource(RegistryService.INDEX, RegistryService.RECORD_COMMENT_TYPE)); + new ChangeSource(MarketIndexDao.INDEX, MarketCommentDao.TYPE), + new ChangeSource(RegistryIndexDao.INDEX, RegistryCommentDao.TYPE)); ChangeService.registerListener(this); - this.enable = pluginSettings.enableBlockchainSync(); this.trace = logger.isTraceEnabled(); - this.recordType = MarketService.RECORD_TYPE; // same as RegistryService.RECORD_TYPE - - if (this.enable) { - blockchainService.registerConnectionListener(createConnectionListeners()); - } + this.recordType = RecordDao.TYPE; } @Override @@ -152,48 +139,6 @@ public class CommentUserEventService extends AbstractService implements ChangeSe /* -- internal method -- */ - /** - * Create a listener that notify admin when the Duniter node connection is lost or retrieve - */ - private WebsocketClientEndpoint.ConnectionListener createConnectionListeners() { - return new WebsocketClientEndpoint.ConnectionListener() { - private boolean errorNotified = false; - - @Override - public void onSuccess() { - // Send notify on reconnection - if (errorNotified) { - errorNotified = false; - userEventService.notifyAdmin(UserEvent.newBuilder(UserEvent.EventType.INFO, UserEventCodes.NODE_BMA_UP.name()) - .setMessage(I18n.n("duniter.event.NODE_BMA_UP"), - pluginSettings.getNodeBmaHost(), - String.valueOf(pluginSettings.getNodeBmaPort()), - pluginSettings.getClusterName()) - .build()); - } - } - - @Override - public void onError(Exception e, long lastTimeUp) { - if (errorNotified) return; // already notify - - // Wait 1 min, then notify admin (once) - long now = System.currentTimeMillis() / 1000; - boolean wait = now - lastTimeUp < 60; - if (!wait) { - errorNotified = true; - userEventService.notifyAdmin(UserEvent.newBuilder(UserEvent.EventType.ERROR, UserEventCodes.NODE_BMA_DOWN.name()) - .setMessage(I18n.n("duniter.event.NODE_BMA_DOWN"), - pluginSettings.getNodeBmaHost(), - String.valueOf(pluginSettings.getNodeBmaPort()), - pluginSettings.getClusterName(), - String.valueOf(lastTimeUp)) - .build()); - } - } - }; - } - /** * Send notification from a new comment * @@ -238,7 +183,7 @@ public class CommentUserEventService extends AbstractService implements ChangeSe GchangeEventCodes eventCodeForParentCommentIssuer, String messageKeyForParentCommentIssuer) { // Get record issuer String recordId = comment.getRecord(); - Map<String, Object> record = getFieldsById(index, this.recordType, recordId, + Map<String, Object> record = client.getFieldsById(index, this.recordType, recordId, MarketRecord.PROPERTY_TITLE, MarketRecord.PROPERTY_ISSUER); // Record not found : nothing to emit @@ -275,7 +220,7 @@ public class CommentUserEventService extends AbstractService implements ChangeSe // Notify comment is a reply to another comment if (StringUtils.isNotBlank(comment.getReplyTo())) { - String parentCommentIssuer = getTypedFieldById(index, type, comment.getReplyTo(), RecordComment.PROPERTY_ISSUER); + String parentCommentIssuer = client.getTypedFieldById(index, type, comment.getReplyTo(), RecordComment.PROPERTY_ISSUER); if (StringUtils.isNotBlank(parentCommentIssuer) && !issuer.equals(parentCommentIssuer) && diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/MarketService.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/MarketService.java index 9e9a5a208366d26d54b018fb37faedfd7a076f49..78581d99962e758a6ee1953867c8e3f7a6945816 100644 --- a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/MarketService.java +++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/MarketService.java @@ -23,367 +23,128 @@ package org.duniter.elasticsearch.gchange.service; */ -import com.fasterxml.jackson.core.JsonProcessingException; -import org.duniter.core.client.service.bma.WotRemoteService; -import org.duniter.core.exception.TechnicalException; +import com.fasterxml.jackson.databind.JsonNode; +import org.duniter.core.client.model.elasticsearch.RecordComment; import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.client.Duniter4jClient; +import org.duniter.elasticsearch.exception.NotFoundException; import org.duniter.elasticsearch.gchange.PluginSettings; -import org.duniter.elasticsearch.gchange.service.AbstractService; -import org.duniter.elasticsearch.service.ServiceLocator; -import org.duniter.elasticsearch.threadpool.ThreadPool; -import org.duniter.elasticsearch.user.service.UserEventService; -import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; -import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.index.IndexResponse; +import org.duniter.elasticsearch.gchange.dao.market.MarketCommentDao; +import org.duniter.elasticsearch.gchange.dao.market.MarketIndexDao; +import org.duniter.elasticsearch.gchange.dao.market.MarketRecordDao; +import org.duniter.elasticsearch.user.service.HistoryService; import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.settings.Settings; -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 MarketService extends AbstractService { - public static final String INDEX = "market"; - public static final String RECORD_CATEGORY_TYPE = "category"; - public static final String RECORD_TYPE = "record"; - public static final String RECORD_COMMENT_TYPE = "comment"; - - private static final String CATEGORIES_BULK_CLASSPATH_FILE = "market-categories-bulk-insert.json"; - - private WotRemoteService wotRemoteService; - private UserEventService userEventService; - private CommentService commentService; + private MarketIndexDao indexDao; + private MarketRecordDao recordDao; + private MarketCommentDao commentDao; + private HistoryService historyService; @Inject - public MarketService(Client client, PluginSettings settings, + public MarketService(Duniter4jClient client, PluginSettings settings, CryptoService cryptoService, - CommentService commentService, - UserEventService userEventService, - ThreadPool threadPool, - final ServiceLocator serviceLocator + HistoryService historyService, + MarketIndexDao indexDao, + MarketCommentDao commentDao, + MarketRecordDao recordDao ) { - super("gchange." + INDEX, client, settings, cryptoService); - this.commentService = commentService; - this.userEventService = userEventService; - threadPool.scheduleOnStarted(() -> { - wotRemoteService = serviceLocator.getWotRemoteService(); - }); - } + super("gchange.service.market", client, settings, cryptoService); + this.indexDao = indexDao; + this.commentDao = commentDao; + this.recordDao = recordDao; - /** - * Delete blockchain index, and all data - * @throws JsonProcessingException - */ - public MarketService deleteIndex() { - deleteIndexIfExists(INDEX); - return this; + this.historyService = historyService; } - public boolean existsIndex() { - return super.existsIndex(INDEX); - } /** * Create index need for blockchain registry, if need */ public MarketService createIndexIfNotExists() { - try { - if (!existsIndex(INDEX)) { - createIndex(); - - // Fill categories - fillRecordCategories(); - } - } - catch(JsonProcessingException e) { - throw new TechnicalException(String.format("Error while creating index [%s]", INDEX)); - } - + indexDao.createIndexIfNotExists(); return this; } - /** - * Create index need for category registry - * @throws JsonProcessingException - */ - public MarketService createIndex() throws JsonProcessingException { - logger.info(String.format("Creating index [%s]", INDEX)); - - CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(INDEX); - Settings indexSettings = Settings.settingsBuilder() - .put("number_of_shards", 2) - .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, commentService.createRecordCommentType(INDEX, RECORD_COMMENT_TYPE)); - createIndexRequestBuilder.execute().actionGet(); - + public MarketService deleteIndex() { + indexDao.deleteIndex(); return this; } - /** - * - * @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); + public String indexRecordFromJson(String json) { + JsonNode actualObj = readAndVerifyIssuerSignature(json); + String issuer = getIssuer(actualObj); - // Execute indexBlocksFromNode - IndexResponse response = indexRequest - .setRefresh(true) - .execute().actionGet(); + if (logger.isDebugEnabled()) { + logger.debug(String.format("Indexing a %s from issuer [%s]", recordDao.getType(), issuer.substring(0, 8))); + } - return response.getId(); - } - - public String indexRecordFromJson(String json) { - return checkIssuerAndIndexDocumentFromJson(INDEX, RECORD_TYPE, json); + return recordDao.create(json); } public void updateRecordFromJson(String id, String json) { - checkIssuerAndUpdateDocumentFromJson(INDEX, RECORD_TYPE, id, json); - } - - public String indexCommentFromJson(String json) { - return commentService.indexCommentFromJson(INDEX, RECORD_TYPE, RECORD_COMMENT_TYPE, json); - } + JsonNode actualObj = readAndVerifyIssuerSignature(json); + String issuer = getIssuer(actualObj); - public void updateCommentFromJson(String id, String json) { - commentService.updateCommentFromJson(INDEX, RECORD_TYPE, RECORD_COMMENT_TYPE, id, json); - } + // Check same document issuer + recordDao.checkSameDocumentIssuer(id, issuer); - public MarketService fillRecordCategories() { if (logger.isDebugEnabled()) { - logger.debug(String.format("[%s/%s] Fill data", INDEX, RECORD_CATEGORY_TYPE)); + logger.debug(String.format("Updating %s [%s] from issuer [%s]", recordDao.getType(), id, issuer.substring(0, 8))); } - // Insert categories - bulkFromClasspathFile(CATEGORIES_BULK_CLASSPATH_FILE, INDEX, RECORD_CATEGORY_TYPE, - // Add order attribute (auto incremented) - new AddSequenceAttributeHandler("order", "\\{.*\"name\".*\\}", 1)); - - return this; + recordDao.update(id, json); } - /* -- Internal methods -- */ - - - public XContentBuilder createRecordCategoryType() { - try { - XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(RECORD_CATEGORY_TYPE) - .startObject("properties") - - // name - .startObject("name") - .field("type", "string") - .endObject() - - // order - .startObject("order") - .field("type", "integer") - .endObject() - - // description - /*.startObject("description") - .field("type", "string") - .endObject()*/ - - // parent - .startObject("parent") - .field("type", "string") - .endObject() - - // tags - /*.startObject("tags") - .field("type", "completion") - .field("search_analyzer", "simple") - .field("analyzer", "simple") - .field("preserve_separators", "false") - .endObject()*/ + public String indexCommentFromJson(String json) { + JsonNode commentObj = readAndVerifyIssuerSignature(json); + String issuer = getMandatoryField(commentObj, RecordComment.PROPERTY_ISSUER).asText(); - .endObject() - .endObject().endObject(); + // Check the record document exists + String recordId = getMandatoryField(commentObj, RecordComment.PROPERTY_RECORD).asText(); + checkRecordExistsOrDeleted(recordId); - 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); + if (logger.isDebugEnabled()) { + logger.debug(String.format("Indexing a %s from issuer [%s]", commentDao.getType(), issuer.substring(0, 8))); } + return commentDao.create(json); } - 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() - - // price - .startObject("price") - .field("type", "double") - .endObject() - - // price Unit - .startObject("unit") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // currency - .startObject("currency") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // issuer - .startObject("issuer") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // type (offer, need, ...) - .startObject("type") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - - // location - .startObject("location") - .field("type", "string") - .field("analyzer", stringAnalyzer) - .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() + public void updateCommentFromJson(String id, String json) { + JsonNode commentObj = readAndVerifyIssuerSignature(json); - // 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() + // Check the record document exists + String recordId = getMandatoryField(commentObj, RecordComment.PROPERTY_RECORD).asText(); + checkRecordExistsOrDeleted(recordId); - // picturesCount - .startObject("picturesCount") - .field("type", "integer") - .endObject() + if (logger.isDebugEnabled()) { + String issuer = getMandatoryField(commentObj, RecordComment.PROPERTY_ISSUER).asText(); + logger.debug(String.format("[%s] Indexing a %s from issuer [%s] on [%s]", commentDao.getType(), commentDao.getType(), issuer.substring(0, 8))); + } - // category - .startObject("category") - .field("type", "nested") - .field("dynamic", "false") - .startObject("properties") - .startObject("id") // author - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - .startObject("parent") // author - .field("type", "string") - .field("index", "not_analyzed") - .endObject() - .startObject("name") // author - .field("type", "string") - .field("analyzer", stringAnalyzer) - .endObject() - .endObject() - .endObject() + commentDao.update(id, json); + } - // tags - .startObject("tags") - .field("type", "completion") - .field("search_analyzer", "simple") - .field("analyzer", "simple") - .field("preserve_separators", "false") - .endObject() - .endObject() - .endObject().endObject(); + /* -- Internal methods -- */ - return mapping; + // Check the record document exists (or has been deleted) + private void checkRecordExistsOrDeleted(String id) { + boolean recordExists; + try { + recordExists = recordDao.isExists(id); + } catch (NotFoundException e) { + // Check if exists in delete history + recordExists = historyService.existsInDeleteHistory(recordDao.getIndex(), recordDao.getType(), id); } - catch(IOException ioe) { - throw new TechnicalException(String.format("Error while getting mapping for index [%s/%s]: %s", INDEX, RECORD_TYPE, ioe.getMessage()), ioe); + if (!recordExists) { + throw new NotFoundException(String.format("Comment refers a non-existent document [%s/%s/%s].", recordDao.getIndex(), recordDao.getType(), id)); } } - } 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 index b090936bb9a7f7d192b686997d28c2a09679dbea..ca51cb87372a987f264b201909c9c76dfdb9dd93 100644 --- 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 @@ -23,334 +23,125 @@ package org.duniter.elasticsearch.gchange.service; */ -import com.fasterxml.jackson.core.JsonProcessingException; -import org.duniter.core.exception.TechnicalException; +import com.fasterxml.jackson.databind.JsonNode; +import org.duniter.core.client.model.elasticsearch.RecordComment; import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.client.Duniter4jClient; +import org.duniter.elasticsearch.exception.NotFoundException; import org.duniter.elasticsearch.gchange.PluginSettings; -import org.duniter.elasticsearch.gchange.service.AbstractService; -import org.duniter.elasticsearch.user.service.UserEventService; -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.duniter.elasticsearch.gchange.dao.registry.RegistryCommentDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryIndexDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryRecordDao; +import org.duniter.elasticsearch.user.service.HistoryService; 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 CommentService commentService; - private UserEventService userEventService; + private RegistryIndexDao indexDao; + private RegistryRecordDao recordDao; + private RegistryCommentDao commentDao; + private HistoryService historyService; @Inject - public RegistryService(Client client, + public RegistryService(Duniter4jClient client, PluginSettings settings, CryptoService cryptoService, - CommentService commentService, - UserEventService userEventService) { - super("gchange." + INDEX, client, settings, cryptoService); - this.commentService = commentService; - this.userEventService = userEventService; + HistoryService historyService, + RegistryIndexDao registryIndexDao, + RegistryCommentDao commentDao, + RegistryRecordDao recordDao) { + super("gchange.service.registry", client, settings, cryptoService); + this.indexDao = registryIndexDao; + this.commentDao = commentDao; + this.recordDao = recordDao; + this.historyService = historyService; } /** * 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, commentService.createRecordCommentType(INDEX, RECORD_COMMENT_TYPE)); - createIndexRequestBuilder.execute().actionGet(); - + indexDao.createIndexIfNotExists(); return this; } public RegistryService deleteIndex() { - deleteIndexIfExists(INDEX); + indexDao.deleteIndex(); return this; } - public boolean existsIndex() { - return super.existsIndex(INDEX); - } + public String indexRecordFromJson(String json) { + JsonNode actualObj = readAndVerifyIssuerSignature(json); + String issuer = getIssuer(actualObj); - public RegistryService fillRecordCategories() { if (logger.isDebugEnabled()) { - logger.debug(String.format("[%s/%s] Fill data", INDEX, RECORD_CATEGORY_TYPE)); + logger.debug(String.format("Indexing a %s from issuer [%s]", recordDao.getType(), issuer.substring(0, 8))); } - // 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); + return recordDao.create(json); } public void updateRecordFromJson(String id, String json) { - checkIssuerAndUpdateDocumentFromJson(INDEX, RECORD_TYPE, id, json); - } - - public String indexCommentFromJson(String json) { - return commentService.indexCommentFromJson(INDEX, RECORD_TYPE, RECORD_COMMENT_TYPE, json); - } - - public void updateCommentFromJson(String id, String json) { - commentService.updateCommentFromJson(INDEX, RECORD_TYPE, RECORD_COMMENT_TYPE, id, json); - } - - /* -- Internal methods -- */ - - public XContentBuilder createRecordType() { - String stringAnalyzer = pluginSettings.getDefaultStringAnalyzer(); - - try { - XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(RECORD_TYPE) - .startObject("properties") + JsonNode actualObj = readAndVerifyIssuerSignature(json); + String issuer = getIssuer(actualObj); - // title - .startObject("title") - .field("type", "string") - .field("analyzer", stringAnalyzer) - .endObject() + // Check same document issuer + recordDao.checkSameDocumentIssuer(id, issuer); - // 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() + if (logger.isDebugEnabled()) { + logger.debug(String.format("Updating %s [%s] from issuer [%s]", recordDao.getType(), id, issuer.substring(0, 8))); + } - // 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() + recordDao.update(id, json); + } - // tags - .startObject("tags") - .field("type", "completion") - .field("search_analyzer", "simple") - .field("analyzer", "simple") - .field("preserve_separators", "false") - .endObject() + public String indexCommentFromJson(String json) { + JsonNode commentObj = readAndVerifyIssuerSignature(json); + String issuer = getMandatoryField(commentObj, RecordComment.PROPERTY_ISSUER).asText(); - .endObject() - .endObject().endObject(); + // Check the record document exists + String recordId = getMandatoryField(commentObj, RecordComment.PROPERTY_RECORD).asText(); + checkRecordExistsOrDeleted(recordId); - 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); + if (logger.isDebugEnabled()) { + logger.debug(String.format("Indexing a %s from issuer [%s]", commentDao.getType(), issuer.substring(0, 8))); } + return commentDao.create(json); } - public XContentBuilder createRecordCategoryType() { - try { - XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(RECORD_CATEGORY_TYPE) - .startObject("properties") + public void updateCommentFromJson(String id, String json) { + JsonNode commentObj = readAndVerifyIssuerSignature(json); - // name - .startObject("name") - .field("type", "string") - .field("analyzer", pluginSettings.getDefaultStringAnalyzer()) - .endObject() + // Check the record document exists + String recordId = getMandatoryField(commentObj, RecordComment.PROPERTY_RECORD).asText(); + checkRecordExistsOrDeleted(recordId); - // description - /*.startObject("description") - .field("type", "string") - .endObject()*/ + if (logger.isDebugEnabled()) { + String issuer = getMandatoryField(commentObj, RecordComment.PROPERTY_ISSUER).asText(); + logger.debug(String.format("[%s] Indexing a %s from issuer [%s] on [%s]", commentDao.getType(), commentDao.getType(), issuer.substring(0, 8))); + } - // parent - .startObject("parent") - .field("type", "string") - .field("index", "not_analyzed") - .endObject() + commentDao.update(id, json); + } - // tags - /*.startObject("tags") - .field("type", "completion") - .field("search_analyzer", "simple") - .field("analyzer", "simple") - .field("preserve_separators", "false") - .endObject()*/ - .endObject() - .endObject().endObject(); + /* -- Internal methods -- */ - 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); + // Check the record document exists (or has been deleted) + private void checkRecordExistsOrDeleted(String id) { + boolean recordExists; + try { + recordExists = recordDao.isExists(id); + } catch (NotFoundException e) { + // Check if exists in delete history + recordExists = historyService.existsInDeleteHistory(recordDao.getIndex(), recordDao.getType(), id); } - } - - /** - * - * @param jsonCategory - * @return the product id - */ - public String indexCategoryFromJson(String jsonCategory) { - if (logger.isDebugEnabled()) { - logger.debug("Indexing a category"); + if (!recordExists) { + throw new NotFoundException(String.format("Comment refers a non-existent document [%s/%s/%s].", recordDao.getIndex(), recordDao.getType(), id)); } - - // 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 index ce56b692f84bd6bdd3e5fca64dcbffcff166ca5b..a3d5f3d32290d7d4d21cc65003dceb5772fb27c7 100644 --- 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 @@ -29,8 +29,6 @@ public class ServiceModule extends AbstractModule implements Module { @Override protected void configure() { bind(RegistryService.class).asEagerSingleton(); - bind(CitiesRegistryService.class).asEagerSingleton(); - bind(CommentService.class).asEagerSingleton(); bind(CommentUserEventService.class).asEagerSingleton(); bind(MarketService.class).asEagerSingleton(); bind(SynchroService.class).asEagerSingleton(); 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 index 6498406dd5ecf8027e8f7afdfe31b857f43ec67e..977f23303b90c82c3565935d680579c1392b13a8 100644 --- 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 @@ -24,6 +24,13 @@ package org.duniter.elasticsearch.gchange.service; import org.duniter.core.client.model.local.Peer; import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.client.Duniter4jClient; +import org.duniter.elasticsearch.gchange.dao.market.MarketCommentDao; +import org.duniter.elasticsearch.gchange.dao.market.MarketIndexDao; +import org.duniter.elasticsearch.gchange.dao.market.MarketRecordDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryCommentDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryIndexDao; +import org.duniter.elasticsearch.gchange.dao.registry.RegistryRecordDao; import org.duniter.elasticsearch.gchange.model.Protocol; import org.duniter.elasticsearch.model.SynchroResult; import org.duniter.elasticsearch.service.AbstractSynchroService; @@ -39,7 +46,7 @@ import org.elasticsearch.common.inject.Inject; public class SynchroService extends AbstractSynchroService { @Inject - public SynchroService(Client client, PluginSettings settings, CryptoService cryptoService, + public SynchroService(Duniter4jClient client, PluginSettings settings, CryptoService cryptoService, ThreadPool threadPool, final ServiceLocator serviceLocator) { super(client, settings.getDelegate(), cryptoService, threadPool, serviceLocator); } @@ -69,12 +76,12 @@ public class SynchroService extends AbstractSynchroService { } protected void importMarketChanges(SynchroResult result, Peer peer, long sinceTime) { - importChanges(result, peer, MarketService.INDEX, MarketService.RECORD_TYPE, sinceTime); - importChanges(result, peer, MarketService.INDEX, MarketService.RECORD_COMMENT_TYPE, sinceTime); + importChanges(result, peer, MarketIndexDao.INDEX, MarketRecordDao.TYPE, sinceTime); + importChanges(result, peer, MarketIndexDao.INDEX, MarketCommentDao.TYPE, sinceTime); } protected void importRegistryChanges(SynchroResult result, Peer peer, long sinceTime) { - importChanges(result, peer, RegistryService.INDEX, RegistryService.RECORD_TYPE, sinceTime); - importChanges(result, peer, RegistryService.INDEX, RegistryService.RECORD_COMMENT_TYPE, sinceTime); + importChanges(result, peer, RegistryIndexDao.INDEX, RegistryRecordDao.TYPE, sinceTime); + importChanges(result, peer, RegistryIndexDao.INDEX, RegistryCommentDao.TYPE, sinceTime); } } 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 deleted file mode 100644 index a305ac1b40ff0fca3ed27b843d7c737d1f117d50..0000000000000000000000000000000000000000 --- a/duniter4j-es-gchange/src/test/java/org/duniter/elasticsearch/service/BlockchainServiceTest.java +++ /dev/null @@ -1,172 +0,0 @@ -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-es-user/src/main/java/org/duniter/elasticsearch/user/PluginInit.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginInit.java index c827707dc7a89585f5f7722b9752688386a1899c..d06b1f759ca28f5ad87282e82ba066d9cc50e1c6 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginInit.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginInit.java @@ -44,7 +44,7 @@ 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("node"); + private final static ESLogger logger = Loggers.getLogger("duniter.user"); private final String clusterName; @Inject @@ -111,12 +111,12 @@ public class PluginInit extends AbstractLifecycleComponent<PluginInit> { .createIndexIfNotExists(); if (logger.isInfoEnabled()) { - logger.info("Reloading all Duniter indices... [OK]"); + logger.info("Reloading all Duniter User indices... [OK]"); } } else { if (logger.isInfoEnabled()) { - logger.info("Checking Duniter indices..."); + logger.info("Checking Duniter User indices..."); } injector.getInstance(HistoryService.class).createIndexIfNotExists(); injector.getInstance(UserService.class).createIndexIfNotExists(); @@ -125,7 +125,7 @@ public class PluginInit extends AbstractLifecycleComponent<PluginInit> { injector.getInstance(UserInvitationService.class).createIndexIfNotExists(); if (logger.isInfoEnabled()) { - logger.info("Checking Duniter indices... [OK]"); + logger.info("Checking Duniter User indices... [OK]"); } } } diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/AbstractService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/AbstractService.java index 58555b9de9f77c11ee135d7dc719923aff64b804..4ae645da796d716430e6c64057bdb6ac3b2a29f0 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/AbstractService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/AbstractService.java @@ -23,8 +23,8 @@ package org.duniter.elasticsearch.user.service; */ import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.user.PluginSettings; -import org.elasticsearch.client.Client; /** * Created by blavenie on 10/01/17. @@ -33,19 +33,19 @@ public abstract class AbstractService extends org.duniter.elasticsearch.service. protected PluginSettings pluginSettings; - public AbstractService(String loggerName, Client client, PluginSettings pluginSettings) { + public AbstractService(String loggerName, Duniter4jClient client, PluginSettings pluginSettings) { this(loggerName, client, pluginSettings, null); } - public AbstractService(Client client, PluginSettings pluginSettings) { + public AbstractService(Duniter4jClient client, PluginSettings pluginSettings) { this(client, pluginSettings, null); } - public AbstractService(Client client, PluginSettings pluginSettings, CryptoService cryptoService) { + public AbstractService(Duniter4jClient client, PluginSettings pluginSettings, CryptoService cryptoService) { this("duniter.user", client, pluginSettings, cryptoService); } - public AbstractService(String loggerName, Client client, PluginSettings pluginSettings, CryptoService cryptoService) { + public AbstractService(String loggerName, Duniter4jClient client, PluginSettings pluginSettings, CryptoService cryptoService) { super(loggerName, client, pluginSettings.getDelegate(), cryptoService); this.pluginSettings = pluginSettings; } diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java index 8420fe9dbc5900e44a5419a56809ccd662896acf..2c1a3ccf526deb508f5062490739143a1be595b5 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java @@ -33,6 +33,7 @@ import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; import org.duniter.core.util.CollectionUtils; import org.duniter.core.util.websocket.WebsocketClientEndpoint; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.service.BlockchainService; import org.duniter.elasticsearch.service.changes.ChangeEvent; import org.duniter.elasticsearch.service.changes.ChangeService; @@ -40,7 +41,6 @@ import org.duniter.elasticsearch.service.changes.ChangeSource; import org.duniter.elasticsearch.user.PluginSettings; import org.duniter.elasticsearch.user.model.UserEvent; import org.duniter.elasticsearch.user.model.UserEventCodes; -import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; import org.nuiton.i18n.I18n; @@ -69,7 +69,7 @@ public class BlockchainUserEventService extends AbstractService implements Chang public final boolean enable; @Inject - public BlockchainUserEventService(Client client, PluginSettings settings, CryptoService cryptoService, + public BlockchainUserEventService(Duniter4jClient client, PluginSettings settings, CryptoService cryptoService, BlockchainService blockchainService, UserService userService, UserEventService userEventService) { diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/GroupService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/GroupService.java index 8a06c09d9893234844ed4cff6456e32c1f851330..51e6a05802f55746fd1d6de6e3253b21fa2344fd 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/GroupService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/GroupService.java @@ -29,6 +29,7 @@ import org.apache.commons.collections4.MapUtils; import org.duniter.core.client.model.elasticsearch.UserGroup; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.exception.AccessDeniedException; import org.duniter.elasticsearch.user.service.AbstractService; import org.duniter.elasticsearch.user.PluginSettings; @@ -56,7 +57,7 @@ public class GroupService extends AbstractService { public static final String RECORD_TYPE = "record"; @Inject - public GroupService(Client client, + public GroupService(Duniter4jClient client, PluginSettings settings, CryptoService cryptoService) { super("duniter." + INDEX, client, settings, cryptoService); @@ -67,7 +68,7 @@ public class GroupService extends AbstractService { */ public GroupService createIndexIfNotExists() { try { - if (!existsIndex(INDEX)) { + if (!client.existsIndex(INDEX)) { createIndex(); } } @@ -98,12 +99,12 @@ public class GroupService extends AbstractService { } public GroupService deleteIndex() { - deleteIndexIfExists(INDEX); + client.deleteIndexIfExists(INDEX); return this; } public boolean existsIndex() { - return super.existsIndex(INDEX); + return client.existsIndex(INDEX); } /** @@ -150,17 +151,17 @@ public class GroupService extends AbstractService { public String getTitleById(String id) { - Object title = getFieldById(INDEX, RECORD_TYPE, id, UserGroup.PROPERTY_TITLE); + Object title = client.getFieldById(INDEX, RECORD_TYPE, id, UserGroup.PROPERTY_TITLE); if (title == null) return null; return title.toString(); } public Map<String, String> getTitlesByNames(Set<String> ids) { - Map<String, Object> titles = getFieldByIds(INDEX, RECORD_TYPE, ids, UserGroup.PROPERTY_TITLE); + Map<String, Object> titles = client.getFieldByIds(INDEX, RECORD_TYPE, ids, UserGroup.PROPERTY_TITLE); if (MapUtils.isEmpty(titles)) return null; Map<String, String> result = new HashMap<>(); - titles.entrySet().stream().forEach((entry) -> result.put(entry.getKey(), entry.getValue().toString())); + titles.entrySet().forEach((entry) -> result.put(entry.getKey(), entry.getValue().toString())); return result; } @@ -183,7 +184,7 @@ public class GroupService extends AbstractService { id += "_" + counter; } - if (!isDocumentExists(INDEX, RECORD_TYPE, id)) { + if (!client.isDocumentExists(INDEX, RECORD_TYPE, id)) { return id; } diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/HistoryService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/HistoryService.java index 8cfb537bb19a119287c8be8f30649e4248205a5f..7df495965f4ff5270db75af9aa67eca70672cfa9 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/HistoryService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/HistoryService.java @@ -29,6 +29,7 @@ import org.duniter.core.client.model.elasticsearch.DeleteRecord; import org.duniter.core.client.model.elasticsearch.MessageRecord; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.exception.NotFoundException; import org.duniter.elasticsearch.user.service.AbstractService; import org.duniter.elasticsearch.user.PluginSettings; @@ -57,7 +58,7 @@ public class HistoryService extends AbstractService { public static final String DELETE_TYPE = "delete"; @Inject - public HistoryService(Client client, PluginSettings settings, CryptoService cryptoService) { + public HistoryService(Duniter4jClient client, PluginSettings settings, CryptoService cryptoService) { super("gchange." + INDEX, client, settings, cryptoService); } @@ -66,13 +67,13 @@ public class HistoryService extends AbstractService { * @throws JsonProcessingException */ public HistoryService deleteIndex() { - deleteIndexIfExists(INDEX); + client.deleteIndexIfExists(INDEX); return this; } public boolean existsIndex() { - return super.existsIndex(INDEX); + return client.existsIndex(INDEX); } /** @@ -80,7 +81,7 @@ public class HistoryService extends AbstractService { */ public HistoryService createIndexIfNotExists() { try { - if (!existsIndex(INDEX)) { + if (!client.existsIndex(INDEX)) { createIndex(); } } @@ -120,21 +121,21 @@ public class HistoryService extends AbstractService { String type = actualObj.get(DeleteRecord.PROPERTY_TYPE).asText(); String id = actualObj.get(DeleteRecord.PROPERTY_ID).asText(); - if (!existsIndex(index)) { + if (!client.existsIndex(index)) { throw new NotFoundException(String.format("Index [%s] not exists.", index)); } // Special case for message: check if deletion issuer is the message recipient if (MessageService.INDEX.equals(index) && MessageService.INBOX_TYPE.equals(type)) { - checkSameDocumentField(index, type, id, MessageRecord.PROPERTY_RECIPIENT, issuer); + client.checkSameDocumentField(index, type, id, MessageRecord.PROPERTY_RECIPIENT, issuer); } // Special case for invitation: check if deletion issuer is the invitation recipient else if (UserInvitationService.INDEX.equals(index)) { - checkSameDocumentField(index, type, id, MessageRecord.PROPERTY_RECIPIENT, issuer); + client.checkSameDocumentField(index, type, id, MessageRecord.PROPERTY_RECIPIENT, issuer); } else { // Check document issuer - checkSameDocumentIssuer(index, type, id, issuer); + client.checkSameDocumentIssuer(index, type, id, issuer); } if (logger.isDebugEnabled()) { @@ -177,7 +178,7 @@ public class HistoryService extends AbstractService { /* -- Internal methods -- */ - public XContentBuilder createDeleteType() { + protected XContentBuilder createDeleteType() { try { XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(DELETE_TYPE) .startObject("properties") diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java index 40bb0e1526ce8218c7d4b3b2295c77fcf2db4f5a..5a4a2a3835e61a0fd9fd06915c79023833da29dc 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java @@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.duniter.core.client.model.ModelUtils; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.user.PluginSettings; import org.duniter.elasticsearch.exception.InvalidSignatureException; import org.duniter.elasticsearch.user.service.AbstractService; @@ -63,7 +64,7 @@ public class MessageService extends AbstractService { private final UserEventService userEventService; @Inject - public MessageService(Client client, PluginSettings settings, + public MessageService(Duniter4jClient client, PluginSettings settings, CryptoService cryptoService, UserEventService userEventService) { super("duniter." + INDEX, client, settings, cryptoService); this.userEventService = userEventService; @@ -74,12 +75,12 @@ public class MessageService extends AbstractService { * @throws JsonProcessingException */ public MessageService deleteIndex() { - deleteIndexIfExists(INDEX); + client.deleteIndexIfExists(INDEX); return this; } public boolean existsIndex() { - return super.existsIndex(INDEX); + return client.existsIndex(INDEX); } /** @@ -87,7 +88,7 @@ public class MessageService extends AbstractService { */ public MessageService createIndexIfNotExists() { try { - if (!existsIndex(INDEX)) { + if (!client.existsIndex(INDEX)) { createIndex(); } } @@ -165,7 +166,7 @@ public class MessageService extends AbstractService { } public void markMessageAsRead(String id, String signature) { - Map<String, Object> fields = getMandatoryFieldsById(INDEX, INBOX_TYPE, id, Message.PROPERTY_HASH, Message.PROPERTY_RECIPIENT); + Map<String, Object> fields = client.getMandatoryFieldsById(INDEX, INBOX_TYPE, id, Message.PROPERTY_HASH, Message.PROPERTY_RECIPIENT); String recipient = fields.get(UserEvent.PROPERTY_RECIPIENT).toString(); String hash = fields.get(UserEvent.PROPERTY_HASH).toString(); 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 index 6d683a46d56d02e670cfc017ae0c54ecd673874b..1c32e078c86b173e534eaa036b8fd160e9bab341 100644 --- 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 @@ -25,6 +25,7 @@ package org.duniter.elasticsearch.user.service; import org.duniter.core.client.model.elasticsearch.Protocol; import org.duniter.core.client.model.local.Peer; import org.duniter.core.service.CryptoService; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.user.PluginSettings; import org.duniter.elasticsearch.model.SynchroResult; import org.duniter.elasticsearch.service.ServiceLocator; @@ -39,7 +40,7 @@ import org.elasticsearch.common.inject.Inject; public class SynchroService extends AbstractSynchroService { @Inject - public SynchroService(Client client, PluginSettings settings, CryptoService cryptoService, + public SynchroService(Duniter4jClient client, PluginSettings settings, CryptoService cryptoService, ThreadPool threadPool, final ServiceLocator serviceLocator) { super(client, settings.getDelegate(), cryptoService, threadPool, serviceLocator); } diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserEventService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserEventService.java index 8b48a96ae92ef2b1d84cd253e37e7234c20fd33a..eee64c1b4c3be56fde488512f82416e18710f831 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserEventService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserEventService.java @@ -33,6 +33,7 @@ import org.duniter.core.util.Preconditions; import org.duniter.core.util.StringUtils; import org.duniter.core.util.crypto.CryptoUtils; import org.duniter.core.util.crypto.KeyPair; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.exception.InvalidSignatureException; import org.duniter.elasticsearch.service.changes.ChangeEvent; import org.duniter.elasticsearch.service.changes.ChangeService; @@ -101,7 +102,7 @@ public class UserEventService extends AbstractService implements ChangeService.C public final boolean trace; @Inject - public UserEventService(final Client client, + public UserEventService(final Duniter4jClient client, final PluginSettings pluginSettings, final CryptoService cryptoService, final MailService mailService, @@ -238,7 +239,7 @@ public class UserEventService extends AbstractService implements ChangeService.C public ListenableActionFuture<UpdateResponse> markEventAsRead(String id, String signature) { - Map<String, Object> fields = getMandatoryFieldsById(INDEX, EVENT_TYPE, id, UserEvent.PROPERTY_HASH, UserEvent.PROPERTY_RECIPIENT); + Map<String, Object> fields = client.getMandatoryFieldsById(INDEX, EVENT_TYPE, id, UserEvent.PROPERTY_HASH, UserEvent.PROPERTY_RECIPIENT); String recipient = fields.get(UserEvent.PROPERTY_RECIPIENT).toString(); String hash = fields.get(UserEvent.PROPERTY_HASH).toString(); @@ -446,13 +447,13 @@ public class UserEventService extends AbstractService implements ChangeService.C // Flush the bulk if not empty if ((counter % bulkSize) == 0) { - flushDeleteBulk(INDEX, EVENT_TYPE, bulkRequest); + client.flushDeleteBulk(INDEX, EVENT_TYPE, bulkRequest); bulkRequest = client.prepareBulk(); } } // last flush - flushDeleteBulk(INDEX, EVENT_TYPE, bulkRequest); + client.flushDeleteBulk(INDEX, EVENT_TYPE, bulkRequest); } catch(SearchPhaseExecutionException e) { // Failed or no item on index @@ -461,13 +462,13 @@ public class UserEventService extends AbstractService implements ChangeService.C } private UserProfile getUserProfile(String pubkey, String... fieldnames) { - UserProfile result = getSourceByIdOrNull(UserService.INDEX, UserService.PROFILE_TYPE, pubkey, UserProfile.class, fieldnames); + UserProfile result = client.getSourceByIdOrNull(UserService.INDEX, UserService.PROFILE_TYPE, pubkey, UserProfile.class, fieldnames); if (result == null) result = new UserProfile(); return result; } private UserProfile getUserProfileOrNull(String pubkey, String... fieldnames) { - return getSourceByIdOrNull(UserService.INDEX, UserService.PROFILE_TYPE, pubkey, UserProfile.class, fieldnames); + return client.getSourceByIdOrNull(UserService.INDEX, UserService.PROFILE_TYPE, pubkey, UserProfile.class, fieldnames); } private String toJson(UserEvent userEvent) { @@ -531,7 +532,7 @@ public class UserEventService extends AbstractService implements ChangeService.C // Notify listeners threadPool.schedule(() -> { synchronized (LISTENERS) { - LISTENERS.values().stream().forEach(listener -> { + LISTENERS.values().forEach(listener -> { if (event.getRecipient().equals(listener.getPubkey())) { listener.onEvent(event); } diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserInvitationService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserInvitationService.java index ebb8cde1b58bfeedf90d22b0a96154073d613a2a..441b7e3106d96bef0ca17b45e13b3088a255d134 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserInvitationService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserInvitationService.java @@ -28,15 +28,13 @@ import com.fasterxml.jackson.databind.JsonNode; import org.duniter.core.client.model.ModelUtils; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; -import org.duniter.elasticsearch.exception.InvalidSignatureException; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.user.PluginSettings; import org.duniter.elasticsearch.user.model.Message; import org.duniter.elasticsearch.user.model.UserEvent; import org.duniter.elasticsearch.user.model.UserEventCodes; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.update.UpdateRequestBuilder; -import org.elasticsearch.client.Client; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -44,7 +42,6 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.nuiton.i18n.I18n; import java.io.IOException; -import java.util.Map; /** * Created by Benoit on 30/03/2015. @@ -54,12 +51,12 @@ public class UserInvitationService extends AbstractService { public static final String INDEX = "invitation"; public static final String CERTIFICATION_TYPE = "certification"; - private final UserEventService userEventService; @Inject - public UserInvitationService(Client client, PluginSettings settings, - CryptoService cryptoService, UserEventService userEventService) { + public UserInvitationService(Duniter4jClient client, PluginSettings settings, + CryptoService cryptoService, + UserEventService userEventService) { super("duniter." + INDEX, client, settings, cryptoService); this.userEventService = userEventService; } @@ -69,20 +66,16 @@ public class UserInvitationService extends AbstractService { * @throws JsonProcessingException */ public UserInvitationService deleteIndex() { - deleteIndexIfExists(INDEX); + client.deleteIndexIfExists(INDEX); return this; } - public boolean existsIndex() { - return super.existsIndex(INDEX); - } - /** * Create index need for blockchain registry, if need */ public UserInvitationService createIndexIfNotExists() { try { - if (!existsIndex(INDEX)) { + if (!client.existsIndex(INDEX)) { createIndex(); } } diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java index 75a4c039b19ba403cfe2385b9365a85a6772350e..11f62c689b835b425cdbd8eb72d62152e24d64f9 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java @@ -31,7 +31,7 @@ import org.duniter.core.client.model.ModelUtils; import org.duniter.core.client.model.elasticsearch.UserProfile; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; -import org.duniter.core.service.MailService; +import org.duniter.elasticsearch.client.Duniter4jClient; import org.duniter.elasticsearch.user.PluginSettings; import org.duniter.elasticsearch.exception.AccessDeniedException; import org.duniter.elasticsearch.service.AbstractService; @@ -60,7 +60,7 @@ public class UserService extends AbstractService { public static final String SETTINGS_TYPE = "settings"; @Inject - public UserService(Client client, + public UserService(Duniter4jClient client, PluginSettings settings, CryptoService cryptoService) { super("duniter." + INDEX, client, settings.getDelegate(), cryptoService); @@ -71,7 +71,7 @@ public class UserService extends AbstractService { */ public UserService createIndexIfNotExists() { try { - if (!existsIndex(INDEX)) { + if (!client.existsIndex(INDEX)) { createIndex(); } } @@ -104,14 +104,10 @@ public class UserService extends AbstractService { } public UserService deleteIndex() { - deleteIndexIfExists(INDEX); + client.deleteIndexIfExists(INDEX); return this; } - public boolean existsIndex() { - return super.existsIndex(INDEX); - } - /** * * Index an user profile @@ -203,17 +199,17 @@ public class UserService extends AbstractService { public String getProfileTitle(String issuer) { - Object title = getFieldById(INDEX, PROFILE_TYPE, issuer, UserProfile.PROPERTY_TITLE); + Object title = client.getFieldById(INDEX, PROFILE_TYPE, issuer, UserProfile.PROPERTY_TITLE); if (title == null) return null; return title.toString(); } public Map<String, String> getProfileTitles(Set<String> issuers) { - Map<String, Object> titles = getFieldByIds(INDEX, PROFILE_TYPE, issuers, UserProfile.PROPERTY_TITLE); + Map<String, Object> titles = client.getFieldByIds(INDEX, PROFILE_TYPE, issuers, UserProfile.PROPERTY_TITLE); if (MapUtils.isEmpty(titles)) return null; Map<String, String> result = new HashMap<>(); - titles.entrySet().stream().forEach((entry) -> result.put(entry.getKey(), entry.getValue().toString())); + titles.entrySet().forEach((entry) -> result.put(entry.getKey(), entry.getValue().toString())); return result; } @@ -230,7 +226,7 @@ public class UserService extends AbstractService { Map<String, String> profileTitles = getProfileTitles(pubkeys); StringBuilder sb = new StringBuilder(); - pubkeys.stream().forEach((pubkey)-> { + pubkeys.forEach((pubkey)-> { String title = profileTitles != null ? profileTitles.get(pubkey) : null; sb.append(separator); sb.append(title != null ? title : @@ -242,7 +238,6 @@ public class UserService extends AbstractService { /* -- Internal methods -- */ - public XContentBuilder createProfileType() { String stringAnalyzer = pluginSettings.getDefaultStringAnalyzer(); diff --git a/pom.xml b/pom.xml index 446da6f8318b6eabf8300826b65aa1e3680a5309..88eddb0f088c6db29c768a364c55b97ab592402f 100644 --- a/pom.xml +++ b/pom.xml @@ -285,11 +285,6 @@ <version>${tyrus.version}</version> </dependency> - <dependency> - <groupId>com.github.spullara.mustache.java</groupId> - <artifactId>mustache.java</artifactId> - <version>0.9.4</version> - </dependency> </dependencies> </dependencyManagement>