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