/* * Copyright 2010-2011 Ning, Inc. * Copyright 2014-2016 Groupon, Inc * Copyright 2014-2016 The Billing Project, LLC * * The Billing Project licenses this file to you under the Apache License, version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package org.killbill.billing.util.tag.dao; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.UUID; import org.killbill.billing.BillingExceptionBase; import org.killbill.billing.ErrorCode; import org.killbill.billing.callcontext.InternalCallContext; import org.killbill.billing.callcontext.InternalTenantContext; import org.killbill.billing.events.TagDefinitionInternalEvent; import org.killbill.billing.util.api.TagDefinitionApiException; import org.killbill.billing.util.audit.ChangeType; import org.killbill.billing.util.cache.CacheControllerDispatcher; import org.killbill.billing.util.callcontext.InternalCallContextFactory; import org.killbill.billing.util.dao.NonEntityDao; import org.killbill.billing.util.entity.dao.EntityDaoBase; import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper; import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper; import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory; import org.killbill.billing.util.tag.TagDefinition; import org.killbill.billing.util.tag.api.user.TagEventBuilder; import org.killbill.bus.api.PersistentBus; import org.killbill.clock.Clock; import org.skife.jdbi.v2.IDBI; import org.skife.jdbi.v2.exceptions.TransactionFailedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.Iterators; import com.google.inject.Inject; public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinitionModelDao, TagDefinition, TagDefinitionApiException> implements TagDefinitionDao { private static final Logger log = LoggerFactory.getLogger(DefaultTagDefinitionDao.class); private final TagEventBuilder tagEventBuilder; private final PersistentBus bus; @Inject public DefaultTagDefinitionDao(final IDBI dbi, final TagEventBuilder tagEventBuilder, final PersistentBus bus, final Clock clock, final CacheControllerDispatcher controllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) { super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controllerDispatcher, nonEntityDao, internalCallContextFactory), TagDefinitionSqlDao.class); this.tagEventBuilder = tagEventBuilder; this.bus = bus; } @Override public List<TagDefinitionModelDao> getTagDefinitions(final boolean includeSystemTags, final InternalTenantContext context) { return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<TagDefinitionModelDao>>() { @Override public List<TagDefinitionModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception { // Get user definitions from the database final TagDefinitionSqlDao tagDefinitionSqlDao = entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class); final Iterator<TagDefinitionModelDao> all = tagDefinitionSqlDao.getAll(context); final List<TagDefinitionModelDao> definitionList = new LinkedList<TagDefinitionModelDao>(); Iterators.addAll(definitionList, all); // Add invoice tag definitions definitionList.addAll(SystemTags.get(includeSystemTags)); return definitionList; } }); } @Override public TagDefinitionModelDao getByName(final String definitionName, final InternalTenantContext context) { return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<TagDefinitionModelDao>() { @Override public TagDefinitionModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception { final TagDefinitionModelDao tagDefinitionModelDao = SystemTags.lookup(definitionName); return tagDefinitionModelDao != null ? tagDefinitionModelDao : entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class).getByName(definitionName, context); } }); } @Override public TagDefinitionModelDao getById(final UUID definitionId, final InternalTenantContext context) { return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<TagDefinitionModelDao>() { @Override public TagDefinitionModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception { final TagDefinitionModelDao tagDefinitionModelDao = SystemTags.lookup(definitionId); return tagDefinitionModelDao != null ? tagDefinitionModelDao : entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class).getById(definitionId.toString(), context); } }); } @Override public List<TagDefinitionModelDao> getByIds(final Collection<UUID> definitionIds, final InternalTenantContext context) { return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<TagDefinitionModelDao>>() { @Override public List<TagDefinitionModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception { final List<TagDefinitionModelDao> result = new LinkedList<TagDefinitionModelDao>(); for (final UUID cur : definitionIds) { final TagDefinitionModelDao tagDefinitionModelDao = SystemTags.lookup(cur); if (tagDefinitionModelDao != null) { result.add(tagDefinitionModelDao); } } if (definitionIds.size() > 0) { result.addAll(entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class).getByIds(Collections2.transform(definitionIds, new Function<UUID, String>() { @Override public String apply(final UUID input) { return input.toString(); } }), context)); } return result; } }); } @Override public TagDefinitionModelDao create(final String definitionName, final String description, final InternalCallContext context) throws TagDefinitionApiException { // Make sure a invoice tag with this name don't already exist if (TagModelDaoHelper.isControlTag(definitionName)) { throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_CONFLICTS_WITH_CONTROL_TAG, definitionName); } try { return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<TagDefinitionModelDao>() { @Override public TagDefinitionModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception { final TagDefinitionSqlDao tagDefinitionSqlDao = entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class); // Make sure the tag definition doesn't exist already final TagDefinitionModelDao existingDefinition = tagDefinitionSqlDao.getByName(definitionName, context); if (existingDefinition != null) { throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_ALREADY_EXISTS, definitionName); } // Create it final TagDefinitionModelDao tagDefinition = new TagDefinitionModelDao(context.getCreatedDate(), definitionName, description); createAndRefresh(tagDefinitionSqlDao, tagDefinition, context); // Post an event to the bus final boolean isControlTag = TagModelDaoHelper.isControlTag(tagDefinition.getName()); final TagDefinitionInternalEvent tagDefinitionEvent; if (isControlTag) { tagDefinitionEvent = tagEventBuilder.newControlTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()); } else { tagDefinitionEvent = tagEventBuilder.newUserTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()); } try { bus.postFromTransaction(tagDefinitionEvent, entitySqlDaoWrapperFactory.getHandle().getConnection()); } catch (final PersistentBus.EventBusException e) { log.warn("Failed to post tag definition creation event for tagDefinitionId='{}'", tagDefinition.getId(), e); } return tagDefinition; } }); } catch (final TransactionFailedException exception) { if (exception.getCause() instanceof TagDefinitionApiException) { throw (TagDefinitionApiException) exception.getCause(); } else { throw exception; } } } @Override public void deleteById(final UUID definitionId, final InternalCallContext context) throws TagDefinitionApiException { try { transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() { @Override public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception { final TagDefinitionSqlDao tagDefinitionSqlDao = entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class); // Make sure the tag definition exists final TagDefinitionModelDao tagDefinition = tagDefinitionSqlDao.getById(definitionId.toString(), context); if (tagDefinition == null) { throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_DOES_NOT_EXIST, definitionId); } // Make sure it is not used currently if (tagDefinitionSqlDao.tagDefinitionUsageCount(definitionId.toString(), context) > 0) { throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_IN_USE, definitionId); } // Delete it tagDefinitionSqlDao.markTagDefinitionAsDeleted(definitionId.toString(), context); postBusEventFromTransaction(tagDefinition, tagDefinition, ChangeType.DELETE, entitySqlDaoWrapperFactory, context); return null; } }); } catch (final TransactionFailedException exception) { if (exception.getCause() instanceof TagDefinitionApiException) { throw (TagDefinitionApiException) exception.getCause(); } else { throw exception; } } } protected void postBusEventFromTransaction(final TagDefinitionModelDao tagDefinition, final TagDefinitionModelDao savedTagDefinition, final ChangeType changeType, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws BillingExceptionBase { final TagDefinitionInternalEvent tagDefinitionEvent; final boolean isControlTag = TagModelDaoHelper.isControlTag(tagDefinition.getName()); switch (changeType) { case INSERT: tagDefinitionEvent = (isControlTag) ? tagEventBuilder.newControlTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()) : tagEventBuilder.newUserTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()); break; case DELETE: tagDefinitionEvent = (isControlTag) ? tagEventBuilder.newControlTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()) : tagEventBuilder.newUserTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()); break; default: return; } try { bus.postFromTransaction(tagDefinitionEvent, entitySqlDaoWrapperFactory.getHandle().getConnection()); } catch (final PersistentBus.EventBusException e) { log.warn("Failed to post tag definition event for tagDefinitionId='{}'", tagDefinition.getId().toString(), e); } } @Override protected TagDefinitionApiException generateAlreadyExistsException(final TagDefinitionModelDao entity, final InternalCallContext context) { return new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_ALREADY_EXISTS, entity.getId()); } }