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