diff --git a/OWNER.md b/OWNER.md new file mode 100644 index 0000000..fed4ac8 --- /dev/null +++ b/OWNER.md @@ -0,0 +1 @@ +Ivanov Mikhail diff --git a/logs/application.log b/logs/application.log new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/arhangel/dim/container/BeanGraph.java b/src/main/java/arhangel/dim/container/BeanGraph.java index 306c0e6..156eb39 100644 --- a/src/main/java/arhangel/dim/container/BeanGraph.java +++ b/src/main/java/arhangel/dim/container/BeanGraph.java @@ -1,6 +1,11 @@ package arhangel.dim.container; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -8,6 +13,9 @@ * */ public class BeanGraph { + + private static Logger log = LoggerFactory.getLogger(BeanGraph.class); + // Граф представлен в виде списка связности для каждой вершины private Map> vertices = new HashMap<>(); @@ -16,7 +24,9 @@ public class BeanGraph { * @param value - объект, привязанный к вершине */ public BeanVertex addVertex(Bean value) { - return null; + BeanVertex vertex = new BeanVertex(value); + vertices.put(vertex, new ArrayList<>()); + return vertex; } /** @@ -24,27 +34,61 @@ public BeanVertex addVertex(Bean value) { * @param from из какой вершины * @param to в какую вершину */ - public void addEdge(BeanVertex from ,BeanVertex to) { + public void addEdge(BeanVertex from , BeanVertex to) throws Exception { + if (from != null && to != null && vertices.containsKey(from)) { + vertices.get(from).add(to); + } else { + throw new InvalidConfigurationException("Error in addEdge"); + } } /** * Проверяем, связаны ли вершины */ public boolean isConnected(BeanVertex v1, BeanVertex v2) { - return false; + return getLinked(v1).contains(v2); } /** * Получить список вершин, с которыми связана vertex */ public List getLinked(BeanVertex vertex) { - return null; + return vertices.get(vertex); } /** * Количество вершин в графе */ public int size() { - return 0; + return vertices.size(); + } + + void dfs(BeanVertex current) throws Exception { + current.setState(BeanVertex.State.MARKED); + for (BeanVertex vertex : getLinked(current)) { + //log.info("ADD SORT LIST " + vertex.getBean().getName()); + if (vertex.getState() == BeanVertex.State.MARKED) { + throw new CycleReferenceException("Cycle in your ref dependency"); + } + if (vertex.getState() == BeanVertex.State.DEFAULT) { + dfs(vertex); + } + } + current.setState(BeanVertex.State.VISITED); + sorted.add(current.getBean()); } + + private List sorted = new LinkedList<>(); + + public List topSort() throws Exception { + for (BeanVertex vertex : vertices.keySet()) { + if (vertex.getState() == BeanVertex.State.DEFAULT) { + //log.info("ADD SORT LIST " + vertex.getBean().getName()); + dfs(vertex); + } + } + + return sorted; + } + } diff --git a/src/main/java/arhangel/dim/container/BeanVertex.java b/src/main/java/arhangel/dim/container/BeanVertex.java index 1fbd084..ff85639 100644 --- a/src/main/java/arhangel/dim/container/BeanVertex.java +++ b/src/main/java/arhangel/dim/container/BeanVertex.java @@ -4,7 +4,15 @@ * Вершина графа, которая содержит бин */ public class BeanVertex { + + public enum State { + DEFAULT, + MARKED, + VISITED + } + private Bean bean; + private State state = State.DEFAULT; public BeanVertex(Bean bean) { this.bean = bean; @@ -17,4 +25,12 @@ public Bean getBean() { public void setBean(Bean bean) { this.bean = bean; } + + public State getState() { + return state; + } + + void setState(State state) { + this.state = state; + } } diff --git a/src/main/java/arhangel/dim/container/BeanXmlReader.java b/src/main/java/arhangel/dim/container/BeanXmlReader.java new file mode 100644 index 0000000..54f161d --- /dev/null +++ b/src/main/java/arhangel/dim/container/BeanXmlReader.java @@ -0,0 +1,110 @@ +package arhangel.dim.container; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class BeanXmlReader { + + private static Logger log = LoggerFactory.getLogger(BeanXmlReader.class); + + private static final String TAG_BEAN = "bean"; + private static final String TAG_PROPERTY = "property"; + private static final String ATTR_NAME = "name"; + private static final String ATTR_VALUE = "val"; + private static final String ATTR_REF = "ref"; + private static final String ATTR_BEAN_ID = "id"; + private static final String ATTR_BEAN_CLASS = "class"; + + List beans = new ArrayList<>(); + + public List parseBeans(String pathToFile) throws Exception { + Document config = readXml(pathToFile); + Element root = config.getDocumentElement(); + NodeList nodes = root.getChildNodes(); + + //проверка на уникальность id + Set uniqueId = new HashSet<>(); + + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + if (TAG_BEAN.equals(node.getNodeName())) { + parseBean(node); + if (uniqueId.contains(beans.get(beans.size() - 1).getName())) { + throw new InvalidConfigurationException("Object name is not unique"); + } + uniqueId.add(beans.get(beans.size() - 1).getName()); + } + } + + return beans; + } + + private void parseBean(Node bean) throws Exception { + NamedNodeMap attr = bean.getAttributes(); + Node name = attr.getNamedItem(ATTR_BEAN_ID); + String nameVal = name.getNodeValue(); + String classVal = attr.getNamedItem(ATTR_BEAN_CLASS).getNodeValue(); + //log.info("BEAN: [name: {}, class: {}]", nameVal, classVal); + + // ищем все проперти внутри + NodeList list = bean.getChildNodes(); + Map properties = new HashMap<>(); + for (int i = 0; i < list.getLength(); i++) { + Node node = list.item(i); + if (TAG_PROPERTY.equals(node.getNodeName())) { + Property property = parseProperty(node); + //log.info("\tSET {}", property); + properties.put(property.getName(), property); + } + } + // + beans.add(new Bean(nameVal, classVal, properties)); + } + + private Property parseProperty(Node node) throws Exception { + NamedNodeMap map = node.getAttributes(); + String name = map.getNamedItem(ATTR_NAME).getNodeValue(); + Node val = map.getNamedItem(ATTR_VALUE); + if (val != null) { + // если значение примитивного типа + return new Property(name, val.getNodeValue(), ValueType.VAL); + } else { + // если значение ссылочного типа + val = map.getNamedItem(ATTR_REF); + if (val != null) { + return new Property(name, val.getNodeValue(), ValueType.REF); + } else { + throw new InvalidConfigurationException("Failed to parse property " + name); + } + } + } + + private Document readXml(String path) throws Exception { + File file = new File(path); + //log.info("Context configuration xml: " + file.getAbsolutePath()); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + return db.parse(file); + } + + public List getBeans() { + return beans; + } + +} diff --git a/src/main/java/arhangel/dim/container/Container.java b/src/main/java/arhangel/dim/container/Container.java index 6ca797f..def6cc6 100644 --- a/src/main/java/arhangel/dim/container/Container.java +++ b/src/main/java/arhangel/dim/container/Container.java @@ -1,21 +1,65 @@ package arhangel.dim.container; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Используйте ваш xml reader чтобы прочитать конфиг и получить список бинов */ public class Container { + + + private static Logger log = LoggerFactory.getLogger(Container.class); + private List beans; + private Map objectsById = new HashMap<>(); + private Map objectsByClass = new HashMap<>(); + /** * Если не получается считать конфиг, то бросьте исключение * @throws InvalidConfigurationException неверный конфиг */ public Container(String pathToConfig) throws InvalidConfigurationException { - // вызываем BeanXmlReader + try { + + BeanXmlReader beanXmlReader = new BeanXmlReader(); + beanXmlReader.parseBeans(pathToConfig); + this.beans = beanXmlReader.getBeans(); + + Map vertexByNamed = new HashMap<>(); + + BeanGraph beanGraph = new BeanGraph(); + for (Bean bean : beans) { + //log.info("ADD VERTEX " + bean.getName()); + vertexByNamed.put(bean.getName(), beanGraph.addVertex(bean)); + } + + for (Bean bean : beans) { + for (Property property : bean.getProperties().values()) { + if (property.getType() == ValueType.REF) { + beanGraph.addEdge(vertexByNamed.get(bean.getName()), vertexByNamed.get(property.getValue())); + } + } + } + + beans = beanGraph.topSort(); + + for (Bean bean : beans) { + //log.info("INSTANT:" + bean.getName()); + instantiateBean(bean); + } + } catch (Exception ex) { + throw new InvalidConfigurationException(ex.getMessage()); + } } /** @@ -23,7 +67,7 @@ public Container(String pathToConfig) throws InvalidConfigurationException { * Например, Car car = (Car) container.getByName("carBean") */ public Object getByName(String name) { - return null; + return objectsById.get(name); } /** @@ -31,12 +75,11 @@ public Object getByName(String name) { * Например, Car car = (Car) container.getByClass("arhangel.dim.container.Car") */ public Object getByClass(String className) { - return null; + return objectsByClass.get(className); } - private void instantiateBean(Bean bean) { + private void instantiateBean(Bean bean) throws Exception { - /* // Примерный ход работы String className = bean.getClassName(); @@ -51,15 +94,60 @@ private void instantiateBean(Bean bean) { Field field = clazz.getDeclaredField(name); // проверяем, если такого поля нет, то кидаем InvalidConfigurationException с описание ошибки + if (field == null) { + throw new InvalidConfigurationException("Failed to set field [" + name + "] for class " + clazz.getName()); + } + + Property prop = bean.getProperties().get(name); + // Делаем приватные поля доступными field.setAccessible(true); // Далее определяем тип поля и заполняем его // Если поле - примитив, то все просто // Если поле ссылка, то эта ссылка должа была быть инициализирована ранее + // храним тип данных + Type type = field.getType(); + + Method method = clazz.getDeclaredMethod("set" + Character.toUpperCase(name.charAt(0)) + + name.substring(1), field.getType()); + + switch (prop.getType()) { + case VAL: + method.invoke(ob, convert(type.getTypeName(), prop.getValue())); + break; + case REF: + String refName = prop.getValue(); + if (objectsById.containsKey(refName)) { + method.invoke(ob, objectsById.get(refName)); + } else { + throw new InvalidConfigurationException("Failed to instantiate bean. Field " + name); + } + break; + default: + } + + } + + objectsById.put(bean.getName(), ob); + objectsByClass.put(bean.getClassName(), ob); + } - */ - + // конвертирует строку в объект соответствующего + private Object convert(String typeName, String data) throws Exception { + switch (typeName) { + case "int": + case "Integer": + return Integer.valueOf(data); + case "double": + case "Double": + return Double.valueOf(data); + case "boolean": + case "Boolean": + return Boolean.valueOf(data); + default: + throw new InvalidConfigurationException("type name = " + typeName); + } } } diff --git a/src/main/java/arhangel/dim/container/Context.java b/src/main/java/arhangel/dim/container/Context.java new file mode 100644 index 0000000..a37885b --- /dev/null +++ b/src/main/java/arhangel/dim/container/Context.java @@ -0,0 +1,189 @@ +package arhangel.dim.container; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class Context { + + private static Logger log = LoggerFactory.getLogger(Context.class); + + private static final String TAG_BEAN = "bean"; + private static final String TAG_PROPERTY = "property"; + + private static final String ATTR_NAME = "name"; + private static final String ATTR_VALUE = "val"; + private static final String ATTR_REF = "ref"; + + private static final String ATTR_BEAN_ID = "id"; + private static final String ATTR_BEAN_CLASS = "class"; + + + List beans = new ArrayList<>(); + + Map objectsById = new HashMap<>(); + Map objectsByClass = new HashMap<>(); + + public static void main(String[] args) throws Exception { + + // Dynamic config + Context context = new Context("config.xml"); + } + + public Context(String xmlPath) throws Exception { + Document config = readXml(xmlPath); + Element root = config.getDocumentElement(); + NodeList nodes = root.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + if (TAG_BEAN.equals(node.getNodeName())) { + parseBean(node); + } + } + + // прочитали xml и знаем все о конфигурации + instantiateBeans(); + } + + public Object getBeanByName(String beanName) { + return objectsById.get(beanName); + } + + public Object getBeanByClass(String className) { + return objectsByClass.get(className); + } + + public void instantiateBeans() throws Exception { + log.info("beans: {}", beans); + for (Bean bean : beans) { + // по имени класса его можно инстанцировать + // обязательно должен быть дефолтный конструктор + String className = bean.getClassName(); + Class clazz = Class.forName(className); + // ищем дефолтный конструктор + Object ob = clazz.newInstance(); + + + for (String name : bean.getProperties().keySet()) { + // ищем поле с таким именен внутри класса + // учитывая приватные + Field field = clazz.getDeclaredField(name); + if (field == null) { + throw new InvalidConfigurationException("Failed to set field [" + name + "] for class " + clazz.getName()); + } + Property prop = bean.getProperties().get(name); + // Чтобы изменять приватные поля + field.setAccessible(true); + + // храним тип данных + Type type = field.getType(); + + switch (prop.getType()) { + case VAL: + field.set(ob, convert(type.getTypeName(), prop.getValue())); + break; + case REF: + String refName = prop.getValue(); + if (objectsById.containsKey(refName)) { + field.set(ob, objectsById.get(refName)); + } else { + throw new InvalidConfigurationException("Failed to instantiate bean. Field " + name); + } + break; + default: + } + } + + + log.info("Bean instantiated: {}", bean); + objectsById.put(bean.getName(), ob); + objectsByClass.put(bean.getClassName(), ob); + + } + } + + // конвертирует строку в объект соответствующего + private Object convert(String typeName, String data) throws Exception { + switch (typeName) { + case "int": + case "Integer": + return Integer.valueOf(data); + case "double": + case "Double": + return Double.valueOf(data); + case "boolean": + case "Boolean": + return Boolean.valueOf(data); + default: + throw new InvalidConfigurationException("type name = " + typeName); + } + } + + private void parseBean(Node bean) throws Exception { + NamedNodeMap attr = bean.getAttributes(); + Node name = attr.getNamedItem(ATTR_BEAN_ID); + String nameVal = name.getNodeValue(); + String classVal = attr.getNamedItem(ATTR_BEAN_CLASS).getNodeValue(); + log.info("BEAN: [name: {}, class: {}]", nameVal, classVal); + + // ищем все проперти внутри + NodeList list = bean.getChildNodes(); + Map properties = new HashMap<>(); + for (int i = 0; i < list.getLength(); i++) { + Node node = list.item(i); + if (TAG_PROPERTY.equals(node.getNodeName())) { + Property property = parseProperty(node); + log.info("\tSET {}", property); + properties.put(property.getName(), property); + } + } + // + beans.add(new Bean(nameVal, classVal, properties)); + } + + private Property parseProperty(Node node) throws Exception { + NamedNodeMap map = node.getAttributes(); + String name = map.getNamedItem(ATTR_NAME).getNodeValue(); + Node val = map.getNamedItem(ATTR_VALUE); + if (val != null) { + // если значение примитивного типа + return new Property(name, val.getNodeValue(), ValueType.VAL); + } else { + // если значение ссылочного типа + val = map.getNamedItem(ATTR_REF); + if (val != null) { + return new Property(name, val.getNodeValue(), ValueType.REF); + } else { + throw new InvalidConfigurationException("Failed to parse property " + name); + } + } + } + + private Document readXml(String path) throws Exception { + File file = new File(path); + log.info("Context configuration xml: " + file.getAbsolutePath()); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + return db.parse(file); + } + + public List getBeans() { + return beans; + } +} + diff --git a/src/main/java/arhangel/dim/container/InvalidConfigurationException.java b/src/main/java/arhangel/dim/container/InvalidConfigurationException.java new file mode 100644 index 0000000..c34fec9 --- /dev/null +++ b/src/main/java/arhangel/dim/container/InvalidConfigurationException.java @@ -0,0 +1,7 @@ +package arhangel.dim.container; + +public class InvalidConfigurationException extends Exception { + public InvalidConfigurationException(String msg) { + super(msg); + } +} diff --git a/src/main/java/arhangel/dim/container/TestMain.java b/src/main/java/arhangel/dim/container/TestMain.java new file mode 100644 index 0000000..03e2954 --- /dev/null +++ b/src/main/java/arhangel/dim/container/TestMain.java @@ -0,0 +1,15 @@ +package arhangel.dim.container; + +import arhangel.dim.container.beans.Car; + +public class TestMain { + public static void main(String[] args) { + try { + Container container = new Container("config.xml"); + Car car = (Car) container.getByName("carBean"); + System.out.println(car.getEngine().getPower()); + } catch (InvalidConfigurationException ex) { + System.out.println(ex.getMessage()); + } + } +} diff --git a/src/main/java/arhangel/dim/container/dag/Graph.java b/src/main/java/arhangel/dim/container/dag/Graph.java new file mode 100644 index 0000000..8527dec --- /dev/null +++ b/src/main/java/arhangel/dim/container/dag/Graph.java @@ -0,0 +1,89 @@ +package arhangel.dim.container.dag; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * + */ +public class Graph { + private Map, List>> vertices = new HashMap<>(); + + /** + * Добавить вершину в граф + * @param value - объект, привязанный к вершине + */ + public Vertex addVertex(V value) { + Vertex vertex = new Vertex<>(value); + vertices.put(vertex, new ArrayList<>()); + return vertex; + } + + /** + * Соединить вершины ребром + * @param from из какой вершины + * @param to в какую вершину + * @param isDirected - если true, то связь односторонняя, иначе - двухсторонняя + */ + public void addEdge(Vertex from, Vertex to, boolean isDirected) { + if (vertices.get(from) != null) { + vertices.get(from).add(to); + if (!isDirected && vertices.get(to) != null) { + vertices.get(to).add(from); + } + } + } + + /** + * Проверяем, связаны ли вершины + */ + public boolean isConnected(Vertex v1, Vertex v2) { + return getLinked(v1).contains(v2); + } + + /** + * Получить список вершин, с которыми связана vertex + */ + public List> getLinked(Vertex vertex) { + return vertices.get(vertex); + } + + /** + * Количество вершин в графе + */ + public int size() { + return vertices.size(); + } + + private LinkedList> sorted = new LinkedList<>(); + + public void dfs() { + vertices.keySet().stream().filter(vertex -> vertex.getState() != Vertex.State.VISITED).forEach(this::dfs); + } + + public void dfs(Vertex current) { + + current.setState(Vertex.State.MARKED); + for (Vertex vertex : getLinked(current)) { + if (vertex.getState() == Vertex.State.MARKED) { + System.out.println("Cycle: " + current + "->" + vertex); + return; + } + if (vertex.getState() != Vertex.State.VISITED) { + dfs(vertex); + } + } + current.setState(Vertex.State.VISITED); + sorted.push(current); + } + + public List> toposort() { + dfs(); + System.out.println(sorted); + return sorted; + } + +} diff --git a/src/main/java/arhangel/dim/container/dag/Main.java b/src/main/java/arhangel/dim/container/dag/Main.java new file mode 100644 index 0000000..eeeba79 --- /dev/null +++ b/src/main/java/arhangel/dim/container/dag/Main.java @@ -0,0 +1,35 @@ +package arhangel.dim.container.dag; + +import java.util.List; + +/** + * + */ +public class Main { + + public static void main(String[] args) throws Exception { + Graph graph = new Graph<>(); + Vertex v1 = graph.addVertex(1); + Vertex v2 = graph.addVertex(2); + Vertex v3 = graph.addVertex(3); + Vertex v4 = graph.addVertex(4); + Vertex v5 = graph.addVertex(5); + + Vertex v6 = graph.addVertex(6); + Vertex v7 = graph.addVertex(7); + + + graph.addEdge(v1, v2, true); + graph.addEdge(v1, v3, true); + graph.addEdge(v2, v3, true); + graph.addEdge(v2, v4, true); + graph.addEdge(v3, v5, true); + graph.addEdge(v4, v5, true); + + graph.addEdge(v6, v7, true); + + List> sorted = graph.toposort(); + + } + +} diff --git a/src/main/java/arhangel/dim/container/dag/Vertex.java b/src/main/java/arhangel/dim/container/dag/Vertex.java new file mode 100644 index 0000000..2c5e6cd --- /dev/null +++ b/src/main/java/arhangel/dim/container/dag/Vertex.java @@ -0,0 +1,44 @@ +package arhangel.dim.container.dag; + +/** + * Представление вершины графа + */ +public class Vertex { + + public enum State { + DEFAULT, + MARKED, + VISITED + } + + private V value; + private State state = State.DEFAULT; + + public Vertex(V value) { + this.value = value; + } + + public V getValue() { + return value; + } + + public void setValue(V value) { + this.value = value; + } + + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + @Override + public String toString() { + return "Vertex{" + + "value=" + value + + ", state=" + state + + '}'; + } +} diff --git a/src/main/java/arhangel/dim/lections/collections/BoundGenerics.java b/src/main/java/arhangel/dim/lections/collections/BoundGenerics.java new file mode 100644 index 0000000..2f982b0 --- /dev/null +++ b/src/main/java/arhangel/dim/lections/collections/BoundGenerics.java @@ -0,0 +1,102 @@ +package arhangel.dim.lections.collections; + +import java.util.ArrayList; +import java.util.List; + +/** + * + */ +public class BoundGenerics { + + static class Animal { + void feed() { + System.out.println("Animal feed()"); + } + } + + static class Pet extends Animal { + void call() { + System.out.println("Pet call()"); + } + } + + static class Cat extends Pet { + void mew() { + System.out.println("Cat mew()"); + } + } + + static class Dog extends Pet { + void bark() { + System.out.println("Dog bark()"); + } + } + + + static void fillPets(List pets) { + pets.add(new Dog()); + pets.add(new Cat()); + } + + static void copy(List dest, List src) { + + for (T e : src) { + dest.add(e); + } + + // src.stream().forEach(dest::add); + } + + public static void main(String[] args) { + List cats = new ArrayList<>(); + cats.add(new Cat()); + cats.add(new Cat()); + + + List pets = cats; + + + List dogs = new ArrayList<>(); + dogs.add(new Dog()); + dogs.add(new Dog()); + + callPets(cats); // Incompatible types (compile time) +// callPets(dogs); + + + List animals = new ArrayList<>(); + // Incompatible types +// fillPets(animals); + + } + +// static void callPets(List list) { +// // Позовем домашних питомцев +// for (Pet pet : list) { +// pet.call(); +// } +// } + + // Коллекция pets - поставщик данных (producer) +// static void callPets(List pets) { +// for (T item : pets) { +// item.call(); +// } +// +// //pets.stream().forEach(Pet::call); +// } + + + static void callPets(List pets) { + pets.stream().forEach(Pet::call); + } + + + // Коллекция pets - потребитель данных (consumer) +// static void fillPets(List pets) { +// pets.add(new Dog()); +// pets.add(new Cat()); +// } + + +} diff --git a/src/main/java/arhangel/dim/lections/collections/Box.java b/src/main/java/arhangel/dim/lections/collections/Box.java new file mode 100644 index 0000000..468d37c --- /dev/null +++ b/src/main/java/arhangel/dim/lections/collections/Box.java @@ -0,0 +1,28 @@ +package arhangel.dim.lections.collections; + +/** + * + */ +public class Box { + private T item; + + public Box(T item) { + this.item = item; + } + + public T getItem() { + return item; + } + + public void setItem(T item) { + this.item = item; + // Object - nothing more + } + + @Override + public String toString() { + return "Box{" + + "item=" + item + + '}'; + } +} diff --git a/src/main/java/arhangel/dim/lections/collections/Demo.java b/src/main/java/arhangel/dim/lections/collections/Demo.java new file mode 100644 index 0000000..d862eae --- /dev/null +++ b/src/main/java/arhangel/dim/lections/collections/Demo.java @@ -0,0 +1,60 @@ +package arhangel.dim.lections.collections; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; + +/** + * 7 + */ +public class Demo { + + // SAM - Single Abstract Method + static class MyPredicate implements Predicate { + @Override + public boolean test(Integer integer) { + return integer % 2 != 0; + } + } + + static class MyConsumer implements Consumer { + @Override + public void accept(T item) { + System.out.println("# " + item); + } + } + + public static void main(String[] args) { + List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7); + + // Filter with predicate + final int skip = 3; + // skip = 4; + numbers.stream().filter(new MyPredicate()).forEach(new MyConsumer<>()); + + numbers + .stream() + .filter((val) -> val != skip) + .forEach(System.out::println); + + + // Map + + int sum = numbers.stream() + .map((val) -> val * val) + .reduce(0, (val1, val2) -> val1 + val2); + System.out.println("sum: " + sum); + + + List myList = + Arrays.asList("a1", "a2", "b1", "c2", "c1"); + + myList.stream() + .filter(s -> s.startsWith("c")) + .map(String::toUpperCase) + .sorted() + .forEach(System.out::println); + + } +} diff --git a/src/main/java/arhangel/dim/lections/collections/FunctionalProgramming.java b/src/main/java/arhangel/dim/lections/collections/FunctionalProgramming.java new file mode 100644 index 0000000..53a84da --- /dev/null +++ b/src/main/java/arhangel/dim/lections/collections/FunctionalProgramming.java @@ -0,0 +1,75 @@ +package arhangel.dim.lections.collections; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + + +public class FunctionalProgramming { + + // Принимает одно значение, возвращает одно значение + interface Function { + R apply(T val); + } + + // Принимает элемент и проверяет его на условие + interface Predicate { + boolean test(T val); + } + + // Принимает 2 аргумента, проводит опреацию и возвращает результат + interface BiFunction { + R apply(U val1, V val2); + } + + // 2 аргумента одного типа + interface BiOperator extends BiFunction { + T apply(T val1, T val2); + } + + static class Square implements Function { + public Integer apply(Integer val) { + return val * val; + } + } + + /* + Применить к каждому элементу коллекции заданную операцию + */ + static List map(Collection collection, Function functor) { + return Collections.emptyList(); + } + + /* + Проверить элементы коллекции на заданное условие. + Вернуть коллекцию элементов, прошедших фильтр + */ + static List filter(List list, Predicate predicate) { + return Collections.emptyList(); + } + + /* + Последовательно применить операцию ко всем элементам коллекции + Вернуть одно значение + */ + static T reduce(List list, T init, BiOperator op) { + return null; + } + + public static void main(String[] args) { + List numbers = Arrays.asList(1, 2, 3, 4); + + // Returned 1, 4, 9, 16 + System.out.println("map [^2]: " + numbers + " -> " + map(numbers, new Square())); + + System.out.println("filter [%3]: " + numbers + " -> " + filter(numbers, new Predicate() { + @Override + public boolean test(Integer val) { + return val % 3 != 0; + } + })); + + } +} diff --git a/src/main/java/arhangel/dim/lections/collections/Stack.java b/src/main/java/arhangel/dim/lections/collections/Stack.java new file mode 100644 index 0000000..a04f604 --- /dev/null +++ b/src/main/java/arhangel/dim/lections/collections/Stack.java @@ -0,0 +1,26 @@ +package arhangel.dim.lections.collections; + +import java.util.Collection; + +/** + * + */ +public interface Stack extends Iterable { + + void push(E element) throws StackException; + + E pop() throws StackException; + + E peek(); + + int getSize(); + + boolean isEmpty(); + + boolean isFull(); + + // A little bit wrong =( Could you fix it? + void pushAll(Collection src) throws StackException; + + void popAll(Collection dst) throws StackException; +} diff --git a/src/main/java/arhangel/dim/lections/collections/StackException.java b/src/main/java/arhangel/dim/lections/collections/StackException.java new file mode 100644 index 0000000..afdd057 --- /dev/null +++ b/src/main/java/arhangel/dim/lections/collections/StackException.java @@ -0,0 +1,10 @@ +package arhangel.dim.lections.collections; + +/** + * + */ +public class StackException extends Exception { + public StackException(String msg) { + super(msg); + } +} diff --git a/src/main/java/arhangel/dim/lections/exception/ExceptionDemo.java b/src/main/java/arhangel/dim/lections/exception/ExceptionDemo.java index 6b635fa..35295fa 100644 --- a/src/main/java/arhangel/dim/lections/exception/ExceptionDemo.java +++ b/src/main/java/arhangel/dim/lections/exception/ExceptionDemo.java @@ -2,54 +2,85 @@ import java.io.IOException; +import java.sql.Connection; /** * */ public class ExceptionDemo { - public void convertString(String str) { + public static void convertString(String str) { if (str == null) { throw new IllegalArgumentException("Arg str must be non-null"); } } - public static void main(String[] args) { - //new ExceptionDemo().convertString(null); + public static void m1() throws Exception { + throw new ArithmeticException("Amth"); + } - //System.out.println("str == null -> " + getSize(null)); - //System.out.println("str != null -> " + getSize("test")); + public static void m2() throws Exception { + try { + m1(); + } catch (Exception e) { + throw new Exception(e); + } + } + + public static void main(String[] args) throws Exception { + + try { + m2(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + + //convertString(null); + +// System.out.println("str == null -> " + getSize(null)); +// System.out.println("str != null -> " + getSize("test")); //exceptionLost(); } public static int getSize(String str) { + Connection conn = null; try { - return str.toString().length(); + return str.length(); } catch (Exception e) { System.out.println("in catch block"); + //System.exit(0); return -1; } finally { + if (conn != null) { + try { + conn.close(); + } catch (Exception e) { + // do nothing + } + } return 0; } } - public static void exceptionLost() { + public static void exceptionLost() throws Exception { try { try { throw new Exception("a"); + } catch (Exception e) { + System.out.println("In catch"); + throw e; // a } finally { - if (true) { - throw new IOException("b"); - } + System.out.println("In finally block"); + +// if (true) { +// throw new IOException("b"); +// } System.err.println("c"); } } catch (IOException e) { System.err.println(e.getMessage()); - } catch (Exception e) { - System.err.println("d"); - System.err.println(e.getMessage()); } } } diff --git a/src/main/java/arhangel/dim/lections/threads/DeadLock.java b/src/main/java/arhangel/dim/lections/threads/DeadLock.java new file mode 100644 index 0000000..c47e347 --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/DeadLock.java @@ -0,0 +1,78 @@ +package arhangel.dim.lections.threads; + + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + */ +public class DeadLock { + static Logger logger = LoggerFactory.getLogger(DeadLock.class); + Lock lock = new ReentrantLock(); + + public static void main(String[] args) { + + final Account a1 = new Account(1); + a1.sum = 100; + final Account a2 = new Account(2); + a2.sum = 300; + + Thread t1 = new Thread(() -> { + Account.transact(a1, a2, 10); + }, ""); + + Thread t2 = new Thread(() -> { + Account.transact(a2, a1, 20); + }, ""); + + t1.start(); + t2.start(); + } + + static class Account { + int sum; + int id; + + public Account(int id) { + this.id = id; + } + + static void transact(final Account from, final Account to, int amount) { + Account lock1; + Account lock2; + +// lock1 = from; +// lock2 = to; + + if (from.id < to.id) { + lock1 = from; + lock2 = to; + } else { + lock1 = to; + lock2 = from; + } + + synchronized (lock1) { + logger.info("Lock1({}) was acquired by thread {}. Waiting for lock2({}), " + + "lock1.id, Thread.currentThread().getName(), lock2.id"); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + synchronized (lock2) { + logger.info("Lock2({}) was acquired by thread {}. Waiting for lock1({}), " + + "lock2.id, Thread.currentThread().getName(), lock1.id"); + from.sum -= amount; + to.sum += amount; + } + } + } + } + +} diff --git a/src/main/java/arhangel/dim/lections/threads/Monitor.java b/src/main/java/arhangel/dim/lections/threads/Monitor.java new file mode 100644 index 0000000..8c985c4 --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/Monitor.java @@ -0,0 +1,45 @@ +package arhangel.dim.lections.threads; + +/** + * + */ +public class Monitor { + + private final Object lockObject = new Object(); + + private final Object anotherLockObject = new Object(); + + int counter = 0; + + public void doChange() { + synchronized (lockObject) { + counter++; + } + } + + public void doAnotherChange() { + synchronized (anotherLockObject) { + counter++; + } + } + + public static void main(String[] args) { + Monitor monitor = new Monitor(); + + new Thread(() -> { + for (int i = 0; i < 100_000; i++) { + monitor.doChange(); + } + }).start(); + + new Thread(() -> { + for (int i = 0; i < 100_000; i++) { + monitor.doAnotherChange(); + } + }).start(); + + System.out.println(monitor.counter); + } + + +} diff --git a/src/main/java/arhangel/dim/lections/threads/MyThread.java b/src/main/java/arhangel/dim/lections/threads/MyThread.java new file mode 100644 index 0000000..c8db1e4 --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/MyThread.java @@ -0,0 +1,26 @@ +package arhangel.dim.lections.threads; + +import java.util.concurrent.TimeUnit; + +class MyThread extends Thread { + + private String name; + + public MyThread(String name) { + this.name = name; + } + + @Override + public void run() { + System.out.println("Thread " + name + ": started"); + try { + for (int i = 0; i < 5; i++) { + System.out.println("MyThread " + name + " : " + i); + TimeUnit.SECONDS.sleep(1); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("MyThread: finished"); + } +} diff --git a/src/main/java/arhangel/dim/lections/threads/Pool.java b/src/main/java/arhangel/dim/lections/threads/Pool.java new file mode 100644 index 0000000..325f1c4 --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/Pool.java @@ -0,0 +1,63 @@ +package arhangel.dim.lections.threads; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * + */ +public class Pool { + + public static void main(String[] args) throws Exception { + + int cores = Runtime.getRuntime().availableProcessors(); + System.out.println("Processors available: " + cores); + ExecutorService service = Executors.newFixedThreadPool(2); + + List futures = new ArrayList<>(); + + for (int i = 0; i < 4; i++) { +// Future future = service.submit(new MyThread("t#" + i)); + Future future = service.submit(new Task(i * 2)); + futures.add(future); + } + + System.out.println("1) ========================"); + + for (Future f : futures) { + System.out.println("result: " + f.get()); + } + + System.out.println("2) ========================"); + } + + static class Task implements Callable { + + int num; + + public Task(int num) { + this.num = num; + } + + @Override + public Integer call() throws Exception { + int acc = 0; + try { + for (int i = 0; i < num; i++) { + TimeUnit.SECONDS.sleep(1); + acc += i; + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + return acc; + } + } + +} diff --git a/src/main/java/arhangel/dim/lections/threads/SimpleThread.java b/src/main/java/arhangel/dim/lections/threads/SimpleThread.java new file mode 100644 index 0000000..141a3d5 --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/SimpleThread.java @@ -0,0 +1,61 @@ +package arhangel.dim.lections.threads; + +import java.util.concurrent.TimeUnit; + +/** + * + */ +public class SimpleThread { + + + + public static void main(String[] args) throws Exception { + inParallel(); +// start(); +// join(); + } + + static void inParallel() throws Exception { + Thread t1 = new MyThread("inParallel"); + + // Запуск кода в новом треде + System.out.println("Starting thread"); + t1.start(); + + for (int i = 0; i < 5; i++) { + System.out.println("Main:" + i); + TimeUnit.SECONDS.sleep(2); + } + System.out.println("Main thread finished"); + } + + + static void start() { + Thread t1 = new MyThread("simpleThread"); + + // Запуск кода в новом треде + System.out.println("Starting thread"); + t1.start(); + + // А здесь? +// System.out.println("Running"); +// t1.run(); + + System.out.println("Error:"); + // Нельзя запустить поток еще раз + // Почему? + //t1.start(); + } + + static void join() throws Exception { + Thread thread = new MyThread("joinThread"); + System.out.println("Starting thread..."); + thread.start(); + System.out.println("Joining"); + //t.join(); + System.out.println("Joined"); + + + } + +} diff --git a/src/main/java/arhangel/dim/lections/threads/StopThread.java b/src/main/java/arhangel/dim/lections/threads/StopThread.java new file mode 100644 index 0000000..0bc5808 --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/StopThread.java @@ -0,0 +1,93 @@ +package arhangel.dim.lections.threads; + +import java.util.Scanner; +import java.util.concurrent.TimeUnit; + +/** + * + */ +public class StopThread { + + static class FlagThread extends Thread { + private volatile boolean pleaseStop; + + @Override + public void run() { + while (!pleaseStop) { + try { + System.out.println("Thread::sleep()"); + TimeUnit.SECONDS.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + public void stopThread() { + System.out.println("Stopping..."); + pleaseStop = true; + } + } + + static class InterThread extends Thread { + @Override + public void run() { + while (!Thread.currentThread().isInterrupted()) { + try { + System.out.println("Thread::sleep()"); + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + } + } + + static class DummyThread extends Thread { + @Override + public void run() { + while (true) { + System.out.println("q"); +// try { +// System.out.println("Thread::sleep()"); +// TimeUnit.SECONDS.sleep(1); +// } catch (InterruptedException e) { +// } + } + } + } + + public static void flagThread() { + FlagThread flagThread = new FlagThread(); + flagThread.start(); + + Scanner scanner = new Scanner(System.in); + scanner.next(); + flagThread.stopThread(); + } + + public static void interruptThread() { + Thread thread = new InterThread(); + thread.start(); + + Scanner scanner = new Scanner(System.in); + scanner.next(); + thread.interrupt(); + } + + public static void dummyThread() { + Thread thread = new DummyThread(); + thread.start(); + + Scanner scanner = new Scanner(System.in); + scanner.next(); + thread.interrupt(); + } + + public static void main(String[] args) throws Exception { + //flagThread(); + //interruptThread(); + dummyThread(); + } +} diff --git a/src/main/java/arhangel/dim/lections/threads/counting/AtomicCounter.java b/src/main/java/arhangel/dim/lections/threads/counting/AtomicCounter.java new file mode 100644 index 0000000..fdac6db --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/counting/AtomicCounter.java @@ -0,0 +1,16 @@ +package arhangel.dim.lections.threads.counting; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * + */ +public class AtomicCounter implements Counter { + + private AtomicLong val = new AtomicLong(0); + + @Override + public long inc() { + return val.getAndIncrement(); + } +} diff --git a/src/main/java/arhangel/dim/lections/threads/counting/Counter.java b/src/main/java/arhangel/dim/lections/threads/counting/Counter.java new file mode 100644 index 0000000..95b720b --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/counting/Counter.java @@ -0,0 +1,9 @@ +package arhangel.dim.lections.threads.counting; + +/** + * + */ +public interface Counter { + + long inc(); +} diff --git a/src/main/java/arhangel/dim/lections/threads/counting/CounterTest.java b/src/main/java/arhangel/dim/lections/threads/counting/CounterTest.java new file mode 100644 index 0000000..089ba4b --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/counting/CounterTest.java @@ -0,0 +1,87 @@ +package arhangel.dim.lections.threads.counting; + +/** + * + */ +public class CounterTest { + + static class Sequencer extends Thread { + private Counter counter; + + public Sequencer(Counter counter) { + this.counter = counter; + } + + @Override + public void run() { + for (int i = 0; i < 100_000; i++) { + counter.inc(); + } + } + } + + static class UnsafeSequencer extends Thread { + private LockCounter counter; + + public UnsafeSequencer(LockCounter counter) { + this.counter = counter; + } + + @Override + public void run() { + for (int i = 0; i < 100_000; i++) { + counter.incUnsafe(); + } + } + } + + public static void main(String[] args) throws Exception { + //testCounter(); + testSafeUnsafe(); + } + + public static void testCounter() throws Exception { + final int threadNum = 2; + //Counter counter = new SimpleCounter(); + Counter counter = new AtomicCounter(); + //LockCounter counter = new LockCounter(); + Thread[] threads = new Thread[threadNum]; + for (int i = 0; i < threadNum; i++) { + Thread thread = new Sequencer(counter); + threads[i] = thread; + thread.start(); + } + + for (Thread t : threads) { + t.join(); + } + + System.out.printf("Threads: %d\nCounter: %d", threadNum, counter.inc()); + } + + public static void testSafeUnsafe() throws Exception { + + final int threadNum = 2; + //Counter counter = new SimpleCounter(); + //Counter counter = new AtomicCounter(); + LockCounter counter = new LockCounter(); + Thread[] threads = new Thread[threadNum]; + for (int i = 0; i < threadNum; i++) { + Thread thread = new Sequencer(counter); + threads[i] = thread; + thread.start(); + } + Thread unsafe = new UnsafeSequencer(counter); + unsafe.start(); + + for (Thread t : threads) { + t.join(); + } + + unsafe.join(); + + System.out.printf("Threads: %d\nCounter: %d", threadNum, counter.inc()); + } + + +} diff --git a/src/main/java/arhangel/dim/lections/threads/counting/LockCounter.java b/src/main/java/arhangel/dim/lections/threads/counting/LockCounter.java new file mode 100644 index 0000000..608d5ec --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/counting/LockCounter.java @@ -0,0 +1,19 @@ +package arhangel.dim.lections.threads.counting; + +/** + * реализация счетчика через лок + */ +public class LockCounter implements Counter { + + private long counter; + + public synchronized long inc() { + return counter++; + } + + public long incUnsafe() { + return counter++; + } + + +} diff --git a/src/main/java/arhangel/dim/lections/threads/counting/SimpleCounter.java b/src/main/java/arhangel/dim/lections/threads/counting/SimpleCounter.java new file mode 100644 index 0000000..03c221b --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/counting/SimpleCounter.java @@ -0,0 +1,13 @@ +package arhangel.dim.lections.threads.counting; + +/** + * + */ +public class SimpleCounter implements Counter { + private long val; + + public long inc() { + return val++; + } + +} diff --git a/src/main/java/arhangel/dim/lections/threads/queueu/BlockingQueue.java b/src/main/java/arhangel/dim/lections/threads/queueu/BlockingQueue.java new file mode 100644 index 0000000..27eb325 --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/queueu/BlockingQueue.java @@ -0,0 +1,19 @@ +package arhangel.dim.lections.threads.queueu; + +/** + * + */ +public interface BlockingQueue { + + /** + * + * @param e the element to add + */ + void put(E elem) throws InterruptedException; + + /** + * + * @return the head element + */ + E take() throws InterruptedException; +} diff --git a/src/main/java/arhangel/dim/lections/threads/queueu/ListBlockingQueue.java b/src/main/java/arhangel/dim/lections/threads/queueu/ListBlockingQueue.java new file mode 100644 index 0000000..6b8b083 --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/queueu/ListBlockingQueue.java @@ -0,0 +1,50 @@ +package arhangel.dim.lections.threads.queueu; + +import java.util.LinkedList; + +/** + * + */ +public class ListBlockingQueue implements BlockingQueue { + + public static final int DEFAULT_CAPACITY = 10; + + private int capacity; + private LinkedList list = new LinkedList<>(); + + public ListBlockingQueue() { + capacity = DEFAULT_CAPACITY; + } + + public ListBlockingQueue(int capacity) { + this.capacity = capacity; + } + + @Override + public void put(E elem) throws InterruptedException { + + /* + Внутри критической секции пытаемся поместить элемент в очередь + Если очередь полная isFull==true то блокируемся на wait() пока условие не будет выполнено + */ + } + + @Override + public E take() throws InterruptedException { + + /* + Внутри критической секции пытаемся достать элемент из очереди + Если очередь пустая isEmpty == true то блокируемся на wait() пока условие не будет выполнено + */ + return null; + } + + private boolean isFull() { + return list.size() == capacity; + } + + private boolean isEmpty() { + return list.size() == 0; + } + +} diff --git a/src/main/java/arhangel/dim/lections/threads/queueu/ProducerConsumer.java b/src/main/java/arhangel/dim/lections/threads/queueu/ProducerConsumer.java new file mode 100644 index 0000000..1b60c9c --- /dev/null +++ b/src/main/java/arhangel/dim/lections/threads/queueu/ProducerConsumer.java @@ -0,0 +1,73 @@ +package arhangel.dim.lections.threads.queueu; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ProducerConsumer { + + static Logger log = LoggerFactory.getLogger(ProducerConsumer.class); + + static boolean isReady = false; + + static class Producer extends Thread { + private final Object lock; + + public Producer(Object lock) { + this.lock = lock; + } + + @Override + public void run() { + log.info("[PRODUCER] Preparing data..."); + try { + sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + log.info("[PRODUCER] Data prepared. Notify All!"); + + isReady = true; + synchronized (lock) { + lock.notifyAll(); + } + } + + } + + static class Consumer extends Thread { + private final Object lock; + + public Consumer(Object lock) { + this.lock = lock; + } + + @Override + public void run() { + + synchronized (lock) { + log.info("[CONSUMER] Waiting for data..."); + + // Если данные еще не готовы + while (!isReady) { + try { + // ждем + lock.wait(); + // как только пробудились, заново проверяем состояние данных + // если они не готовы (или кто-то уже их поменял), то снова ждем + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + log.info("[CONSUMER] Data received"); + } + } + + } + + public static void main(String[] args) { + Object lock = new Object(); + new Consumer(lock).start(); + new Producer(lock).start(); + } +} diff --git a/src/test/java/arhangel/dim/container/BeanGraphTest.java b/src/test/java/arhangel/dim/container/BeanGraphTest.java index 2d762dd..5efc9d0 100644 --- a/src/test/java/arhangel/dim/container/BeanGraphTest.java +++ b/src/test/java/arhangel/dim/container/BeanGraphTest.java @@ -20,7 +20,7 @@ public class BeanGraphTest { private List vertices; @Before - public void initTest() { + public void initTest() throws Exception { graph = new BeanGraph(); BeanVertex v0 = graph.addVertex(new Bean("0", null, null)); BeanVertex v1 = graph.addVertex(new Bean("1", null, null));