// 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 streamer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import streamer.debug.FakeSink; import streamer.debug.FakeSource; public class PipelineImpl implements Pipeline { protected String id; protected boolean verbose = System.getProperty("streamer.Pipeline.debug", "false").equals("true"); public PipelineImpl(String id) { this.id = id; elements = initElementMap(id); } protected Map<String, Element> elements; protected HashMap<String, Element> initElementMap(String id) { HashMap<String, Element> map = new HashMap<String, Element>(); map.put(IN, new BaseElement(id + "." + IN)); map.put(OUT, new BaseElement(id + "." + OUT)); return map; } @Override public Link getLink(String padName) { Link link = elements.get(IN).getLink(padName); if (link == null) link = elements.get(OUT).getLink(padName); return link; } @Override public Set<String> getPads(Direction direction) { switch (direction) { case IN: return elements.get(IN).getPads(direction); case OUT: return elements.get(OUT).getPads(direction); } return null; } @Override public void validate() { for (Element element : elements.values()) element.validate(); // Check IN element { Element element = get(IN); int outPadsNumber = element.getPads(Direction.OUT).size(); int inPadsNumber = element.getPads(Direction.IN).size(); if ((outPadsNumber | inPadsNumber) > 0 && (outPadsNumber == 0 || inPadsNumber == 0)) throw new RuntimeException("[ " + this + "] Pads of input element of pipeline are not balanced. Element: " + element + ", output pads: " + element.getPads(Direction.OUT).toString() + ", input pads: " + element.getPads(Direction.IN).toString() + "."); } // Check OUT element { Element element = get(OUT); int outPadsNumber = element.getPads(Direction.OUT).size(); int inPadsNumber = element.getPads(Direction.IN).size(); if ((outPadsNumber | inPadsNumber) > 0 && (outPadsNumber == 0 || inPadsNumber == 0)) throw new RuntimeException("[ " + this + "] Pads of output element of pipeline are not balanced. Element: " + element + ", output pads: " + element.getPads(Direction.OUT).toString() + ", input pads: " + element.getPads(Direction.IN).toString() + "."); } } @Override public void dropLink(String padName) { if (elements.get(IN).getLink(padName) != null) elements.get(IN).dropLink(padName); if (elements.get(OUT).getLink(padName) != null) elements.get(OUT).dropLink(padName); } @Override public void dropLink(Link link) { elements.get(IN).dropLink(link); elements.get(OUT).dropLink(link); } @Override public void replaceLink(Link existingLink, Link newLink) { elements.get(IN).replaceLink(existingLink, newLink); elements.get(OUT).replaceLink(existingLink, newLink); } @Override public void setLink(String padName, Link link, Direction direction) { // Wire links to internal elements instead elements.get(direction.toString()).setLink(padName, link, direction); } @Override public void poll(boolean block) { throw new RuntimeException("Not implemented."); } @Override public void handleData(ByteBuffer buf, Link link) { get(IN).handleData(buf, link); } @Override public void handleEvent(Event event, Direction direction) { switch (direction) { case IN: get(IN).handleEvent(event, direction); break; case OUT: get(OUT).handleEvent(event, direction); break; } } @Override public void add(Element... elements) { for (Element element : elements) { String id = element.getId(); if (this.elements.containsKey(id)) throw new RuntimeException("This pipeline already contains element with same ID. New element: " + element + ", existing element: " + this.elements.get(id) + "."); this.elements.put(id, element); } } @Override public void link(String... elementNames) { elementNames = filterOutEmptyStrings(elementNames); if (elementNames.length < 2) throw new RuntimeException("At least two elements are necessary to create link between them."); // Parse array of element and pad names Element elements[] = new Element[elementNames.length]; String inputPads[] = new String[elementNames.length]; String outputPads[] = new String[elementNames.length]; int i = 0; for (String elementName : elementNames) { if (elementName.contains("< ")) { inputPads[i] = elementName.substring(0, elementName.indexOf("< ")); elementName = elementName.substring(elementName.indexOf("< ") + 2); } else { inputPads[i] = STDIN; } if (elementName.contains(" >")) { outputPads[i] = elementName.substring(elementName.indexOf(" >") + 2); elementName = elementName.substring(0, elementName.indexOf(" >")); } else { outputPads[i] = STDOUT; } elements[i] = get(elementName); if (elements[i] == null) throw new RuntimeException("Cannot find element by name in this pipeline. Element name: \"" + elementName + "\" (" + elementNames[i] + "), pipeline: " + this + "."); i++; } // Link elements for (i = 0; i < elements.length - 1; i++) { Element leftElement = elements[i]; Element rightElement = elements[i + 1]; String leftPad = outputPads[i]; String rightPad = inputPads[i + 1]; String linkId = leftElement.getId() + " >" + leftPad + " | " + rightPad + "< " + rightElement.getId(); if (verbose) System.out.println("[" + this + "] INFO: Linking: " + linkId + "."); Link link = new SyncLink(linkId); leftElement.setLink(leftPad, link, Direction.OUT); rightElement.setLink(rightPad, link, Direction.IN); } } /** * Filter out empty strings from array and return new array with non-empty * elements only. If array contains no empty string, returns same array. */ private String[] filterOutEmptyStrings(String[] strings) { boolean found = false; for (String string : strings) { if (string == null || string.isEmpty()) { found = true; break; } } if (!found) return strings; List<String> filteredStrings = new ArrayList<String>(strings.length); for (String string : strings) if (string != null && !string.isEmpty()) filteredStrings.add(string); return filteredStrings.toArray(new String[filteredStrings.size()]); } @Override public void addAndLink(Element... elements) { add(elements); link(elements); } private void link(Element... elements) { String elementNames[] = new String[elements.length]; int i = 0; for (Element element : elements) { elementNames[i++] = element.getId(); } link(elementNames); } @Override public Element get(String elementName) { return elements.get(elementName); } @Override public Link getLink(String elementName, String padName) { return elements.get(elementName).getLink(padName); } @Override public void setLink(String elementName, String padName, Link link, Direction direction) { elements.get(elementName).setLink(padName, link, direction); } @Override public String getId() { return id; } @Override public void runMainLoop(String elementName, String padName, boolean separateThread, boolean waitForStartEvent) { validate(); Link link = getLink(elementName, padName); if (link == null) throw new NullPointerException("Cannot find link. Element name: " + elementName + ", element: " + get(elementName) + ", pad: " + padName + "."); if (!waitForStartEvent) link.sendEvent(Event.STREAM_START, Direction.OUT); if (separateThread) { Thread thread = new Thread(link); thread.setDaemon(true); thread.start(); } else { link.run(); } } @Override public String toString() { return "Pipeline(" + id + ")"; } /** * Example. */ public static void main(String args[]) { // System.setProperty("streamer.Link.debug", "true"); // System.setProperty("streamer.Element.debug", "true"); // System.setProperty("streamer.Pipeline.debug", "true"); Pipeline pipeline = new PipelineImpl("main"); // Create elements pipeline.add(new FakeSource("source") { { incommingBufLength = 3; numBuffers = 10; delay = 100; } }); pipeline.add(new BaseElement("tee")); pipeline.add(new FakeSink("sink") { { verbose = true; } }); pipeline.add(new FakeSink("sink2") { { verbose = true; } }); // Link elements pipeline.link("source", "tee", "sink"); pipeline.link("tee >out2", "sink2"); // Run main loop pipeline.runMainLoop("source", STDOUT, false, false); } }