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 package org.apache.oozie.service; 019 020 import java.text.SimpleDateFormat; 021 import java.util.Date; 022 import java.util.UUID; 023 import java.util.concurrent.atomic.AtomicLong; 024 025 import org.apache.oozie.ErrorCode; 026 import org.apache.oozie.util.ParamChecker; 027 import 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 */ 038 public 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 private 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 = services.getConf().get(CONF_GENERATOR, "counter").trim(); 057 if (genType.equals("counter")) { 058 counter = new AtomicLong(); 059 startTime = new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date()); 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 * Return the public interface for UUID service. 080 * 081 * @return {@link UUIDService}. 082 */ 083 @Override 084 public Class<? extends Service> getInterface() { 085 return UUIDService.class; 086 } 087 088 private String longPadding(long number) { 089 StringBuilder sb = new StringBuilder(); 090 sb.append(number); 091 if (sb.length() <= 7) { 092 sb.insert(0, "0000000".substring(sb.length())); 093 } 094 return sb.toString(); 095 } 096 097 /** 098 * Create a unique ID. 099 * 100 * @param type: Type of Id. Generally 'C' for Coordinator and 'W' for Workflow. 101 * @return unique ID. 102 */ 103 public String generateId(ApplicationType type) { 104 StringBuilder sb = new StringBuilder(); 105 106 if (counter != null) { 107 sb.append(longPadding(counter.getAndIncrement())).append('-').append(startTime); 108 } 109 else { 110 sb.append(UUID.randomUUID().toString()); 111 if (sb.length() > (37 - systemId.length())) { 112 sb.setLength(37 - systemId.length()); 113 } 114 } 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 /** 125 * Create a child ID. 126 * <p/> 127 * If the same child name is given the returned child ID is the same. 128 * 129 * @param id unique ID. 130 * @param childName child name. 131 * @return a child ID. 132 */ 133 public String generateChildId(String id, String childName) { 134 id = ParamChecker.notEmpty(id, "id") + "@" + ParamChecker.notEmpty(childName, "childName"); 135 136 // limitation due to current DB schema for action ID length (100) 137 if (id.length() > 95) { 138 throw new RuntimeException(XLog.format("Child ID exceeds limit of 95 characters, [{0}]", id)); 139 } 140 return id; 141 } 142 143 /** 144 * Return the ID from a child ID. 145 * 146 * @param childId child ID. 147 * @return ID of the child ID. 148 */ 149 public String getId(String childId) { 150 int index = ParamChecker.notEmpty(childId, "childId").indexOf("@"); 151 if (index == -1) { 152 throw new IllegalArgumentException(XLog.format("invalid child id [{0}]", childId)); 153 } 154 return childId.substring(0, index); 155 } 156 157 /** 158 * Return the child name from a child ID. 159 * 160 * @param childId child ID. 161 * @return child name. 162 */ 163 public String getChildName(String childId) { 164 int index = ParamChecker.notEmpty(childId, "childId").indexOf("@"); 165 if (index == -1) { 166 throw new IllegalArgumentException(XLog.format("invalid child id [{0}]", childId)); 167 } 168 return childId.substring(index + 1); 169 } 170 171 public enum ApplicationType { 172 WORKFLOW('W'), COORDINATOR('C'), BUNDLE('B'); 173 private final char type; 174 175 private ApplicationType(char type) { 176 this.type = type; 177 } 178 179 public char getType() { 180 return type; 181 } 182 } 183 }