/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * * 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.jbpm.casemgmt.impl.generator; import org.drools.core.command.impl.ExecutableCommand; import org.jbpm.casemgmt.api.generator.CaseIdGenerator; import org.jbpm.casemgmt.api.generator.CasePrefixNotFoundException; import org.jbpm.shared.services.impl.TransactionalCommandService; import org.jbpm.shared.services.impl.commands.PersistObjectCommand; import org.jbpm.shared.services.impl.commands.QueryNameCommand; import org.jbpm.shared.services.impl.commands.RemoveObjectCommand; import org.kie.api.runtime.Context; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.persistence.NoResultException; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Data base tabled backed case id generator. The underlying table keeps single entry per case prefix and updates it * (by incrementing current value) on each call to generate method. * * Generation is done with pessimistic locking to secure correctness and since it's the only operation in transaction it should not * cause any performance issues. */ public class TableCaseIdGenerator implements CaseIdGenerator { private static final Logger logger = LoggerFactory.getLogger(TableCaseIdGenerator.class); private boolean removeOnUnregister = Boolean.parseBoolean(System.getProperty("org.jbpm.casemgmt.table.generator.clean", "false")); private static final String IDENTIFIER = "DB"; private TransactionalCommandService commandService; public TableCaseIdGenerator(TransactionalCommandService commandService) { this.commandService = commandService; } @Override public String getIdentifier() { return IDENTIFIER; } @Override public void register(String prefix) { CaseIdInfo caseIdInfo = findCaseIdInfoByPrefix(prefix); if (caseIdInfo == null) { logger.debug("Case id prefix {} not yet registered", prefix); caseIdInfo = new CaseIdInfo(); caseIdInfo.setCaseIdPrefix(prefix); caseIdInfo.setCurrentValue(new Long(0)); commandService.execute(new PersistObjectCommand(caseIdInfo)); } else { logger.debug("Case id prefix {} already registered and it's current value is {}", prefix, caseIdInfo.getCurrentValue()); } } @Override public void unregister(String prefix) { if (removeOnUnregister) { CaseIdInfo caseIdInfo = findCaseIdInfoByPrefix(prefix); if (caseIdInfo != null) { commandService.execute(new RemoveObjectCommand(caseIdInfo)); logger.debug("Removed permanently case id info for prefix {}", prefix); } } else { logger.debug("Skipping remove of case id info for prefix {}", prefix); } } @Override public String generate(String prefix, Map<String, Object> optionalParameters) throws CasePrefixNotFoundException { CaseIdInfo caseIdInfo = commandService.execute(new IncrementAndGetCaseIdCommand(prefix)); logger.debug("Next sequence value for case id prefix {} is {}", prefix, caseIdInfo.getCurrentValue()); long nextVal = caseIdInfo.getCurrentValue(); String paddedNumber = String.format("%010d", nextVal); return prefix + "-" + paddedNumber; } protected CaseIdInfo findCaseIdInfoByPrefix(String prefix) { Map<String, Object> params = new HashMap<String, Object>(); params.put("prefix", prefix); params.put("firstResult", 0); params.put("maxResults", 1); List<CaseIdInfo> caseIdInfos = commandService.execute(new QueryNameCommand<List<CaseIdInfo>>("findCaseIdInfoByPrefix", params)); if (caseIdInfos.isEmpty()) { return null; } return caseIdInfos.get(0); } private class IncrementAndGetCaseIdCommand implements ExecutableCommand<CaseIdInfo> { private static final long serialVersionUID = 8670412133363766162L; private String prefix; public IncrementAndGetCaseIdCommand(String prefix) { this.prefix = prefix; } @Override public CaseIdInfo execute(Context context) { Map<String, Object> params = new HashMap<String, Object>(); params.put("prefix", prefix); params.put("firstResult", 0); params.put("maxResults", 1); CaseIdInfo caseIdInfo = null; try { org.jbpm.shared.services.impl.JpaPersistenceContext ctx = (org.jbpm.shared.services.impl.JpaPersistenceContext) context; caseIdInfo = ctx.queryAndLockWithParametersInTransaction("findCaseIdInfoByPrefix",params, true, CaseIdInfo.class); if (caseIdInfo != null) { caseIdInfo.setCurrentValue(caseIdInfo.getCurrentValue() + 1); ctx.merge(caseIdInfo); } } catch (NoResultException e) { } return caseIdInfo; } } }