Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions OWNER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ivanov Mikhail
Empty file added logs/application.log
Empty file.
54 changes: 49 additions & 5 deletions src/main/java/arhangel/dim/container/BeanGraph.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
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;

/**
*
*/
public class BeanGraph {

private static Logger log = LoggerFactory.getLogger(BeanGraph.class);

// Граф представлен в виде списка связности для каждой вершины
private Map<BeanVertex, List<BeanVertex>> vertices = new HashMap<>();

Expand All @@ -16,35 +24,71 @@ public class BeanGraph {
* @param value - объект, привязанный к вершине
*/
public BeanVertex addVertex(Bean value) {
return null;
BeanVertex vertex = new BeanVertex(value);
vertices.put(vertex, new ArrayList<>());
return vertex;
}

/**
* Соединить вершины ребром
* @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");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Строго говоря, бросать ошибку конфигурации и писать сообщение, что не найдено ребро - нелогично. Имеет смысл пробросить эту ошибку как проверяемое исключение выше по стеку и там отреагировать на него

}
}

/**
* Проверяем, связаны ли вершины
*/
public boolean isConnected(BeanVertex v1, BeanVertex v2) {
return false;
return getLinked(v1).contains(v2);
}

/**
* Получить список вершин, с которыми связана vertex
*/
public List<BeanVertex> getLinked(BeanVertex vertex) {
return null;
return vertices.get(vertex);
}

/**
* Количество вершин в графе
*/
public int size() {
return 0;
return vertices.size();
}

void dfs(BeanVertex current) throws Exception {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А что еще может вылететь кроме CycleRefException?

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<Bean> sorted = new LinkedList<>();

public List<Bean> 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;
}

}
16 changes: 16 additions & 0 deletions src/main/java/arhangel/dim/container/BeanVertex.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
}
99 changes: 94 additions & 5 deletions src/main/java/arhangel/dim/container/BeanXmlReader.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
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";
Expand All @@ -14,8 +31,80 @@ public class BeanXmlReader {
private static final String ATTR_BEAN_ID = "id";
private static final String ATTR_BEAN_CLASS = "class";

public List<Bean> parseBeans(String pathToFile) {
return null;
List<Bean> beans = new ArrayList<>();

public List<Bean> parseBeans(String pathToFile) throws Exception {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Пусть бросает только InvalidConfiguartionExceptino

Document config = readXml(pathToFile);
Element root = config.getDocumentElement();
NodeList nodes = root.getChildNodes();

//проверка на уникальность id
Set<String> 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");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хорошая проверка!

}
uniqueId.add(beans.get(beans.size() - 1).getName());
}
}

return beans;
}

private void parseBean(Node bean) throws Exception {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Боюсь, я знаю откуда вы скопировали этот код

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ой, извините, этот код действительно есть в репозитории. Абсолютно нормально его использовать.

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<String, Property> 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<Bean> getBeans() {
return beans;
}

}
103 changes: 96 additions & 7 deletions src/main/java/arhangel/dim/container/Container.java
Original file line number Diff line number Diff line change
@@ -1,41 +1,85 @@
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<Bean> beans;

private Map<String, Object> objectsById = new HashMap<>();
private Map<String, Object> 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<String, BeanVertex> 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());
}
}

/**
* Вернуть объект по имени бина из конфига
* Например, Car car = (Car) container.getByName("carBean")
*/
public Object getByName(String name) {
return null;
return objectsById.get(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();
Expand All @@ -50,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);
}
}

}
Loading