package alien4cloud.dao; import java.io.IOException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.get.MultiGetItemResponse; import org.elasticsearch.action.get.MultiGetResponse; import org.elasticsearch.common.collect.Lists; import org.elasticsearch.mapping.MappingBuilder; import alien4cloud.exception.IndexingServiceException; import alien4cloud.model.common.IDatableResource; import lombok.SneakyThrows; /** * ElasticSearch DAO to manage id based operations. * * @author luc boutier */ public abstract class ESGenericIdDAO extends ESIndexMapper implements IGenericIdDAO { @Override public <T> boolean exist(Class<T> clazz, String id) { return getClient().prepareGet(getIndexForType(clazz), MappingBuilder.indexTypeFromClass(clazz), id).setFields(new String[0]).execute().actionGet() .isExists(); } /** * The save method should be in charge to set the creationDate and the lastUpdateDate. * * @param data * @param <T> */ private <T> void updateDate(T data) { if (data instanceof IDatableResource) { IDatableResource resource = (IDatableResource) data; resource.setLastUpdateDate(new Date()); if (resource.getCreationDate() == null) { resource.setCreationDate(resource.getLastUpdateDate()); } } } @Override @SneakyThrows({ IOException.class }) public <T> void save(T data) { String indexName = getIndexForType(data.getClass()); String typeName = MappingBuilder.indexTypeFromClass(data.getClass()); updateDate(data); String json = getJsonMapper().writeValueAsString(data); getClient().prepareIndex(indexName, typeName).setOperationThreaded(false).setSource(json).setRefresh(true).execute().actionGet(); } @Override @SneakyThrows({ IOException.class }) public <T> void save(T[] entities) { if (entities == null || entities.length == 0) { return; } BulkRequestBuilder bulkRequestBuilder = getClient().prepareBulk().setRefresh(true); for (T data : entities) { String indexName = getIndexForType(data.getClass()); String typeName = MappingBuilder.indexTypeFromClass(data.getClass()); updateDate(data); String json = getJsonMapper().writeValueAsString(data); bulkRequestBuilder.add(getClient().prepareIndex(indexName, typeName).setSource(json)); } bulkRequestBuilder.execute().actionGet(); } @SuppressWarnings("unchecked") @Override @SneakyThrows({ IOException.class }) public <T> T findById(Class<T> clazz, String id) { boolean abstractType = Modifier.isAbstract(clazz.getModifiers()); assertIdNotNullFor(id, "findById"); String indexName = getIndexForType(clazz); String typeName = abstractType ? null : MappingBuilder.indexTypeFromClass(clazz); GetResponse response = getClient().prepareGet(indexName, typeName, id).execute().actionGet(); if (response == null || !response.isExists()) { ESIndexMapper.getLog().debug("Nothing found in index <{}>, type <{}>, for Id <{}>.", indexName, typeName, id); return null; } ESIndexMapper.getLog().debug("Found one in index <{}>, type <{}>, for Id <{}>.", indexName, typeName, id); if (abstractType) { return (T) getJsonMapper().readValue(response.getSourceAsString(), getTypesToClasses().get(response.getType())); } return getJsonMapper().readValue(response.getSourceAsString(), clazz); } @Override @SneakyThrows({ IOException.class }) public <T> List<T> findByIds(Class<T> clazz, String... ids) { String indexName = getIndexForType(clazz); String typeName = MappingBuilder.indexTypeFromClass(clazz); MultiGetResponse response = getClient().prepareMultiGet().add(indexName, typeName, ids).execute().actionGet(); if (response == null || response.getResponses() == null || response.getResponses().length == 0) { ESIndexMapper.getLog().debug("Nothing found in index <{}>, type <{}>, for Ids <{}>.", indexName, typeName, Arrays.toString(ids)); return null; } List<T> result = new ArrayList<>(); for (MultiGetItemResponse getItemResponse : response.getResponses()) { if (getItemResponse.getResponse().isExists()) { result.add(getJsonMapper().readValue(getItemResponse.getResponse().getSourceAsString(), clazz)); } } return result; } @Override public void delete(Class<?> clazz, String id) { assertIdNotNullFor(id, "delete"); String indexName = getIndexForType(clazz); String typeName = MappingBuilder.indexTypeFromClass(clazz); getClient().prepareDelete(indexName, typeName, id).setRefresh(true).execute().actionGet(); } private void assertIdNotNullFor(String id, String operation) { if (id == null || id.trim().isEmpty()) { ESIndexMapper.getLog().error("Null or empty Id is not allowed for operation <" + operation + ">."); throw new IndexingServiceException("Null or empty Id is not allowed for operation <" + operation + ">."); } } protected Class<?>[] getRequestedTypes(Class<?> clazz) { // FIXME clazz might be null if (Modifier.isAbstract(clazz.getModifiers())) { List<Class<?>> classes = Lists.newArrayList(); for (Class<?> registeredClass : this.getTypesToClasses().values()) { if (clazz.isAssignableFrom(registeredClass)) { classes.add(registeredClass); } } return classes.toArray(new Class<?>[classes.size()]); } return new Class<?>[] { clazz }; } protected String[] getTypesStrings(Class<?>... classes) { if (classes == null) { return null; } List<String> types = new ArrayList<String>(classes.length); for (Class<?> clazz : classes) { if (clazz != null) { types.add(MappingBuilder.indexTypeFromClass(clazz)); } } if (types.isEmpty()) { return null; } return types.toArray(new String[types.size()]); } }