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 protected String startTime; 045 private AtomicLong counter; 046 private String systemId; 047 048 /** 049 * Initialize the UUID service. 050 * 051 * @param services services instance. 052 * @throws ServiceException thrown if the UUID service could not be initialized. 053 */ 054 @Override 055 public void init(Services services) throws ServiceException { 056 String genType = ConfigurationService.get(services.getConf(), CONF_GENERATOR).trim(); 057 if (genType.equals("counter")) { 058 counter = new AtomicLong(); 059 resetStartTime(); 060 } 061 else { 062 if (!genType.equals("random")) { 063 throw new ServiceException(ErrorCode.E0120, genType); 064 } 065 } 066 systemId = services.getSystemId(); 067 } 068 069 /** 070 * Destroy the UUID service. 071 */ 072 @Override 073 public void destroy() { 074 counter = null; 075 startTime = null; 076 } 077 078 /** 079 * reset start time 080 * @return 081 */ 082 protected void resetStartTime() { 083 startTime = new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date()); 084 } 085 086 /** 087 * Return the public interface for UUID service. 088 * 089 * @return {@link UUIDService}. 090 */ 091 @Override 092 public Class<? extends Service> getInterface() { 093 return UUIDService.class; 094 } 095 096 protected String longPadding(long number) { 097 StringBuilder sb = new StringBuilder(); 098 sb.append(number); 099 if (sb.length() <= 7) { 100 sb.insert(0, "0000000".substring(sb.length())); 101 } 102 return sb.toString(); 103 } 104 105 /** 106 * Create a unique ID. 107 * 108 * @param type: Type of Id. Generally 'C' for Coordinator and 'W' for Workflow. 109 * @return unique ID. 110 */ 111 public String generateId(ApplicationType type) { 112 StringBuilder sb = new StringBuilder(); 113 114 sb.append(getSequence()); 115 sb.append('-').append(systemId); 116 sb.append('-').append(type.getType()); 117 // limitation due to current DB schema for action ID length (100) 118 if (sb.length() > 40) { 119 throw new RuntimeException(XLog.format("ID exceeds limit of 40 characters, [{0}]", sb)); 120 } 121 return sb.toString(); 122 } 123 124 private String getSequence() { 125 StringBuilder sb = new StringBuilder(); 126 if (counter != null) { 127 sb.append(createSequence()); 128 } 129 else { 130 sb.append(UUID.randomUUID().toString()); 131 if (sb.length() > (37 - systemId.length())) { 132 sb.setLength(37 - systemId.length()); 133 } 134 } 135 return sb.toString(); 136 } 137 138 protected String createSequence() { 139 return appendTimeToSequence(getCounter(), startTime); 140 } 141 142 protected long getCounter() { 143 return counter.getAndIncrement(); 144 } 145 146 protected String appendTimeToSequence(long id, String localStartTime) { 147 StringBuilder sb = new StringBuilder(); 148 sb.append(longPadding(id)).append('-').append(localStartTime); 149 return sb.toString(); 150 } 151 152 /** 153 * Create a child ID. 154 * <p/> 155 * If the same child name is given the returned child ID is the same. 156 * 157 * @param id unique ID. 158 * @param childName child name. 159 * @return a child ID. 160 */ 161 public String generateChildId(String id, String childName) { 162 id = ParamChecker.notEmpty(id, "id") + "@" + ParamChecker.notEmpty(childName, "childName"); 163 164 // limitation due to current DB schema for action ID length (100) 165 if (id.length() > 95) { 166 throw new RuntimeException(XLog.format("Child ID exceeds limit of 95 characters, [{0}]", id)); 167 } 168 return id; 169 } 170 171 /** 172 * Return the ID from a child ID. 173 * 174 * @param childId child ID. 175 * @return ID of the child ID. 176 */ 177 public String getId(String childId) { 178 int index = ParamChecker.notEmpty(childId, "childId").indexOf("@"); 179 if (index == -1) { 180 throw new IllegalArgumentException(XLog.format("invalid child id [{0}]", childId)); 181 } 182 return childId.substring(0, index); 183 } 184 185 /** 186 * Return the child name from a child ID. 187 * 188 * @param childId child ID. 189 * @return child name. 190 */ 191 public String getChildName(String childId) { 192 int index = ParamChecker.notEmpty(childId, "childId").indexOf("@"); 193 if (index == -1) { 194 throw new IllegalArgumentException(XLog.format("invalid child id [{0}]", childId)); 195 } 196 return childId.substring(index + 1); 197 } 198 199 public enum ApplicationType { 200 WORKFLOW('W'), COORDINATOR('C'), BUNDLE('B'); 201 private final char type; 202 203 private ApplicationType(char type) { 204 this.type = type; 205 } 206 207 public char getType() { 208 return type; 209 } 210 } 211}