001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.oozie.service; 019 020import java.text.SimpleDateFormat; 021import java.util.Date; 022import java.util.UUID; 023import java.util.concurrent.atomic.AtomicLong; 024 025import org.apache.oozie.ErrorCode; 026import org.apache.oozie.util.ParamChecker; 027import org.apache.oozie.util.XLog; 028 029/** 030 * The UUID service generates unique IDs. 031 * <p> 032 * The configuration property {@link #CONF_GENERATOR} specifies the ID generation type, 'random' or 'counter'. 033 * <p> 034 * For 'random' uses the JDK UUID.randomUUID() method. 035 * <p> 036 * For 'counter' uses a counter postfixed wit the system start up time. 037 */ 038public class UUIDService implements Service { 039 040 public static final String CONF_PREFIX = Service.CONF_PREFIX + "UUIDService."; 041 042 public static final String CONF_GENERATOR = CONF_PREFIX + "generator"; 043 044 public static final int MAX_OOZIE_JOB_ID_LEN = 40; 045 046 public static final int MAX_ACTION_ID_LEN = 255; 047 048 protected String startTime; 049 private AtomicLong counter; 050 private String systemId; 051 052 /** 053 * Initialize the UUID service. 054 * 055 * @param services services instance. 056 * @throws ServiceException thrown if the UUID service could not be initialized. 057 */ 058 @Override 059 public void init(Services services) throws ServiceException { 060 String genType = ConfigurationService.get(services.getConf(), CONF_GENERATOR).trim(); 061 if (genType.equals("counter")) { 062 counter = new AtomicLong(); 063 resetStartTime(); 064 } 065 else { 066 if (!genType.equals("random")) { 067 throw new ServiceException(ErrorCode.E0120, genType); 068 } 069 } 070 systemId = services.getSystemId(); 071 } 072 073 /** 074 * Destroy the UUID service. 075 */ 076 @Override 077 public void destroy() { 078 counter = null; 079 startTime = null; 080 } 081 082 /** 083 * reset start time 084 */ 085 protected void resetStartTime() { 086 startTime = new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date()); 087 } 088 089 /** 090 * Return the public interface for UUID service. 091 * 092 * @return {@link UUIDService}. 093 */ 094 @Override 095 public Class<? extends Service> getInterface() { 096 return UUIDService.class; 097 } 098 099 protected String longPadding(long number) { 100 StringBuilder sb = new StringBuilder(); 101 sb.append(number); 102 if (sb.length() <= 7) { 103 sb.insert(0, "0000000".substring(sb.length())); 104 } 105 return sb.toString(); 106 } 107 108 /** 109 * Create a unique ID. 110 * 111 * @param type Type of Id. Generally 'C' for Coordinator, 'W' for Workflow and 'B' for Bundle. 112 * @return unique ID, id = "${sequence}-${systemId}-[C|W|B]" where, 113 * sequence is ${padded_counter}-${startTime} whose length is exactly 7 + 1 + 15 = 23 characters. 114 * systemId is the value defined in the {@link Services#CONF_SYSTEM_ID} configuration property. 115 * Unique ID Example: 0007728-150515180312570-oozie-oozi-W 116 */ 117 public String generateId(ApplicationType type) { 118 StringBuilder sb = new StringBuilder(); 119 120 sb.append(getSequence()); 121 sb.append('-').append(systemId); 122 sb.append('-').append(type.getType()); 123 // 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. 124 if (sb.length() > MAX_OOZIE_JOB_ID_LEN) { 125 throw new RuntimeException(XLog.format("ID exceeds limit of " + MAX_OOZIE_JOB_ID_LEN + " characters, [{0}]", sb)); 126 } 127 return sb.toString(); 128 } 129 130 private String getSequence() { 131 StringBuilder sb = new StringBuilder(); 132 if (counter != null) { 133 sb.append(createSequence()); 134 } 135 else { 136 sb.append(UUID.randomUUID().toString()); 137 if (sb.length() > (37 - systemId.length())) { 138 sb.setLength(37 - systemId.length()); 139 } 140 } 141 return sb.toString(); 142 } 143 144 protected String createSequence() { 145 return appendTimeToSequence(getCounter(), startTime); 146 } 147 148 protected long getCounter() { 149 return counter.getAndIncrement(); 150 } 151 152 protected String appendTimeToSequence(long id, String localStartTime) { 153 StringBuilder sb = new StringBuilder(); 154 sb.append(longPadding(id)).append('-').append(localStartTime); 155 return sb.toString(); 156 } 157 158 /** 159 * Create a child ID. 160 * <p> 161 * If the same child name is given the returned child ID is the same. 162 * 163 * @param id unique ID. 164 * @param childName child name. 165 * @return a child ID. 166 */ 167 public String generateChildId(String id, String childName) { 168 id = ParamChecker.notEmpty(id, "id") + "@" + ParamChecker.notEmpty(childName, "childName"); 169 170 // limitation due to current DB schema for action ID length (255) 171 if (id.length() > MAX_ACTION_ID_LEN) { 172 throw new RuntimeException(XLog.format("Child ID exceeds limit of " + MAX_ACTION_ID_LEN + " characters, [{0}]", id)); 173 } 174 return id; 175 } 176 177 /** 178 * Return the ID from a child ID. 179 * 180 * @param childId child ID. 181 * @return ID of the child ID. 182 */ 183 public String getId(String childId) { 184 int index = ParamChecker.notEmpty(childId, "childId").indexOf("@"); 185 if (index == -1) { 186 throw new IllegalArgumentException(XLog.format("invalid child id [{0}]", childId)); 187 } 188 return childId.substring(0, index); 189 } 190 191 /** 192 * Return the child name from a child ID. 193 * 194 * @param childId child ID. 195 * @return child name. 196 */ 197 public String getChildName(String childId) { 198 int index = ParamChecker.notEmpty(childId, "childId").indexOf("@"); 199 if (index == -1) { 200 throw new IllegalArgumentException(XLog.format("invalid child id [{0}]", childId)); 201 } 202 return childId.substring(index + 1); 203 } 204 205 public enum ApplicationType { 206 WORKFLOW('W'), COORDINATOR('C'), BUNDLE('B'); 207 private final char type; 208 209 private ApplicationType(char type) { 210 this.type = type; 211 } 212 213 public char getType() { 214 return type; 215 } 216 } 217}