/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.api.machine.server.jpa; import com.google.inject.persist.Transactional; import org.eclipse.che.api.core.ApiException; import org.eclipse.che.api.core.ConflictException; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.api.machine.server.event.BeforeRecipeRemovedEvent; import org.eclipse.che.api.machine.server.event.RecipePersistedEvent; import org.eclipse.che.api.machine.server.recipe.RecipeImpl; import org.eclipse.che.api.machine.server.spi.RecipeDao; import org.eclipse.che.core.db.jpa.DuplicateKeyException; import org.eclipse.che.core.db.jpa.IntegrityConstraintViolationException; import javax.inject.Inject; import javax.inject.Provider; import javax.inject.Singleton; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Join; import javax.persistence.criteria.ParameterExpression; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.List; import static java.lang.String.format; import static java.util.Objects.requireNonNull; /** * Implementation of {@link RecipeDao}. * * @author Anton Korneta */ @Singleton public class JpaRecipeDao implements RecipeDao { @Inject private Provider<EntityManager> managerProvider; @Inject private EventService eventService; @Override public void create(RecipeImpl recipe) throws ConflictException, ServerException { requireNonNull(recipe); try { doCreate(recipe); } catch (DuplicateKeyException ex) { throw new ConflictException(format("Recipe with id %s already exists", recipe.getId())); } catch (IntegrityConstraintViolationException ex) { throw new ConflictException("Could not create recipe with permissions for non-existent user"); } catch (RuntimeException ex) { throw new ServerException(ex.getLocalizedMessage(), ex); } } @Override public RecipeImpl update(RecipeImpl update) throws NotFoundException, ServerException { requireNonNull(update); try { return doUpdate(update); } catch (RuntimeException ex) { throw new ServerException(ex.getLocalizedMessage(), ex); } } @Override public void remove(String id) throws ServerException { requireNonNull(id); try { doRemove(id); } catch (RuntimeException x) { throw new ServerException(x.getLocalizedMessage(), x); } } @Override @Transactional public RecipeImpl getById(String id) throws NotFoundException, ServerException { requireNonNull(id); try { final EntityManager manager = managerProvider.get(); final RecipeImpl recipe = manager.find(RecipeImpl.class, id); if (recipe == null) { throw new NotFoundException(format("Recipe with id '%s' doesn't exist", id)); } return recipe; } catch (RuntimeException ex) { throw new ServerException(ex.getLocalizedMessage(), ex); } } @Override @Transactional public List<RecipeImpl> search(String user, List<String> tags, String type, int skipCount, int maxItems) throws ServerException { try { final EntityManager manager = managerProvider.get(); final CriteriaBuilder cb = manager.getCriteriaBuilder(); final CriteriaQuery<RecipeImpl> query = cb.createQuery(RecipeImpl.class); final Root<RecipeImpl> fromRecipe = query.from(RecipeImpl.class); final ParameterExpression<String> typeParam = cb.parameter(String.class, "recipeType"); final Predicate checkType = cb.or(cb.isNull(typeParam), cb.equal(fromRecipe.get("type"), typeParam)); final TypedQuery<RecipeImpl> typedQuery; if (tags != null && !tags.isEmpty()) { final Join<RecipeImpl, String> tag = fromRecipe.join("tags"); query.select(cb.construct(RecipeImpl.class, tag.getParent())) .where(cb.and(checkType, tag.in(tags))) .groupBy(fromRecipe.get("id")) .having(cb.equal(cb.count(tag), tags.size())); typedQuery = manager.createQuery(query) .setParameter("tags", tags); } else { typedQuery = manager.createQuery(query.where(checkType)); } return typedQuery.setParameter("recipeType", type) .setFirstResult(skipCount) .setMaxResults(maxItems) .getResultList(); } catch (RuntimeException ex) { throw new ServerException(ex.getLocalizedMessage(), ex); } } @Transactional(rollbackOn = {RuntimeException.class, ServerException.class}) protected void doRemove(String id) throws ServerException { final EntityManager manager = managerProvider.get(); final RecipeImpl recipe = manager.find(RecipeImpl.class, id); if (recipe != null) { eventService.publish(new BeforeRecipeRemovedEvent(new RecipeImpl(recipe))).propagateException(); manager.remove(recipe); manager.flush(); } } @Transactional protected RecipeImpl doUpdate(RecipeImpl update) throws NotFoundException { final EntityManager manager = managerProvider.get(); if (manager.find(RecipeImpl.class, update.getId()) == null) { throw new NotFoundException(format("Could not update recipe with id %s because it doesn't exist", update.getId())); } RecipeImpl merged = manager.merge(update); manager.flush(); return merged; } @Transactional(rollbackOn = {RuntimeException.class, ApiException.class}) protected void doCreate(RecipeImpl recipe) throws ConflictException, ServerException { EntityManager manage = managerProvider.get(); manage.persist(recipe); manage.flush(); eventService.publish(new RecipePersistedEvent(recipe)).propagateException(); } }