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    }