/* * JBoss, Home of Professional Open Source * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors * as indicated by the @authors tag. All rights reserved. */ package org.searchisko.persistence.service; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Logger; import javax.persistence.EntityManager; import javax.persistence.EntityNotFoundException; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.StreamingOutput; import org.elasticsearch.common.Strings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.searchisko.api.rest.ESDataOnlyResponse; import org.searchisko.persistence.jpa.model.ModelToJSONMapConverter; /** * JPA implementation of entity service. It's not session bean because type is unknown, so must be called from Session * bean to work with transactions! * * @author Libor Krzyzanek * @author Vlastimil Elias (velias at redhat dot com) * */ public class JpaEntityService<T> implements EntityService { protected Logger log; private EntityManager em; protected ModelToJSONMapConverter<T> converter; protected Class<T> entityType; public JpaEntityService(EntityManager em, ModelToJSONMapConverter<T> converter, Class<T> entityType) { log = Logger.getLogger(this.getClass().getName()); this.em = em; this.converter = converter; this.entityType = entityType; } @Override public StreamingOutput getAll(Integer from, Integer size, final String[] fieldsToRemove) { CriteriaBuilder cb = em.getCriteriaBuilder(); final List<T> result = listEntities(cb, from, size); CriteriaQuery<Long> queryCount = cb.createQuery(Long.class); queryCount.select(cb.count(queryCount.from(entityType))); final long count = em.createQuery(queryCount).getSingleResult(); return new StreamingOutput() { @Override public void write(OutputStream output) throws IOException, WebApplicationException { XContentBuilder builder = XContentFactory.jsonBuilder(output); builder.startObject(); if (result != null) { builder.field("total", count); builder.startArray("hits"); for (T t : result) { Map<String, Object> jsonData = converter.convertToJsonMap(t); builder.startObject(); builder.field("id", converter.getId(t)); builder.field("data", ESDataOnlyResponse.removeFields(jsonData, fieldsToRemove)); builder.endObject(); } } else { builder.field("total", 0); builder.startArray("hits"); } builder.endArray(); builder.endObject(); builder.close(); } }; } protected List<T> listEntities(CriteriaBuilder cb, Integer from, Integer size) { CriteriaQuery<T> queryList = cb.createQuery(entityType); Root<T> root = queryList.from(entityType); queryList.select(root); queryList.orderBy(cb.asc(root.get(converter.getEntityIdFieldName()))); TypedQuery<T> q = em.createQuery(queryList); if (from != null && from >= 0) q.setFirstResult(from); if (size != null && size > 0) q.setMaxResults(size); return q.getResultList(); } @Override public List<Map<String, Object>> getAll() { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<T> criteria = cb.createQuery(entityType); Root<T> root = criteria.from(entityType); criteria.select(root); final List<T> result = em.createQuery(criteria).getResultList(); List<Map<String, Object>> ret = new ArrayList<>(); try { for (T row : result) { ret.add(converter.convertToJsonMap(row)); } return ret; } catch (Exception e) { throw new RuntimeException(e); } } @Override public Map<String, Object> get(String id) { T jpaEntity = em.find(entityType, id); if (jpaEntity == null) { return null; } try { return converter.convertToJsonMap(jpaEntity); } catch (Exception e) { throw new RuntimeException(e); } } @Override public String create(Map<String, Object> entity) { String id = Strings.randomBase64UUID(); create(id, entity); return id; } @Override public void create(String id, Map<String, Object> entity) { try { T jpaEntity = em.find(entityType, id); if (jpaEntity != null) { // Entity exists. Only update the value converter.updateValue(jpaEntity, entity); } else { jpaEntity = converter.convertToModel(id, entity); em.persist(jpaEntity); } } catch (Exception e) { throw new RuntimeException(e); } } @Override public void update(String id, Map<String, Object> entity) { create(id, entity); } @Override public void delete(String id) { try { T reference = em.getReference(entityType, id); em.remove(reference); } catch (EntityNotFoundException e) { // OK } } protected int LIST_PAGE_SIZE = 200; protected static class ListRequestImpl implements ListRequest { List<ContentTuple<String, Map<String, Object>>> content; int beginIndex = 0; protected ListRequestImpl(int beginIndex, List<ContentTuple<String, Map<String, Object>>> content) { super(); this.beginIndex = beginIndex; this.content = content; } @Override public boolean hasContent() { return content != null && !content.isEmpty(); } @Override public List<ContentTuple<String, Map<String, Object>>> content() { return content; } } @Override public ListRequest listRequestInit() { return listRequestImpl(0); } @Override public ListRequest listRequestNext(ListRequest previous) { ListRequestImpl lr = (ListRequestImpl) previous; return listRequestImpl(lr.beginIndex + LIST_PAGE_SIZE); } protected ListRequest listRequestImpl(int beginIndex) { CriteriaBuilder cb = em.getCriteriaBuilder(); final List<T> result = listEntities(cb, beginIndex, LIST_PAGE_SIZE); List<ContentTuple<String, Map<String, Object>>> content = new ArrayList<>(10); for (T data : result) { try { content.add(converter.convertToContentTuple(data)); } catch (IOException e) { log.severe("Could not convert Entity.id=" + converter.getId(data) + " JSON content to valid object so skip it: " + e.getMessage()); } } return new ListRequestImpl(beginIndex, content); } }