| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF 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.apache.oozie.service; |
| |
| import java.text.SimpleDateFormat; |
| import java.util.Date; |
| import java.util.UUID; |
| import java.util.concurrent.atomic.AtomicLong; |
| |
| import org.apache.oozie.ErrorCode; |
| import org.apache.oozie.util.ParamChecker; |
| import org.apache.oozie.util.XLog; |
| |
| /** |
| * The UUID service generates unique IDs. |
| * <p> |
| * The configuration property {@link #CONF_GENERATOR} specifies the ID generation type, 'random' or 'counter'. |
| * <p> |
| * For 'random' uses the JDK UUID.randomUUID() method. |
| * <p> |
| * For 'counter' uses a counter postfixed wit the system start up time. |
| */ |
| public class UUIDService implements Service { |
| |
| public static final String CONF_PREFIX = Service.CONF_PREFIX + "UUIDService."; |
| |
| public static final String CONF_GENERATOR = CONF_PREFIX + "generator"; |
| |
| public static final int MAX_OOZIE_JOB_ID_LEN = 40; |
| |
| public static final int MAX_ACTION_ID_LEN = 255; |
| |
| protected String startTime; |
| private AtomicLong counter; |
| private String systemId; |
| |
| /** |
| * Initialize the UUID service. |
| * |
| * @param services services instance. |
| * @throws ServiceException thrown if the UUID service could not be initialized. |
| */ |
| @Override |
| public void init(Services services) throws ServiceException { |
| String genType = ConfigurationService.get(services.getConf(), CONF_GENERATOR).trim(); |
| if (genType.equals("counter")) { |
| counter = new AtomicLong(); |
| resetStartTime(); |
| } |
| else { |
| if (!genType.equals("random")) { |
| throw new ServiceException(ErrorCode.E0120, genType); |
| } |
| } |
| systemId = services.getSystemId(); |
| } |
| |
| /** |
| * Destroy the UUID service. |
| */ |
| @Override |
| public void destroy() { |
| counter = null; |
| startTime = null; |
| } |
| |
| /** |
| * reset start time |
| */ |
| protected void resetStartTime() { |
| startTime = new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date()); |
| } |
| |
| /** |
| * Return the public interface for UUID service. |
| * |
| * @return {@link UUIDService}. |
| */ |
| @Override |
| public Class<? extends Service> getInterface() { |
| return UUIDService.class; |
| } |
| |
| protected String longPadding(long number) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(number); |
| if (sb.length() <= 7) { |
| sb.insert(0, "0000000".substring(sb.length())); |
| } |
| return sb.toString(); |
| } |
| |
| /** |
| * Create a unique ID. |
| * |
| * @param type Type of Id. Generally 'C' for Coordinator, 'W' for Workflow and 'B' for Bundle. |
| * @return unique ID, id = "${sequence}-${systemId}-[C|W|B]" where, |
| * sequence is ${padded_counter}-${startTime} whose length is exactly 7 + 1 + 15 = 23 characters. |
| * systemId is the value defined in the {@link Services#CONF_SYSTEM_ID} configuration property. |
| * Unique ID Example: 0007728-150515180312570-oozie-oozi-W |
| */ |
| public String generateId(ApplicationType type) { |
| StringBuilder sb = new StringBuilder(); |
| |
| sb.append(getSequence()); |
| sb.append('-').append(systemId); |
| sb.append('-').append(type.getType()); |
| // limited to MAX_OOZIE_JOB_ID_LEN as this partial id will be stored in the Action Id field with db schema limit of 255. |
| if (sb.length() > MAX_OOZIE_JOB_ID_LEN) { |
| throw new RuntimeException(XLog.format("ID exceeds limit of " + MAX_OOZIE_JOB_ID_LEN + " characters, [{0}]", sb)); |
| } |
| return sb.toString(); |
| } |
| |
| private String getSequence() { |
| StringBuilder sb = new StringBuilder(); |
| if (counter != null) { |
| sb.append(createSequence()); |
| } |
| else { |
| sb.append(UUID.randomUUID().toString()); |
| if (sb.length() > (37 - systemId.length())) { |
| sb.setLength(37 - systemId.length()); |
| } |
| } |
| return sb.toString(); |
| } |
| |
| protected String createSequence() { |
| return appendTimeToSequence(getCounter(), startTime); |
| } |
| |
| protected long getCounter() { |
| return counter.getAndIncrement(); |
| } |
| |
| protected String appendTimeToSequence(long id, String localStartTime) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(longPadding(id)).append('-').append(localStartTime); |
| return sb.toString(); |
| } |
| |
| /** |
| * Create a child ID. |
| * <p> |
| * If the same child name is given the returned child ID is the same. |
| * |
| * @param id unique ID. |
| * @param childName child name. |
| * @return a child ID. |
| */ |
| public String generateChildId(String id, String childName) { |
| id = ParamChecker.notEmpty(id, "id") + "@" + ParamChecker.notEmpty(childName, "childName"); |
| |
| // limitation due to current DB schema for action ID length (255) |
| if (id.length() > MAX_ACTION_ID_LEN) { |
| throw new RuntimeException(XLog.format("Child ID exceeds limit of " + MAX_ACTION_ID_LEN + " characters, [{0}]", id)); |
| } |
| return id; |
| } |
| |
| /** |
| * Return the ID from a child ID. |
| * |
| * @param childId child ID. |
| * @return ID of the child ID. |
| */ |
| public String getId(String childId) { |
| int index = ParamChecker.notEmpty(childId, "childId").indexOf("@"); |
| if (index == -1) { |
| throw new IllegalArgumentException(XLog.format("invalid child id [{0}]", childId)); |
| } |
| return childId.substring(0, index); |
| } |
| |
| /** |
| * Return the child name from a child ID. |
| * |
| * @param childId child ID. |
| * @return child name. |
| */ |
| public String getChildName(String childId) { |
| int index = ParamChecker.notEmpty(childId, "childId").indexOf("@"); |
| if (index == -1) { |
| throw new IllegalArgumentException(XLog.format("invalid child id [{0}]", childId)); |
| } |
| return childId.substring(index + 1); |
| } |
| |
| public enum ApplicationType { |
| WORKFLOW('W'), COORDINATOR('C'), BUNDLE('B'); |
| private final char type; |
| |
| private ApplicationType(char type) { |
| this.type = type; |
| } |
| |
| public char getType() { |
| return type; |
| } |
| } |
| } |