blob: 29583b67f2992bf5b874279fc772fb4c7254c10e [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.oozie.service;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.oozie.ErrorCode;
import org.apache.oozie.util.ParamChecker;
import org.apache.oozie.util.XLog;
/**
* The UUID service generates unique IDs.
* <p>
* The configuration property {@link #CONF_GENERATOR} specifies the ID generation type, 'random' or 'counter'.
* <p>
* For 'random' uses the JDK UUID.randomUUID() method.
* <p>
* For 'counter' uses a counter postfixed wit the system start up time.
*/
public class UUIDService implements Service {
public static final String CONF_PREFIX = Service.CONF_PREFIX + "UUIDService.";
public static final String CONF_GENERATOR = CONF_PREFIX + "generator";
public static final int MAX_OOZIE_JOB_ID_LEN = 40;
public static final int MAX_ACTION_ID_LEN = 255;
protected String startTime;
private AtomicLong counter;
private String systemId;
/**
* Initialize the UUID service.
*
* @param services services instance.
* @throws ServiceException thrown if the UUID service could not be initialized.
*/
@Override
public void init(Services services) throws ServiceException {
String genType = ConfigurationService.get(services.getConf(), CONF_GENERATOR).trim();
if (genType.equals("counter")) {
counter = new AtomicLong();
resetStartTime();
}
else {
if (!genType.equals("random")) {
throw new ServiceException(ErrorCode.E0120, genType);
}
}
systemId = services.getSystemId();
}
/**
* Destroy the UUID service.
*/
@Override
public void destroy() {
counter = null;
startTime = null;
}
/**
* reset start time
*/
protected void resetStartTime() {
startTime = new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date());
}
/**
* Return the public interface for UUID service.
*
* @return {@link UUIDService}.
*/
@Override
public Class<? extends Service> getInterface() {
return UUIDService.class;
}
protected String longPadding(long number) {
StringBuilder sb = new StringBuilder();
sb.append(number);
if (sb.length() <= 7) {
sb.insert(0, "0000000".substring(sb.length()));
}
return sb.toString();
}
/**
* Create a unique ID.
*
* @param type Type of Id. Generally 'C' for Coordinator, 'W' for Workflow and 'B' for Bundle.
* @return unique ID, id = "${sequence}-${systemId}-[C|W|B]" where,
* sequence is ${padded_counter}-${startTime} whose length is exactly 7 + 1 + 15 = 23 characters.
* systemId is the value defined in the {@link Services#CONF_SYSTEM_ID} configuration property.
* Unique ID Example: 0007728-150515180312570-oozie-oozi-W
*/
public String generateId(ApplicationType type) {
StringBuilder sb = new StringBuilder();
sb.append(getSequence());
sb.append('-').append(systemId);
sb.append('-').append(type.getType());
// 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.
if (sb.length() > MAX_OOZIE_JOB_ID_LEN) {
throw new RuntimeException(XLog.format("ID exceeds limit of " + MAX_OOZIE_JOB_ID_LEN + " characters, [{0}]", sb));
}
return sb.toString();
}
private String getSequence() {
StringBuilder sb = new StringBuilder();
if (counter != null) {
sb.append(createSequence());
}
else {
sb.append(UUID.randomUUID().toString());
if (sb.length() > (37 - systemId.length())) {
sb.setLength(37 - systemId.length());
}
}
return sb.toString();
}
protected String createSequence() {
return appendTimeToSequence(getCounter(), startTime);
}
protected long getCounter() {
return counter.getAndIncrement();
}
protected String appendTimeToSequence(long id, String localStartTime) {
StringBuilder sb = new StringBuilder();
sb.append(longPadding(id)).append('-').append(localStartTime);
return sb.toString();
}
/**
* Create a child ID.
* <p>
* If the same child name is given the returned child ID is the same.
*
* @param id unique ID.
* @param childName child name.
* @return a child ID.
*/
public String generateChildId(String id, String childName) {
id = ParamChecker.notEmpty(id, "id") + "@" + ParamChecker.notEmpty(childName, "childName");
// limitation due to current DB schema for action ID length (255)
if (id.length() > MAX_ACTION_ID_LEN) {
throw new RuntimeException(XLog.format("Child ID exceeds limit of " + MAX_ACTION_ID_LEN + " characters, [{0}]", id));
}
return id;
}
/**
* Return the ID from a child ID.
*
* @param childId child ID.
* @return ID of the child ID.
*/
public String getId(String childId) {
int index = ParamChecker.notEmpty(childId, "childId").indexOf("@");
if (index == -1) {
throw new IllegalArgumentException(XLog.format("invalid child id [{0}]", childId));
}
return childId.substring(0, index);
}
/**
* Return the child name from a child ID.
*
* @param childId child ID.
* @return child name.
*/
public String getChildName(String childId) {
int index = ParamChecker.notEmpty(childId, "childId").indexOf("@");
if (index == -1) {
throw new IllegalArgumentException(XLog.format("invalid child id [{0}]", childId));
}
return childId.substring(index + 1);
}
public enum ApplicationType {
WORKFLOW('W'), COORDINATOR('C'), BUNDLE('B');
private final char type;
private ApplicationType(char type) {
this.type = type;
}
public char getType() {
return type;
}
}
}