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
019package org.apache.oozie.coord.input.dependency;
020
021import java.io.DataInput;
022import java.io.DataOutput;
023import java.io.IOException;
024import java.util.ArrayList;
025import java.util.Collection;
026import java.util.Date;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030import java.util.Map.Entry;
031import java.util.Set;
032
033import org.apache.commons.lang.StringUtils;
034import org.apache.hadoop.io.Writable;
035import org.apache.oozie.CoordinatorActionBean;
036import org.apache.oozie.command.CommandException;
037import org.apache.oozie.command.coord.CoordCommandUtils;
038import org.apache.oozie.coord.CoordELFunctions;
039import org.apache.oozie.coord.input.logic.CoordInputLogicEvaluatorUtil;
040import org.apache.oozie.dependency.ActionDependency;
041import org.apache.oozie.util.DateUtils;
042import org.apache.oozie.util.WritableUtils;
043import org.jdom.Element;
044import org.jdom.JDOMException;
045
046public abstract class AbstractCoordInputDependency implements Writable, CoordInputDependency {
047    protected boolean isDependencyMet = false;
048    /*
049     * Transient variables only used for processing, not stored in DB.
050     */
051    protected transient Map<String, List<String>> missingDependenciesSet = new HashMap<String, List<String>>();
052    protected transient Map<String, List<String>> availableDependenciesSet = new HashMap<String, List<String>>();
053    protected Map<String, List<CoordInputInstance>> dependencyMap = new HashMap<String, List<CoordInputInstance>>();
054
055    public AbstractCoordInputDependency() {
056    }
057
058
059    public AbstractCoordInputDependency(Map<String, List<CoordInputInstance>> dependencyMap) {
060        this.dependencyMap = dependencyMap;
061        generateDependencies();
062    }
063
064    public void addInputInstanceList(String inputEventName, List<CoordInputInstance> inputInstanceList) {
065        dependencyMap.put(inputEventName, inputInstanceList);
066    }
067
068    public Map<String, List<CoordInputInstance>> getDependencyMap() {
069        return dependencyMap;
070    }
071
072    public void setDependencyMap(Map<String, List<CoordInputInstance>> dependencyMap) {
073        this.dependencyMap = dependencyMap;
074    }
075
076    public void addToAvailableDependencies(String dataSet, CoordInputInstance coordInputInstance) {
077        coordInputInstance.setAvailability(true);
078        List<String> availableSet = availableDependenciesSet.get(dataSet);
079        if (availableSet == null) {
080            availableSet = new ArrayList<String>();
081            availableDependenciesSet.put(dataSet, availableSet);
082        }
083        availableSet.add(coordInputInstance.getInputDataInstance());
084        removeFromMissingDependencies(dataSet, coordInputInstance);
085    }
086
087    public void removeFromMissingDependencies(String dataSet, CoordInputInstance coordInputInstance) {
088        coordInputInstance.setAvailability(true);
089        List<String> missingSet = missingDependenciesSet.get(dataSet);
090        if (missingSet != null) {
091            missingSet.remove(coordInputInstance.getInputDataInstance());
092            if (missingSet.isEmpty()) {
093                missingDependenciesSet.remove(dataSet);
094            }
095        }
096
097    }
098
099    public void addToMissingDependencies(String dataSet, CoordInputInstance coordInputInstance) {
100        List<String> availableSet = missingDependenciesSet.get(dataSet);
101        if (availableSet == null) {
102            availableSet = new ArrayList<String>();
103        }
104        availableSet.add(coordInputInstance.getInputDataInstance());
105        missingDependenciesSet.put(dataSet, availableSet);
106
107    }
108
109    protected void generateDependencies() {
110        try {
111            missingDependenciesSet = new HashMap<String, List<String>>();
112            availableDependenciesSet = new HashMap<String, List<String>>();
113
114            for (Entry<String, List<CoordInputInstance>> entry : dependencyMap.entrySet()) {
115                String key = entry.getKey();
116                for (CoordInputInstance coordInputInstance : entry.getValue()) {
117                    if (coordInputInstance.isAvailable()) {
118                        addToAvailableDependencies(key, coordInputInstance);
119                    }
120                    else {
121                        addToMissingDependencies(key, coordInputInstance);
122                    }
123                }
124            }
125        }
126        catch (Exception e) {
127            throw new RuntimeException(e);
128        }
129
130    }
131
132    public List<String> getAvailableDependencies(String dataSet) {
133        if (availableDependenciesSet.get(dataSet) != null) {
134            return availableDependenciesSet.get(dataSet);
135        }
136        else {
137            return new ArrayList<String>();
138        }
139
140    }
141
142    public String getMissingDependencies(String dataSet) {
143        StringBuilder sb = new StringBuilder();
144        for (String dependencies : missingDependenciesSet.get(dataSet)) {
145            sb.append(dependencies).append("#");
146        }
147        return sb.toString();
148    }
149
150    public void addToAvailableDependencies(String dataSet, String availableSet) {
151        List<CoordInputInstance> list = dependencyMap.get(dataSet);
152        if (list == null) {
153            list = new ArrayList<CoordInputInstance>();
154            dependencyMap.put(dataSet, list);
155        }
156
157        for (String available : availableSet.split(CoordELFunctions.INSTANCE_SEPARATOR)) {
158            CoordInputInstance coordInstance = new CoordInputInstance(available, true);
159            list.add(coordInstance);
160            addToAvailableDependencies(dataSet, coordInstance);
161        }
162
163    }
164
165    public String getMissingDependencies() {
166        StringBuilder sb = new StringBuilder();
167        if (missingDependenciesSet != null) {
168            for (List<String> dependenciesList : missingDependenciesSet.values()) {
169                for (String dependencies : dependenciesList) {
170                    sb.append(dependencies).append("#");
171                }
172            }
173        }
174        return sb.toString();
175    }
176
177    public List<String> getMissingDependenciesAsList() {
178        List<String> missingDependencies = new ArrayList<String>();
179        for (List<String> dependenciesList : missingDependenciesSet.values()) {
180            missingDependencies.addAll(dependenciesList);
181        }
182        return missingDependencies;
183    }
184
185    public List<String> getAvailableDependenciesAsList() {
186        List<String> availableDependencies = new ArrayList<String>();
187        for (List<String> dependenciesList : availableDependenciesSet.values()) {
188            availableDependencies.addAll(dependenciesList);
189
190        }
191        return availableDependencies;
192    }
193
194    public String serialize() throws IOException {
195        return CoordInputDependencyFactory.getMagicNumber()
196                + new String(WritableUtils.toByteArray(this), CoordInputDependencyFactory.CHAR_ENCODING);
197
198    }
199
200    public String getListAsString(List<String> dataSets) {
201        StringBuilder sb = new StringBuilder();
202        for (String dependencies : dataSets) {
203            sb.append(dependencies).append("#");
204        }
205
206        return sb.toString();
207    }
208
209    public void setDependencyMet(boolean isDependencyMeet) {
210        this.isDependencyMet = isDependencyMeet;
211    }
212
213    public boolean isDependencyMet() {
214        return missingDependenciesSet.isEmpty() || isDependencyMet;
215    }
216
217    public boolean isUnResolvedDependencyMet() {
218        return false;
219    }
220
221
222    @Override
223    public void addToAvailableDependencies(Collection<String> availableList) {
224        for (Entry<String, List<CoordInputInstance>> dependenciesList : dependencyMap.entrySet()) {
225            for (CoordInputInstance coordInputInstance : dependenciesList.getValue()) {
226                if (availableList.contains(coordInputInstance.getInputDataInstance()))
227                    addToAvailableDependencies(dependenciesList.getKey(), coordInputInstance);
228            }
229        }
230    }
231
232    @Override
233    public ActionDependency checkPushMissingDependencies(CoordinatorActionBean coordAction,
234            boolean registerForNotification) throws CommandException, IOException,
235            JDOMException {
236        boolean status = new CoordInputLogicEvaluatorUtil(coordAction).checkPushDependencies();
237        if (status) {
238            coordAction.getPushInputDependencies().setDependencyMet(true);
239        }
240        return new ActionDependency(coordAction.getPushInputDependencies().getMissingDependenciesAsList(), coordAction
241                .getPushInputDependencies().getAvailableDependenciesAsList());
242
243    }
244
245    public boolean checkPullMissingDependencies(CoordinatorActionBean coordAction,
246            StringBuilder existList, StringBuilder nonExistList) throws IOException, JDOMException {
247        boolean status = new CoordInputLogicEvaluatorUtil(coordAction).checkPullMissingDependencies();
248        if (status) {
249            coordAction.getPullInputDependencies().setDependencyMet(true);
250        }
251        return status;
252
253    }
254
255    public boolean isChangeInDependency(StringBuilder nonExistList, String missingDependencies,
256            StringBuilder nonResolvedList, boolean status) {
257        if (!StringUtils.isEmpty(missingDependencies)) {
258            return !missingDependencies.equals(getMissingDependencies());
259        }
260        else {
261            return true;
262        }
263    }
264
265    @SuppressWarnings("unchecked")
266    public boolean checkUnresolved(CoordinatorActionBean coordAction, Element eAction)
267            throws Exception {
268        String actualTimeStr = eAction.getAttributeValue("action-actual-time");
269        Element inputList = eAction.getChild("input-events", eAction.getNamespace());
270        Date actualTime = null;
271        if (actualTimeStr == null) {
272            actualTime = new Date();
273        }
274        else {
275            actualTime = DateUtils.parseDateOozieTZ(actualTimeStr);
276        }
277        if (inputList == null) {
278            return true;
279        }
280        List<Element> eDataEvents = inputList.getChildren("data-in", eAction.getNamespace());
281        for (Element dEvent : eDataEvents) {
282            if (dEvent.getChild(CoordCommandUtils.UNRESOLVED_INSTANCES_TAG, dEvent.getNamespace()) == null) {
283                continue;
284            }
285            String unResolvedInstance = dEvent.getChild(CoordCommandUtils.UNRESOLVED_INSTANCES_TAG,
286                    dEvent.getNamespace()).getTextTrim();
287            String name = dEvent.getAttribute("name").getValue();
288            addUnResolvedList(name, unResolvedInstance);
289        }
290        return new CoordInputLogicEvaluatorUtil(coordAction).checkUnResolved(actualTime);
291    }
292
293    @Override
294    public void write(DataOutput out) throws IOException {
295        WritableUtils.writeStringAsBytes(out,INTERNAL_VERSION_ID);
296        out.writeBoolean(isDependencyMet);
297        WritableUtils.writeMapWithList(out, dependencyMap);
298    }
299
300    @Override
301    public void readFields(DataInput in) throws IOException {
302        WritableUtils.readBytesAsString(in);
303        this.isDependencyMet = in.readBoolean();
304        dependencyMap = WritableUtils.readMapWithList(in, CoordInputInstance.class);
305        generateDependencies();
306    }
307
308    public boolean isDataSetResolved(String dataSet){
309        if(getAvailableDependencies(dataSet) ==null|| getDependencyMap().get(dataSet) == null){
310            return false;
311        }
312        return getAvailableDependencies(dataSet).size() == getDependencyMap().get(dataSet).size();
313    }
314
315    @Override
316    public Map<String, ActionDependency> getMissingDependencies(CoordinatorActionBean coordAction)
317            throws CommandException, IOException, JDOMException {
318        Map<String, ActionDependency> missingDependenciesMap = new HashMap<String, ActionDependency>();
319        for (String key : missingDependenciesSet.keySet()) {
320            missingDependenciesMap.put(key, new ActionDependency(missingDependenciesSet.get(key), new ArrayList<String>()));
321        }
322        return missingDependenciesMap;
323    }
324
325    public String getFirstMissingDependency() {
326        return null;
327    }
328
329}