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