diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/Main.java b/src/ru/fizteh/fivt/students/torunova/servlet/Main.java new file mode 100644 index 000000000..e02db6e10 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/Main.java @@ -0,0 +1,50 @@ +package ru.fizteh.fivt.students.torunova.servlet; + +import ru.fizteh.fivt.students.torunova.servlet.database.actions.Action; +import ru.fizteh.fivt.students.torunova.servlet.database.actions.Start; +import ru.fizteh.fivt.students.torunova.servlet.database.actions.Stop; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbNameException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectFileException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.TableNotCreatedException; +import ru.fizteh.fivt.students.torunova.servlet.interpreter.Shell; +import ru.fizteh.fivt.students.torunova.servlet.server.HttpServer; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +/** + * Created by nastya on 22.10.14. + */ + +public class Main { + private static final String DATABASE_DIRECTORY = "fizteh.db.dir"; + + public static void main(String[] args) { + if (System.getProperty(DATABASE_DIRECTORY) == null) { + System.err.println("Name of database not specified. Please, specify it via -D" + DATABASE_DIRECTORY); + System.exit(1); + } + HttpServer server = null; + try { + server = new HttpServer(System.getProperty(DATABASE_DIRECTORY)); + } catch (IncorrectDbException | IncorrectDbNameException + | IncorrectFileException | TableNotCreatedException | IOException e) { + System.out.println(e.toString()); + System.exit(1); + } + Set actions = new HashSet<>(); + actions.add(new Start(server, System.out)); + actions.add(new Stop(server, System.out)); + Shell shell = new Shell(actions, System.in, System.out, "stophttp", true); + + if (!shell.run()) { + System.exit(1); + } else { + System.exit(0); + } + } +} + + diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/ProxyFactory.java b/src/ru/fizteh/fivt/students/torunova/servlet/ProxyFactory.java new file mode 100644 index 000000000..0a370df44 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/ProxyFactory.java @@ -0,0 +1,123 @@ +package ru.fizteh.fivt.students.torunova.servlet; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import ru.fizteh.fivt.proxy.LoggingProxyFactory; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.Writer; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.IdentityHashMap; + +/** + * Created by nastya on 02.12.14. + */ +public class ProxyFactory implements LoggingProxyFactory { + class Logger implements InvocationHandler { + private Object implementation; + private Document document; + private Transformer transformer; + private final Writer writer; + public Logger(Object newImplementation, Writer newWriter) { + implementation = newImplementation; + document = createNewDocument(); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + try { + transformer = transformerFactory.newTransformer(); + } catch (TransformerConfigurationException e) { + //ignored. + } + writer = newWriter; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + document = createNewDocument(); + Element invoke = document.createElement("invoke"); + invoke.setAttribute("timestamp", String.valueOf(System.currentTimeMillis())); + invoke.setAttribute("class", implementation.getClass().getName()); + invoke.setAttribute("name", method.getName()); + document.appendChild(invoke); + Element arguments = document.createElement("arguments"); + invoke.appendChild(arguments); + Element argument; + if (args != null) { + for (Object obj : args) { + argument = document.createElement("argument"); + if (obj instanceof Iterable) { + IdentityHashMap map = new IdentityHashMap(); + map.put(obj, null); + logIterable(argument, (Iterable) obj, map); + } else { + argument.appendChild(document.createTextNode(obj.toString())); + } + arguments.appendChild(argument); + } + } + Element returnTag = document.createElement("return"); + Object result = null; + try { + result = method.invoke(implementation, args); + } catch (InvocationTargetException e) { + Element thrown = document.createElement("thrown"); + thrown.appendChild(document.createTextNode(e.getTargetException().toString())); + invoke.appendChild(thrown); + transformer.transform(new DOMSource(document), new StreamResult(writer)); + throw e.getTargetException(); + + } + if (method.getReturnType() != void.class) { + returnTag.appendChild(document.createTextNode(result.toString())); + } + invoke.appendChild(returnTag); + synchronized (writer) { + transformer.transform(new DOMSource(document), new StreamResult(writer)); + } + return result; + } + private void logIterable(Element parent, Iterable iterable, IdentityHashMap elements) { + Element value; + Element list = document.createElement("list"); + parent.appendChild(list); + for (Object obj: iterable) { + value = document.createElement("value"); + if (elements.containsKey(obj)) { + value.appendChild(document.createTextNode("cyclic")); + } else if (obj instanceof Iterable) { + IdentityHashMap map = new IdentityHashMap(elements); + map.put(obj, null); + logIterable(value, (Iterable) obj, map); + } else { + value.appendChild(document.createTextNode(obj.toString())); + } + list.appendChild(value); + } + + } + private Document createNewDocument() { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = null; + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + //ignored. + } + return builder.newDocument(); + } + } + @Override + public Object wrap(Writer writer, Object implementation, Class interfaceClass) { + return Proxy.newProxyInstance(interfaceClass.getClassLoader(), + new Class[] {interfaceClass}, new Logger(implementation, writer)); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/Database.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/Database.java new file mode 100644 index 000000000..f0bbaece1 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/Database.java @@ -0,0 +1,171 @@ +package ru.fizteh.fivt.students.torunova.servlet.database; + + +import ru.fizteh.fivt.storage.strings.TableProvider; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbNameException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectFileException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.TableNotCreatedException; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * Created by nastya on 19.10.14. + */ +public class Database implements TableProvider { + private String dbName; + private Map tables = new HashMap<>(); + + @Override + public int hashCode() { + return dbName.hashCode(); + } + + @Override + public boolean equals(Object db1) { + if (!(db1 instanceof Database)) { + return false; + } + Database db = (Database) db1; + return dbName.equals(db.dbName); + } + + public Database(String name) throws IncorrectDbNameException, + IOException, + TableNotCreatedException, + IncorrectFileException, + IncorrectDbException { + File db = new File(name).getAbsoluteFile(); + if (!db.exists()) { + if (!db.mkdirs()) { + throw new RuntimeException("database cannot be created"); + } + } else if (!db.isDirectory()) { + throw new IncorrectDbNameException("File with this name already exists."); + } + dbName = db.getAbsolutePath(); + File[]dbTables = db.listFiles(); + if (dbTables != null) { + for (File table : dbTables) { + if (table.getAbsoluteFile().isDirectory()) { + tables.put(table.getName(), new TableImpl(table.getAbsolutePath())); + } else { + throw new IncorrectDbException("Database contains illegal files."); + } + } + } + } + + @Override + public TableImpl getTable(String name) { + checkTableName(name); + return tables.get(name); + } + + @Override + public TableImpl createTable(String tableName) { + checkTableName(tableName); + File table = new File(dbName, tableName); + String newTableName = table.getAbsolutePath(); + if (!tables.containsKey(tableName)) { + TableImpl newTable; + try { + newTable = new TableImpl(newTableName); + } catch (TableNotCreatedException | IncorrectFileException | IOException e) { + throw new RuntimeException(e); + } + tables.put(tableName, newTable); + return newTable; + } + return null; + } + + @Override + public void removeTable(String name) { + checkTableName(name); + File f = new File(dbName, name); + if (tables.containsKey(name)) { + removeRecursive(f.getAbsolutePath()); + tables.get(name).markAsRemoved(); + tables.remove(name); + } else { + throw new IllegalStateException("does not exist"); + } + } + + public Map showTables() { + Map tablesWithSize = new HashMap<>(); + tables.forEach((name, table)->tablesWithSize.put(name, table.getNumberOfEntries())); + return tablesWithSize; + } + public String getDbName() { + return dbName; + } + + public void close() { + for (TableImpl t :tables.values()) { + t.rollback(); + t.markAsClosed(); + } + } + /** + * removes file + * @param file - filename. + * @return true if file is regular and deleted,false otherwise. + */ + private boolean remove(final String file) { + File fileWithAbsolutePath = new File(file).getAbsoluteFile(); + if (fileWithAbsolutePath.isFile()) { + if (!fileWithAbsolutePath.delete()) { + return false; + } + } else if (fileWithAbsolutePath.isDirectory()) { + return false; + } else if (!fileWithAbsolutePath.exists()) { + return false; + } + return true; + } + /** + * removes directory. + * + * @param dir - directory name. + */ + private boolean removeRecursive(final String dir) { + File directory = new File(dir).getAbsoluteFile(); + if (directory.isDirectory()) { + File[] content = directory.listFiles(); + if (content != null) { + for (File item : content) { + if (item.isDirectory()) { + if (!removeRecursive(item.getAbsolutePath())) { + return false; + } + } else { + if (!remove(item.getAbsolutePath())) { + return false; + } + } + } + } + if (!directory.delete()) { + return false; + } + } else { + return false; + } + return true; + } + private void checkTableName(String name) { + if (name == null || Pattern.matches(".*" + Pattern.quote(File.separator) + ".*", name) + || name.equals("..") || name.equals(".")) { + throw new IllegalArgumentException("illegal table name"); + } + } + + +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/DatabaseFactory.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/DatabaseFactory.java new file mode 100644 index 000000000..ee491684a --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/DatabaseFactory.java @@ -0,0 +1,32 @@ +package ru.fizteh.fivt.students.torunova.servlet.database; + +import ru.fizteh.fivt.storage.strings.TableProvider; +import ru.fizteh.fivt.storage.strings.TableProviderFactory; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbNameException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.TableNotCreatedException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectFileException; + +import java.io.IOException; + +/** + * Created by nastya on 04.11.14. + */ +public class DatabaseFactory implements TableProviderFactory { + public DatabaseFactory(){} + @Override + public TableProvider create(String dir) throws IllegalArgumentException { + TableProvider tp; + if (dir == null) { + throw new IllegalArgumentException("Illegal name of database."); + } + try { + tp = new Database(dir); + } catch (IncorrectDbNameException | IOException | TableNotCreatedException + | IncorrectFileException | IncorrectDbException e) { + throw new RuntimeException(e); + } + return tp; + + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/DatabaseFactoryWrapper.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/DatabaseFactoryWrapper.java new file mode 100644 index 000000000..6ac1bbf61 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/DatabaseFactoryWrapper.java @@ -0,0 +1,29 @@ +package ru.fizteh.fivt.students.torunova.servlet.database; + +import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.storage.structured.TableProviderFactory; + +import java.io.IOException; + +/** + * Created by nastya on 24.11.14. + */ +public class DatabaseFactoryWrapper implements TableProviderFactory, AutoCloseable{ + private DatabaseFactory factory; + private boolean closed; + public DatabaseFactoryWrapper() { + factory = new DatabaseFactory(); + } + @Override + public TableProvider create(String path) throws IOException { + if (closed) { + throw new IllegalStateException("Table provider factory already closed."); + } + return new DatabaseWrapper(factory.create(path)); + } + + @Override + public void close() throws Exception { + closed = true; + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/DatabaseWrapper.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/DatabaseWrapper.java new file mode 100644 index 000000000..6559fa00e --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/DatabaseWrapper.java @@ -0,0 +1,298 @@ +package ru.fizteh.fivt.students.torunova.servlet.database; + +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbNameException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.TableNotCreatedException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectFileException; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.stream.Stream; + +/** + * Created by nastya on 20.11.14. + */ +public class DatabaseWrapper implements TableProvider, AutoCloseable { + private Database db; + private static final String REGEXP_TO_SPLIT_JSON = ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"; + private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private boolean closed; + public DatabaseWrapper(String dbName) throws IncorrectDbException, + IncorrectDbNameException, + IncorrectFileException, + TableNotCreatedException, + IOException { + db = new Database(dbName); + } + DatabaseWrapper(ru.fizteh.fivt.storage.strings.TableProvider newDb) { + db = (Database) newDb; + } + + @Override + public int hashCode() { + return db.hashCode(); + } + + @ Override + public boolean equals(Object obj) { + if (!(obj instanceof DatabaseWrapper)) { + return false; + } + return db.equals(((DatabaseWrapper) obj).db); + } + + @Override + public Table getTable(String name) { + if (closed) { + throw new IllegalStateException("Table provide is closed."); + } + readWriteLock.readLock().lock(); + try { + TableImpl t = db.getTable(name); + if (t == null) { + return null; + } + if (t.isClosed()) { + t.markAsOpened(); + } + return new TableWrapper(t, this); + } finally { + readWriteLock.readLock().unlock(); + } + } + + @Override + public Table createTable(String name, List> columnTypes) throws IOException { + if (closed) { + throw new IllegalStateException("Table provider is closed."); + } + readWriteLock.writeLock().lock(); + try { + TableImpl t = db.createTable(name); + if (t == null) { + return null; + } + TableWrapper table = new TableWrapper(t, this, columnTypes.toArray(new Class[0])); + return table; + } finally { + readWriteLock.writeLock().unlock(); + } + } + + @Override + public void removeTable(String name) throws IOException { + if (closed) { + throw new IllegalStateException("Table provide is closed."); + } + readWriteLock.writeLock().lock(); + try { + db.removeTable(name); + } finally { + readWriteLock.writeLock().unlock(); + } + } + + @Override + public Storeable deserialize(Table table, String value) throws ParseException { + if (closed) { + throw new IllegalStateException("Table provide is closed."); + } + Class[] types = new Class[table.getColumnsCount()]; + for (int i = 0; i < types.length; i++) { + types[i] = table.getColumnType(i); + } + StoreableType storeableValue = new StoreableType(types); + if (value == null) { + return null; + } + checkJsonFormat(value); + value = value.substring(value.indexOf('[') + 1, value.lastIndexOf(']')); + value = value.trim(); + String[] values = Stream.of(value.split(REGEXP_TO_SPLIT_JSON)). + map(String::trim).toArray(String[]::new); + if (values.length != types.length) { + throw new ColumnFormatException("Wrong number of values"); + } + for (int i = 0; i < types.length; i++) { + try { + if (values[i].equals("null")) { + storeableValue.setColumnAt(i, null); + } else if (!types[i].equals(Integer.class) + && !types[i].equals(String.class) + && !types[i].equals(Boolean.class)) { + try { + + storeableValue.setColumnAt(i, + types[i].getMethod("parse" + types[i].getSimpleName(), + String.class).invoke(null, values[i])); + } catch (NoSuchMethodException + | IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + Throwable exception = e.getTargetException(); + if (exception.getClass() == NumberFormatException.class) { + throw new IllegalArgumentException("column " + (i + 1) + + " should contain " + classForName(types[i].getSimpleName())); + } else { + throw new RuntimeException(exception); + } + + } + } else if (types[i].equals(Integer.class)) { + int intValue; + try { + intValue = Integer.parseInt(values[i]); + } catch (NumberFormatException e) { + throw new NumberFormatException("column " + (i + 1) + " should contain int"); + } + storeableValue.setColumnAt(i, intValue); + } else if (types[i].equals(String.class)) { + if (!values[i].contains("\"")) { + throw new IllegalArgumentException("column " + (i + 1) + " should contain string"); + } + storeableValue.setColumnAt(i, + values[i].substring(values[i].indexOf('"') + 1, + values[i].lastIndexOf('"'))); + } else if (types[i].equals(Boolean.class)) { + if (!Boolean.parseBoolean(values[i]) && !values[i].equals("false")) { + throw new IllegalArgumentException("column " + (i + 1) + " should contain boolean"); + } + storeableValue.setColumnAt(i, Boolean.parseBoolean(values[i])); + } + } catch (IllegalArgumentException e) { + throw new RuntimeException("wrong type( " + e.getMessage() + " )"); + } + } + return storeableValue; + } + + @Override + public String serialize(Table table, Storeable value) throws ColumnFormatException { + if (closed) { + throw new IllegalStateException("Table provide is closed."); + } + if (value == null) { + return null; + } + StringBuilder builder = new StringBuilder(); + int size = table.getColumnsCount(); + builder.append('['); + Object obj; + for (int i = 0; i < size; i++) { + obj = value.getColumnAt(i); + if (obj != null && obj.getClass().equals(String.class)) { + builder.append('"'); + } + builder.append(obj); + if (obj != null && obj.getClass().equals(String.class)) { + builder.append('"'); + } + if (i != size - 1) { + builder.append(", "); + } + } + builder.append(']'); + return builder.toString(); + } + + @Override + public Storeable createFor(Table table) { + if (closed) { + throw new IllegalStateException("Table provide is closed."); + } + Class[] types = new Class[table.getColumnsCount()]; + for (int i = 0; i < types.length; i++) { + types[i] = table.getColumnType(i); + } + return new StoreableType(types); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + db.getDbName() + "]"; + } + + @Override + public Storeable createFor(Table table, List values) + throws ColumnFormatException, + IndexOutOfBoundsException { + if (closed) { + throw new IllegalStateException("Table provide is closed."); + } + Class[] types = new Class[table.getColumnsCount()]; + for (int i = 0; i < types.length; i++) { + types[i] = table.getColumnType(i); + } + + StoreableType storeableValue = new StoreableType(types); + for (int i = 0; i < values.size(); i++) { + storeableValue.setColumnAt(i, values.get(i)); + } + return storeableValue; + } + + @Override + public List getTableNames() { + if (closed) { + throw new IllegalStateException("Table provide is closed."); + } + readWriteLock.readLock().lock(); + try { + return new ArrayList<>(db.showTables().keySet()); + } finally { + readWriteLock.readLock().unlock(); + } + } + public Map showTables() { + readWriteLock.readLock().lock(); + try { + return db.showTables(); + } finally { + readWriteLock.readLock().unlock(); + } + } + public String getDbName() { + return db.getDbName(); + } + public Class classForName(String className) { + switch(className) { + case "int": + return Integer.class; + case "double": + return Double.class; + case "long": + return Long.class; + case "String": + return String.class; + case "byte": + return Byte.class; + case "float": + return Float.class; + case "boolean": + return Boolean.class; + default: throw new RuntimeException("Unsupported type " + className); + } + } + + @Override + public void close() throws Exception { + closed = true; + db.close(); + } + private void checkJsonFormat(String value) { + if (!value.startsWith("[") || !value.endsWith("]")) { + throw new IllegalStateException("wrong value format " + + "(it should be like this: [value1, value2,..., valueN])"); + } + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/FileMap.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/FileMap.java new file mode 100644 index 000000000..ab49c6a6c --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/FileMap.java @@ -0,0 +1,260 @@ +package ru.fizteh.fivt.students.torunova.servlet.database; + +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectFileException; + +import java.io.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * Created by nastya on 19.10.14. + */ +public class FileMap { + private static final int OVERWRITTEN = 1; + private static final int ADDED = 0; + private static final int DELETED = 2; + private static final int NUMBER_OF_PARTITIONS = 16; + private static final String ENCODING = "UTF-8"; + private Map savedCopy = new HashMap<>(); + private final ThreadLocal> diff = new ThreadLocal>() { + @Override + protected Map initialValue() { + return new HashMap<>(); + } + }; + private final ThreadLocal> changes = new ThreadLocal>() { + @Override + protected Map initialValue() { + return new HashMap<>(); + } + }; + private String file; + private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + public FileMap(final String f) throws IncorrectFileException, IOException { + file = f; + readFile(); + } + + public String put(String key, String value) { + readWriteLock.readLock().lock(); + try { + String putInDiffResult = diff.get().put(key, value); + String getFromSavedCopyResult = savedCopy.get(key); + if (putInDiffResult != null || getFromSavedCopyResult != null) { + changes.get().put(key, OVERWRITTEN); + } else { + changes.get().put(key, ADDED); + } + if (putInDiffResult == null && getFromSavedCopyResult != null) { + return getFromSavedCopyResult; + } + return putInDiffResult; + } finally { + readWriteLock.readLock().unlock(); + } + } + + public String get(String key) { + readWriteLock.readLock().lock(); + try { + String getFromDiffResult = diff.get().get(key); + String getFromSavedCopyResult = savedCopy.get(key); + if (getFromDiffResult == null && getFromSavedCopyResult != null) { + return getFromSavedCopyResult; + } + return getFromDiffResult; + } finally { + readWriteLock.readLock().unlock(); + } + } + + public String remove(String key) { + readWriteLock.readLock().lock(); + try { + String removeFromDiffResult = diff.get().remove(key); + String getFromSavedCopy = savedCopy.get(key); + if (removeFromDiffResult != null || getFromSavedCopy != null) { + changes.get().put(key, DELETED); + } + if (removeFromDiffResult == null && getFromSavedCopy != null) { + return getFromSavedCopy; + } + return removeFromDiffResult; + } finally { + readWriteLock.readLock().unlock(); + } + } + + public Set list() { + readWriteLock.readLock().lock(); + try { + Set workingCopyKeySet = savedCopy.keySet(); + Set diffKeySet = diff.get().keySet(); + Set keys = new HashSet<>(); + if (changes == null || changes.get() == null) { + keys.addAll(workingCopyKeySet); + return keys; + } else { + for (String key : workingCopyKeySet) { + if (changes.get().get(key) == null + || (changes.get().get(key) != null && changes.get().get(key) != DELETED)) { + keys.add(key); + } + } + for (String key : diffKeySet) { + if (changes.get().get(key) != null && changes.get().get(key) != DELETED) { + keys.add(key); + } + } + } + return keys; + } finally { + readWriteLock.readLock().unlock(); + } + } + + public boolean isEmpty() { + readWriteLock.readLock().lock(); + try { + Map newSavedCopy = new HashMap<>(savedCopy); + for (Map.Entry entry : changes.get().entrySet()) { + if (entry.getValue() == DELETED) { + newSavedCopy.remove(entry.getKey()); + } else { + newSavedCopy.put(entry.getKey(), diff.get().get(entry.getKey())); + } + } + return newSavedCopy.isEmpty(); + } finally { + readWriteLock.readLock().unlock(); + } + } + + public int size() { + readWriteLock.readLock().lock(); + try { + Map newSavedCopy = new HashMap<>(savedCopy); + for (Map.Entry entry : changes.get().entrySet()) { + if (entry.getValue() == DELETED) { + newSavedCopy.remove(entry.getKey()); + } else { + newSavedCopy.put(entry.getKey(), diff.get().get(entry.getKey())); + } + } + return newSavedCopy.size(); + } finally { + readWriteLock.readLock().unlock(); + } + } + + public int rollback() { + int numberOfRevertedChanges = countChangedEntries(); + diff.get().clear(); + changes.get().clear(); + return numberOfRevertedChanges; + } + public int commit() throws IOException { + readWriteLock.writeLock().lock(); + try { + DataOutputStream fos = new DataOutputStream(new FileOutputStream(file)); + Set keys = list(); + String valueFromSavedCopy; + String valueFromDiff; + for (String key : keys) { + fos.writeInt(key.getBytes(ENCODING).length); + fos.write(key.getBytes(ENCODING)); + valueFromDiff = diff.get().get(key); + valueFromSavedCopy = savedCopy.get(key); + fos.writeInt((valueFromDiff == null ? valueFromSavedCopy : valueFromDiff).getBytes(ENCODING).length); + fos.write((valueFromDiff == null ? valueFromSavedCopy : valueFromDiff).getBytes(ENCODING)); + } + int numberOfChangedEntries = countChangedEntries(); + Map newSavedCopy = new HashMap<>(savedCopy); + for (Map.Entry entry : changes.get().entrySet()) { + if (entry.getValue() == DELETED) { + newSavedCopy.remove(entry.getKey()); + } else { + newSavedCopy.put(entry.getKey(), diff.get().get(entry.getKey())); + } + } + savedCopy = newSavedCopy; + diff.get().clear(); + changes.get().clear(); + return numberOfChangedEntries; + } finally { + readWriteLock.writeLock().unlock(); + } + } + public int countChangedEntries() { + int counter = 0; + readWriteLock.readLock().lock(); + try { + for (Map.Entry entry : diff.get().entrySet()) { + if (savedCopy.get(entry.getKey()) == null || !savedCopy.get(entry.getKey()).equals(entry.getValue())) { + counter++; + } + } + } finally { + readWriteLock.readLock().unlock(); + } + return counter; + } + private void readFile() throws IncorrectFileException, IOException { + readWriteLock.writeLock().lock(); + try { + DataInputStream fis = new DataInputStream(new FileInputStream(file)); + int length; + while (fis.available() > 0) { + length = fis.readInt(); + if (length >= Runtime.getRuntime().freeMemory()) { + throw new IncorrectFileException("Cannot load file " + file + ". Ran out of memory."); + } + if (length < 0) { + throw new IncorrectFileException("File " + file + " has wrong structure."); + } + byte[] key = new byte[length]; + if (fis.read(key) != length) { + throw new IncorrectFileException("File " + file + " has wrong structure."); + } + if (!checkKey(new String(key, ENCODING))) { + throw new IncorrectFileException("File " + file + " contains illegal key."); + } + length = fis.readInt(); + if (length >= Runtime.getRuntime().freeMemory()) { + throw new IncorrectFileException("Cannot load file " + file + ". Ran out of memory."); + } + if (length < 0) { + throw new IncorrectFileException("File " + file + " has wrong structure."); + } + byte[] value = new byte[length]; + if (fis.read(value) != length) { + throw new IncorrectFileException("File " + file + " has wrong structure."); + } + + savedCopy.put(new String(key, ENCODING), new String(value, ENCODING)); + } + } finally { + readWriteLock.writeLock().unlock(); + } + } + private int getIndexOfFile() { + return Integer.parseInt(file.substring(file.lastIndexOf(File.separatorChar) + 1, + file.lastIndexOf('.'))); + } + private int getIndexOfDir() { + File f = new File(file).getAbsoluteFile(); + String dirName = f.getParentFile().getName(); + return Integer.parseInt(dirName.substring(0, dirName.indexOf('.'))); + } + private boolean checkKey(String key) { + int hashcode = Math.abs(key.hashCode()); + int indexOfKeyFile = hashcode / NUMBER_OF_PARTITIONS % NUMBER_OF_PARTITIONS; + int indexOfKeyDir = hashcode % NUMBER_OF_PARTITIONS; + return indexOfKeyDir == getIndexOfDir() && indexOfKeyFile == getIndexOfFile(); + } + +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/StoreableType.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/StoreableType.java new file mode 100644 index 000000000..62ad52a99 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/StoreableType.java @@ -0,0 +1,124 @@ +package ru.fizteh.fivt.students.torunova.servlet.database; + +import ru.fizteh.fivt.storage.structured.ColumnFormatException; + +import java.util.Arrays; + +/** + * Created by nastya on 16.11.14. + */ +public class StoreableType implements ru.fizteh.fivt.storage.structured.Storeable { + private Class[] types; + private Object[] values; + public StoreableType(Class... newTypes) { + types = newTypes; + values = new Object[types.length]; + } + + @Override + public int hashCode() { + return Arrays.hashCode(values); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof StoreableType)) { + return false; + } + for (int i = 0; i < values.length; i++) { + if (values[i] != null + && !values[i].equals(((StoreableType) obj).getColumnAt(i))) { + return false; + } else if (values[i] == null && ((StoreableType) obj).getColumnAt(i) != null) { + return false; + } + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (Object obj:values) { + builder.append(", "); + if (obj != null) { + builder.append(obj); + } + } + return getClass().getSimpleName() + "[" + builder.toString() + "]"; + } + + @Override + public void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException { + checkColumnIndex(columnIndex); + if (value != null) { + checkColumnFormat(columnIndex, value.getClass()); + } + values[columnIndex] = value; + } + + @Override + public Object getColumnAt(int columnIndex) throws IndexOutOfBoundsException { + checkColumnIndex(columnIndex); + return values[columnIndex]; + } + + @Override + public Integer getIntAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return getTypeAt(columnIndex, Integer.class); + } + + @Override + public Long getLongAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return getTypeAt(columnIndex, Long.class); + } + + @Override + public Byte getByteAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return getTypeAt(columnIndex, Byte.class); + } + + @Override + public Float getFloatAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return getTypeAt(columnIndex, Float.class); + } + + @Override + public Double getDoubleAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return getTypeAt(columnIndex, Double.class); + } + + @Override + public Boolean getBooleanAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkColumnIndex(columnIndex); + checkColumnFormat(columnIndex, Boolean.class); + return (Boolean) values[columnIndex]; + } + + @Override + public String getStringAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return getTypeAt(columnIndex, String.class); + } + public int getNumberOfColumns() { + return types.length; + } + public Class getColumnType(int columnIndex) { + return types[columnIndex]; + } + private void checkColumnFormat(int columnIndex, Class expectedClass) throws ColumnFormatException { + if (!types[columnIndex].equals(expectedClass)) { + throw new ColumnFormatException("Wrong type of value in column " + columnIndex); + } + } + private void checkColumnIndex(int columnIndex) throws IndexOutOfBoundsException { + if (columnIndex >= values.length) { + throw new IndexOutOfBoundsException(); + } + } + private T getTypeAt(int columnIndex, Class clazz) { + checkColumnIndex(columnIndex); + checkColumnFormat(columnIndex, clazz); + return (T) values[columnIndex]; + } + +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/TableHolder.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/TableHolder.java new file mode 100644 index 000000000..de11a365f --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/TableHolder.java @@ -0,0 +1,26 @@ +package ru.fizteh.fivt.students.torunova.servlet.database; + +/** + * Created by nastya on 01.12.14. + */ +public class TableHolder { + private final ThreadLocal currentTable = new ThreadLocal<>(); + private DatabaseWrapper db; + public TableHolder(DatabaseWrapper db) { + this.db = db; + currentTable.set(null); + } + public TableWrapper get() { + return currentTable.get(); + } + public DatabaseWrapper getDb() { + return db; + } + public boolean set(String name) { + currentTable.set((TableWrapper) db.getTable(name)); + return currentTable != null; + } + public void reset() { + currentTable.set(null); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/TableImpl.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/TableImpl.java new file mode 100644 index 000000000..8ed0a2cc3 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/TableImpl.java @@ -0,0 +1,269 @@ +package ru.fizteh.fivt.students.torunova.servlet.database; + +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectFileException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.TableNotCreatedException; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.regex.Pattern; + +/** + * Created by nastya on 19.10.14. + */ +public class TableImpl implements ru.fizteh.fivt.storage.strings.Table{ + private static final int NUMBER_OF_PARTITIONS = 16; + private static final String DIRECTORY_PATTERN = "([0-9]|1[0-5])\\.dir"; + private static final String FILE_PATTERN = "([0-9]|1[0-5])\\.dat"; + private String tableName; + private Map files = new HashMap<>(); + private int numberOfEntries; + private boolean removed; + private boolean closed; + + @Override + public boolean equals(Object t) { + if (!(t instanceof TableImpl)) { + return false; + } + TableImpl table = (TableImpl) t; + return tableName.equals(table.tableName) + && files.equals(table.files) + && numberOfEntries == table.numberOfEntries; + } + + @Override + public int hashCode() { + return tableName.hashCode(); + } + + public TableImpl(String newTableName) throws TableNotCreatedException, IncorrectFileException, IOException { + File table = new File(newTableName).getAbsoluteFile(); + if (!table.exists()) { + if (!table.mkdirs()) { + throw new TableNotCreatedException("table cannot be created"); + } + } + tableName = table.getAbsolutePath(); + File[] tableFiles = table.listFiles(); + File[] filesOfDir; + if (tableFiles != null) { + for (File nextDir : tableFiles) { + if (!(Pattern.matches(DIRECTORY_PATTERN, nextDir.getName()) && nextDir.isDirectory()) + && !(nextDir.isFile() && nextDir.getName().equals("signature.tsv"))) { + throw new TableNotCreatedException("Table " + getName() + + " contains illegal files: " + nextDir.getAbsolutePath()); + } + filesOfDir = nextDir.listFiles(); + if (filesOfDir != null) { + for (File nextFile : filesOfDir) { + if (!(Pattern.matches(FILE_PATTERN, nextFile.getName()) && nextFile.isFile())) { + throw new TableNotCreatedException("Table " + getName() + + " contains illegal files: " + nextFile.getAbsolutePath()); + } + FileMap fm = new FileMap(nextFile.getAbsolutePath()); + if (fm.isEmpty()) { + nextFile.delete(); + nextDir = nextFile.getParentFile(); + File[] nextDirFiles = nextDir.listFiles(); + if (nextDirFiles != null && nextDirFiles.length == 0) { + nextDir.delete(); + } + } else { + files.put(nextFile, fm); + numberOfEntries += fm.size(); + } + } + } + } + } + removed = false; + closed = false; + } + @Override + public String put(String key, String value) { + checkRemovedOrClosed(); + if (key == null || value == null) { + throw new IllegalArgumentException("Key or value is null"); + } + String result; + String fileName = getFileName(key); + String dirName = getDirName(key); + File dir = new File(tableName, dirName).getAbsoluteFile(); + File file = new File(dir, fileName).getAbsoluteFile(); + if (files.containsKey(file)) { + result = files.get(file).put(key, value); + } else { + if (!file.getParentFile().mkdirs()) { + throw new RuntimeException("directory " + dirName + " cannot be created"); + } + FileMap fm; + try { + file.createNewFile(); + fm = new FileMap(file.getAbsolutePath()); + } catch (IOException | IncorrectFileException e) { + throw new RuntimeException(e); + } + result = fm.put(key, value); + files.put(file, fm); + } + if (result == null) { + numberOfEntries++; + } + return result; + } + + @Override + public String getName() { + checkRemovedOrClosed(); + File ourTable = new File(tableName); + return ourTable.getName(); + } + + @Override + public String get(String key) { + checkRemovedOrClosed(); + if (key == null) { + throw new IllegalArgumentException("Key is null"); + } + String fileName = getFileName(key); + String dirName = getDirName(key); + File dir = new File(tableName, dirName).getAbsoluteFile(); + File file = new File(dir, fileName).getAbsoluteFile(); + if ((!dir.exists()) || (!file.exists())) { + return null; + } + FileMap fm; + fm = files.get(file); + if (fm == null) { + return null; + } + return fm.get(key); + } + + @Override + public String remove(String key) { + checkRemovedOrClosed(); + String result; + String fileName = getFileName(key); + String dirName = getDirName(key); + File dir = new File(tableName, dirName).getAbsoluteFile(); + File file = new File(dir, fileName).getAbsoluteFile(); + if (files.containsKey(file)) { + FileMap fm = files.get(file); + result = fm.remove(key); + numberOfEntries--; + return result; + } + return null; + } + + @Override + public int size() { + checkRemovedOrClosed(); + return numberOfEntries; + } + + public List list() { + checkRemovedOrClosed(); + List listOfAllKeys = new ArrayList<>(); + for (FileMap fm : files.values()) { + listOfAllKeys.addAll(fm.list()); + } + return listOfAllKeys; + } + @Override + public int commit() { + checkRemovedOrClosed(); + int numberOfChangedEntries = 0; + Set> entrySet = new HashSet<>(files.entrySet()); + IOException e = null; + for (Map.Entry entry : entrySet) { + FileMap fm = entry.getValue(); + File file = entry.getKey(); + try { + numberOfChangedEntries += fm.commit(); + } catch (IOException e1) { + e = e1; + continue; + } + if (fm.isEmpty()) { + File directory = file.getParentFile().getAbsoluteFile(); + file.delete(); + File[] dirFiles = directory.listFiles(); + if (dirFiles != null && dirFiles.length == 0) { + directory.delete(); + } + files.remove(file); + } + } + if (e != null) { + throw new RuntimeException(e); + } + return numberOfChangedEntries; + } + + @Override + public int rollback() { + checkRemovedOrClosed(); + int numberOfRevertedChanges = 0; + numberOfEntries = 0; + for (FileMap fm:files.values()) { + numberOfRevertedChanges += fm.rollback(); + numberOfEntries += fm.size(); + } + return numberOfRevertedChanges; + } + + public int countChangedEntries() { + checkRemovedOrClosed(); + int numberOfChangedEntries = 0; + for (FileMap fm:files.values()) { + numberOfChangedEntries += fm.countChangedEntries(); + } + return numberOfChangedEntries; + } + public boolean isRemoved() { + return removed; + } + public void markAsRemoved() { + removed = true; + } + public void markAsClosed() { + closed = true; + } + public void markAsOpened() { + closed = false; + } + public boolean isClosed() { + return closed; + } + public int getNumberOfEntries() { + return numberOfEntries; + } + + private String getDirName(String key) { + int hashcode = Math.abs(key.hashCode()); + int ndirectory = hashcode % NUMBER_OF_PARTITIONS; + StringBuilder builder = new StringBuilder(); + builder.append(ndirectory).append(".dir"); + return builder.toString(); + } + + private String getFileName(String key) { + int hashcode = Math.abs(key.hashCode()); + int nfile = hashcode / NUMBER_OF_PARTITIONS % NUMBER_OF_PARTITIONS; + StringBuilder builder = new StringBuilder(); + builder.append(nfile).append(".dat"); + return builder.toString(); + + } + private void checkRemovedOrClosed() { + if (removed) { + throw new IllegalStateException("Table already removed."); + } else if (closed) { + throw new IllegalStateException("Table is closed."); + } + } + +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/TableWrapper.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/TableWrapper.java new file mode 100644 index 000000000..9a69cdabb --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/TableWrapper.java @@ -0,0 +1,227 @@ +package ru.fizteh.fivt.students.torunova.servlet.database; + +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectFileException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.TableNotCreatedException; + +import java.io.*; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +/** + * Created by nastya on 19.11.14. + */ +public class TableWrapper implements ru.fizteh.fivt.storage.structured.Table, AutoCloseable { + private static final String SIGNATURE_FILE = "signature.tsv"; + private TableImpl table; + private StoreableType headOfTable; + private DatabaseWrapper tableProvider; + + public TableWrapper(String tableName, + DatabaseWrapper newTableProvider, Class...newTypes) + throws IOException, + TableNotCreatedException, + IncorrectFileException { + this(new TableImpl(tableName), newTableProvider, newTypes); + } + public TableWrapper(TableImpl newTable, DatabaseWrapper newTableProvider, Class... newTypes) { + table = newTable; + headOfTable = new StoreableType(newTypes); + tableProvider = newTableProvider; + File tableDir = new File(newTableProvider.getDbName(), table.getName()); + File signature = new File(tableDir, SIGNATURE_FILE).getAbsoluteFile(); + if (!signature.exists()) { + PrintWriter fos; + try { + signature.createNewFile(); + fos = new PrintWriter(new FileOutputStream(signature)); + } catch (IOException e) { + throw new RuntimeException(e); + } + for (Class type : newTypes) { + fos.print(normalSimpleName(type) + " "); + } + fos.flush(); + } + } + public TableWrapper(TableImpl newTable, DatabaseWrapper newTableProvider) { + table = newTable; + tableProvider = newTableProvider; + File tableDir = new File(newTableProvider.getDbName(), table.getName()); + File signature = new File(tableDir, SIGNATURE_FILE); + Scanner scanner; + try { + scanner = new Scanner(new FileInputStream(signature)); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + List> types = new ArrayList<>(); + while (scanner.hasNext()) { + types.add(classForName(scanner.next())); + } + headOfTable = new StoreableType(types.toArray(new Class[0])); + } + + @Override + public int hashCode() { + if (table != null && headOfTable != null) { + return (table.hashCode() + headOfTable.hashCode()); + } + return 0; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TableWrapper)) { + return false; + } + return table.equals(((TableWrapper) obj).table) && headOfTable.equals(((TableWrapper) obj).headOfTable); + } + + @Override + public String toString() { + File table = new File(tableProvider.getDbName(), getName()).getAbsoluteFile(); + return getClass().getSimpleName() + "[" + table.getAbsolutePath() + "]"; + } + + @Override + public Storeable put(String key, Storeable value) throws ColumnFormatException { + checkValueFormat((StoreableType) value); + Storeable storeableValue; + try { + storeableValue = tableProvider.deserialize(this, + table.put(key, tableProvider.serialize(this, value))); + } catch (ParseException e) { + throw new RuntimeException(e); + } + return storeableValue; + } + + @Override + public Storeable remove(String key) { + Storeable storeableValue; + try { + storeableValue = tableProvider.deserialize(this, table.remove(key)); + } catch (ParseException e) { + throw new RuntimeException(e); + } + return storeableValue; + } + + @Override + public int size() { + return table.size(); + } + + @Override + public List list() { + return table.list(); + } + + @Override + public int commit() { + return table.commit(); + } + + @Override + public int rollback() { + return table.rollback(); + } + + @Override + public int getNumberOfUncommittedChanges() { + return table.countChangedEntries(); + } + + @Override + public int getColumnsCount() { + if (table.isRemoved()) { + throw new IllegalStateException("Table already removed."); + } + return headOfTable.getNumberOfColumns(); + } + + @Override + public Class getColumnType(int columnIndex) throws IndexOutOfBoundsException { + if (table.isRemoved()) { + throw new IllegalStateException("Table already removed."); + } + return headOfTable.getColumnType(columnIndex); + } + + @Override + public String getName() { + return table.getName(); + } + + @Override + public Storeable get(String key) { + Storeable storeableValue; + try { + storeableValue = tableProvider.deserialize(this, table.get(key)); + } catch (ParseException e) { + throw new RuntimeException(e); + } + return storeableValue; + } + + private void checkValueFormat(StoreableType value) throws ColumnFormatException { + if (headOfTable.getNumberOfColumns() != value.getNumberOfColumns()) { + throw new ColumnFormatException(); + } + int n = headOfTable.getNumberOfColumns(); + for (int i = 0; i < n; i++) { + if (value.getColumnType(i) != headOfTable.getColumnType(i)) { + throw new ColumnFormatException("Wrong value format"); + } + } + } + private String normalSimpleName(Class type) { + String simpleName = type.getSimpleName(); + switch (simpleName) { + case "Integer" : + return "int"; + case "Float" : + return "float"; + case "Double" : + return "double"; + case "Long" : + return "long"; + case "Boolean" : + return "boolean"; + case "Byte" : + return "byte"; + case "String" : + return simpleName; + default: throw new RuntimeException("Unsupported type " + simpleName); + } + } + public Class classForName(String className) { + switch(className) { + case "int": + return Integer.class; + case "double": + return Double.class; + case "long": + return Long.class; + case "String": + return String.class; + case "byte": + return Byte.class; + case "float": + return Float.class; + case "boolean": + return Boolean.class; + default: throw new RuntimeException("Unsupported type " + className); + } + } + + @Override + public void close() throws Exception { + rollback(); + table.markAsClosed(); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/Transaction.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/Transaction.java new file mode 100644 index 000000000..4a0018f73 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/Transaction.java @@ -0,0 +1,26 @@ +package ru.fizteh.fivt.students.torunova.servlet.database; + +import ru.fizteh.fivt.storage.structured.Storeable; + +import java.io.IOException; + +/** + * Created by nastya on 25.12.14. + */ +public interface Transaction { + int getId(); + + Storeable put(String key, Storeable value); + + Storeable get(String key); + + int size(); + + int commit() throws IOException; + + int rollback(); + + String serialize(Storeable value); + + Storeable deserialize(String value); +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/TransactionDatabase.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/TransactionDatabase.java new file mode 100644 index 000000000..732c2fca8 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/TransactionDatabase.java @@ -0,0 +1,146 @@ +package ru.fizteh.fivt.students.torunova.servlet.database; + +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbNameException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectFileException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.TableNotCreatedException; + +import java.io.IOException; +import java.text.ParseException; +import java.util.*; + +/** + * Created by nastya on 25.12.14. + */ +public class TransactionDatabase { + private DatabaseWrapper innerDb; + class TransactionImpl implements Transaction { + //Set deletedKeys= new HashSet<>(); + Map changedKeys = new HashMap<>(); + int transactionId; + String tableNameforSync; + TableWrapper table; + + TransactionImpl(String tableName, int transactionId) { + table = (TableWrapper) innerDb.getTable(tableName); + this.transactionId = transactionId; + tableNameforSync = tableName.intern(); + } + + @Override + public int getId() { + return transactionId; + } + + @Override + public Storeable put(String key, Storeable value) { + synchronized (tableNameforSync) { + apply(); + Storeable result = table.put(key, value); + table.rollback(); + changedKeys.put(key, innerDb.serialize(table, value)); + // deletedKeys.remove(key); + return result; + } + } + + @Override + public Storeable get(String key) { + synchronized (tableNameforSync) { + apply(); + Storeable result = table.get(key); + table.rollback(); + return result; + } + } + + @Override + public int size() { + synchronized (tableNameforSync) { + apply(); + int size = table.size(); + table.rollback(); + return size; + } + } + + @Override + public int commit() throws IOException { + synchronized (tableNameforSync) { + apply(); + //deletedKeys.clear(); + changedKeys.clear(); + return table.commit(); + } + } + + @Override + public int rollback() { + synchronized (tableNameforSync) { + apply(); + //deletedKeys.clear(); + changedKeys.clear(); + return table.rollback(); + } + } + + @Override + public String serialize(Storeable value) { + return innerDb.serialize(table, value); + } + + @Override + public Storeable deserialize(String value) { + Storeable storeable = null; + try { + storeable = innerDb.deserialize(table, value); + } catch (ParseException e) { + //ignored. + } + return storeable; + } + + private void apply() { + for (Map.Entry entry : changedKeys.entrySet()) { + try { + table.put(entry.getKey(), innerDb.deserialize(table, entry.getValue())); + } catch (ParseException e) { + //ignored, because it is never thrown. + } + } + /* for (String key : deletedKeys) { + table.remove(key); + }*/ + } + } + + private Map transactions; + private int numberOfTransactions; + public TransactionDatabase(String dbName) throws + IncorrectDbException, IncorrectDbNameException, + IncorrectFileException, TableNotCreatedException, IOException { + transactions = new HashMap<>(); + innerDb = new DatabaseWrapper(dbName); + numberOfTransactions = 0; + } + public Transaction getTransactionById(int id) { + return transactions.get(id); + } + public Transaction[] getTransactions() { + ArrayList transactionList = new ArrayList<>(); + transactions.forEach((key, value) -> transactionList.add(value)); + return transactionList.toArray(new Transaction[0]); + } + public TransactionImpl beginTransaction(String tablename) { + if (tablename == null) { + throw new IllegalArgumentException("Tablename shouldn't be null."); + } + TransactionImpl transaction = new TransactionImpl(tablename, ++numberOfTransactions); + if (transaction.table == null) { + return null; + } + transactions.put(transaction.getId(), transaction); + return transaction; + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/actions/Action.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/actions/Action.java new file mode 100644 index 000000000..cbde3869e --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/actions/Action.java @@ -0,0 +1,47 @@ +package ru.fizteh.fivt.students.torunova.servlet.database.actions; + +import java.io.IOException; +import java.io.PrintWriter; + +/** + * Created by nastya on 21.10.14. + */ +public abstract class Action { + void tooManyArguments(PrintWriter writer) { + writer.println(getDisplayName() + ": too many arguments."); + } + + void tooFewArguments(PrintWriter writer) { + writer.println(getDisplayName() + ": too few arguments."); + } + + boolean checkNumberOfArguments(int expected, int real, PrintWriter writer) { + if (real > expected) { + tooManyArguments(writer); + return false; + } else if (real < expected) { + tooFewArguments(writer); + return false; + } + return true; + } + + public abstract boolean run(String args) + throws IOException; + public abstract String getName(); + + public String getDisplayName() { + return getName(); + } + + public String[] parseArguments(String notParsedArgs) { + if (notParsedArgs == null) { + return null; + } else if (notParsedArgs.equals("")) { + return new String[0]; + } + return notParsedArgs.split("\\s+"); + } +} + + diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/actions/Start.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/actions/Start.java new file mode 100644 index 000000000..ac6d33dc4 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/actions/Start.java @@ -0,0 +1,44 @@ +package ru.fizteh.fivt.students.torunova.servlet.database.actions; + +import ru.fizteh.fivt.students.torunova.servlet.server.HttpServer; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; + +/** + * Created by nastya on 25.12.14. + */ +public class Start extends Action { + private HttpServer server; + private PrintWriter writer; + public Start(HttpServer server, OutputStream os) { + this.server = server; + writer = new PrintWriter(os, true); + } + @Override + public boolean run(String args) throws IOException { + String[] parameters = parseArguments(args); + if (!checkNumberOfArguments(1, parameters.length, writer)) { + return false; + } + int port; + try { + port = Integer.parseInt(parameters[0]); + } catch (NumberFormatException e) { + writer.println("port should be a number"); + return false; + } + try { + server.start(port); + } catch (Exception e) { + writer.println(e.toString()); + } + return true; + } + + @Override + public String getName() { + return "starthttp"; + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/actions/Stop.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/actions/Stop.java new file mode 100644 index 000000000..029635f8e --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/actions/Stop.java @@ -0,0 +1,34 @@ +package ru.fizteh.fivt.students.torunova.servlet.database.actions; + +import ru.fizteh.fivt.students.torunova.servlet.server.HttpServer; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; + +/** + * Created by nastya on 25.12.14. + */ +public class Stop extends Action{ + private HttpServer server; + private PrintWriter writer; + public Stop(HttpServer server, OutputStream os) { + this.server = server; + writer = new PrintWriter(os, true); + } + @Override + public boolean run(String args) throws IOException { + try { + server.stop(); + } catch (Exception e) { + writer.println(e.toString()); + return false; + } + return true; + } + + @Override + public String getName() { + return "stophttp"; + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/IncorrectDbException.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/IncorrectDbException.java new file mode 100644 index 000000000..5b0507eac --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/IncorrectDbException.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.torunova.servlet.database.exceptions; + + +/** + * Created by nastya on 08.11.14. + */ +public class IncorrectDbException extends Exception{ + public IncorrectDbException(String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/IncorrectDbNameException.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/IncorrectDbNameException.java new file mode 100644 index 000000000..59afb4fa0 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/IncorrectDbNameException.java @@ -0,0 +1,10 @@ +package ru.fizteh.fivt.students.torunova.servlet.database.exceptions; + +/** + * Created by nastya on 24.10.14. + */ +public class IncorrectDbNameException extends Exception { + public IncorrectDbNameException(String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/IncorrectFileException.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/IncorrectFileException.java new file mode 100644 index 000000000..2e3f17a07 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/IncorrectFileException.java @@ -0,0 +1,10 @@ +package ru.fizteh.fivt.students.torunova.servlet.database.exceptions; + +/** + * Created by nastya on 19.10.14. + */ +public class IncorrectFileException extends Exception { + public IncorrectFileException(String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/TableNotCreatedException.java b/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/TableNotCreatedException.java new file mode 100644 index 000000000..9599116e2 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/database/exceptions/TableNotCreatedException.java @@ -0,0 +1,13 @@ +package ru.fizteh.fivt.students.torunova.servlet.database.exceptions; + +/** + * Created by nastya on 20.10.14. + */ +public class TableNotCreatedException extends Exception { + public TableNotCreatedException() { + super(); + } + public TableNotCreatedException(String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/interpreter/Shell.java b/src/ru/fizteh/fivt/students/torunova/servlet/interpreter/Shell.java new file mode 100644 index 000000000..4261ab871 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/interpreter/Shell.java @@ -0,0 +1,130 @@ +package ru.fizteh.fivt.students.torunova.servlet.interpreter; + +/** + * Created by nastya on 21.10.14. + */ + +import ru.fizteh.fivt.students.torunova.servlet.database.actions.Action; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.*; +import java.util.regex.Pattern; + +public class Shell { + private static final String COMMAND_DELIMITER = ";"; + private static final String PROMPT = "$ "; + private Map commands; + private Scanner scanner; + private PrintWriter writer; + private boolean interactive; + private String nameOfExitCommand; + + public Shell(Set cmds, InputStream is, OutputStream os, + String nameOfExitCommand, boolean isInteractive) { + commands = new HashMap<>(); + for (Action action : cmds) { + commands.put(action.getName(), action); + } + scanner = new Scanner(is); + writer = new PrintWriter(os, true); + interactive = isInteractive; + this.nameOfExitCommand = nameOfExitCommand; + + } + + public boolean run() { + String nextCommand = null; + String[] functions; + while (true) { + if (interactive) { + writer.print(PROMPT); + writer.flush(); + } + try { + nextCommand = scanner.nextLine(); + } catch (NoSuchElementException e) { + String status = "0"; + try { + commands.get(nameOfExitCommand).run(status); + } catch (IOException e1) { + writer.println("Caught IOException in exit command"); + abort(); + return false; + } catch (ShellInterruptException e2) { + return true; + } + } + functions = nextCommand.split(COMMAND_DELIMITER); + for (String function : functions) { + function = function.trim(); + String name = getNameOfFunction(function); + String parameters = getTheRestOfArguments(function); + if (commands.containsKey(name)) { + boolean res = false; + try { + res = commands.get(name).run(parameters); + } catch (Exception e) { + e.printStackTrace(); + writer.println("Caught " + e.getClass().getSimpleName() + ": " + e.getMessage()); + if (!interactive || name.equals(nameOfExitCommand)) { + abort(); + return false; + } + } + if (!interactive && !res) { + String status = "1"; + try { + commands.get(nameOfExitCommand).run(status); + } catch (IOException e) { + writer.println("Caught IOException in exit command"); + abort(); + return false; + } + } else if (!res) { + break; + } + } else if (!Pattern.matches("\\s*", name)) { + writer.println("Command not found."); + if (!interactive) { + String status = "1"; + try { + commands.get(nameOfExitCommand).run(status); + } catch (IOException e) { + writer.println("Caught IOException in exit command"); + abort(); + return false; + } catch (ShellInterruptException e) { + return true; + } + } else { + break; + } + } + } + } + } + + private String getNameOfFunction(String args) { + if (args.indexOf(" ") < 0) { + return args.trim(); + } + return args.substring(0, args.indexOf(" ")); + } + + private String getTheRestOfArguments(String args) { + if (args.indexOf(" ") < 0) { + return ""; + } + return args.substring(args.indexOf(" ") + 1); + } + + private void abort() { + writer.println("Aborting..."); + } +} + + + diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/interpreter/ShellInterruptException.java b/src/ru/fizteh/fivt/students/torunova/servlet/interpreter/ShellInterruptException.java new file mode 100644 index 000000000..9003ed0dd --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/interpreter/ShellInterruptException.java @@ -0,0 +1,10 @@ +package ru.fizteh.fivt.students.torunova.servlet.interpreter; + +/** + * Created by nastya on 19.12.14. + */ +public class ShellInterruptException extends RuntimeException { + public ShellInterruptException() { + super(); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/server/HttpServer.java b/src/ru/fizteh/fivt/students/torunova/servlet/server/HttpServer.java new file mode 100644 index 000000000..4733eebbe --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/server/HttpServer.java @@ -0,0 +1,55 @@ +package ru.fizteh.fivt.students.torunova.servlet.server; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import ru.fizteh.fivt.students.torunova.servlet.database.Transaction; +import ru.fizteh.fivt.students.torunova.servlet.database.TransactionDatabase; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectDbNameException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.IncorrectFileException; +import ru.fizteh.fivt.students.torunova.servlet.database.exceptions.TableNotCreatedException; +import ru.fizteh.fivt.students.torunova.servlet.server.servlets.*; + +import java.io.IOException; +import java.net.InetSocketAddress; + +/** + * Created by nastya on 25.12.14. + */ +public class HttpServer { + private TransactionDatabase db; + private Server server; + private boolean running; + private static final String LOCAL_HOST = "0.0.0.0"; + public HttpServer(String dbName) throws IncorrectDbException, + IncorrectDbNameException, IncorrectFileException, + TableNotCreatedException, IOException { + db = new TransactionDatabase(dbName); + } + public void start(int bindPort) throws Exception { + if (running) { + throw new IllegalStateException("Server is already running"); + } + server = new Server(new InetSocketAddress(LOCAL_HOST, bindPort)); + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS); + context.addServlet(new ServletHolder(new GetServlet(db)), "/get"); + context.addServlet(new ServletHolder(new PutServlet(db)), "/put"); + context.addServlet(new ServletHolder(new SizeServlet(db)), "/size"); + context.addServlet(new ServletHolder(new CommitServlet(db)), "/commit"); + context.addServlet(new ServletHolder(new RollbackServlet(db)), "/rollback"); + context.addServlet(new ServletHolder(new BeginServlet(db)), "/begin"); + + context.setContextPath("/"); + server.setHandler(context); + server.start(); + } + public void stop() throws Exception { + Transaction[] transactions = db.getTransactions(); + for (Transaction transaction : transactions) { + transaction.rollback(); + } + server.stop(); + running = false; + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/BeginServlet.java b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/BeginServlet.java new file mode 100644 index 000000000..82c339f45 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/BeginServlet.java @@ -0,0 +1,36 @@ +package ru.fizteh.fivt.students.torunova.servlet.server.servlets; + +import ru.fizteh.fivt.students.torunova.servlet.database.Transaction; +import ru.fizteh.fivt.students.torunova.servlet.database.TransactionDatabase; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Created by nastya on 25.12.14. + */ +public class BeginServlet extends MainServlet { + public BeginServlet(TransactionDatabase db) { + super(db); + } + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + String tableName = request.getParameter("table"); + Transaction transaction = null; + try { + transaction = db.beginTransaction(tableName); + } catch (IllegalArgumentException e) { + response.sendError(BAD_REQUEST, e.getMessage()); + } + if (transaction == null) { + response.sendError(BAD_REQUEST, "No such table."); + + } + response.setStatus(OK); + response.getWriter().write("tid = " + transaction.getId()); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/CommitServlet.java b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/CommitServlet.java new file mode 100644 index 000000000..ff9f24b63 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/CommitServlet.java @@ -0,0 +1,42 @@ +package ru.fizteh.fivt.students.torunova.servlet.server.servlets; + +import ru.fizteh.fivt.students.torunova.servlet.database.Transaction; +import ru.fizteh.fivt.students.torunova.servlet.database.TransactionDatabase; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Created by nastya on 25.12.14. + */ +public class CommitServlet extends MainServlet { + public CommitServlet(TransactionDatabase db) { + super(db); + } + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + int transactionId = 0; + try { + transactionId = Integer.parseInt(request.getParameter("tid")); + //checkTransactionId(transactionId); + } catch (IllegalArgumentException e) { + response.sendError(BAD_REQUEST, "Transaction id shoud be a number with 5 digits"); + } + int numberOfCommitedChanges = 0; + Transaction transaction = db.getTransactionById(transactionId); + if (transaction == null) { + response.sendError(BAD_REQUEST, "Transaction not found."); + } + try { + numberOfCommitedChanges = transaction.commit(); + } catch (IOException e) { + response.sendError(SERVER_ERROR, "IOException occured."); + } + response.setStatus(OK); + response.getWriter().write("diff = " + numberOfCommitedChanges); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/GetServlet.java b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/GetServlet.java new file mode 100644 index 000000000..979327ef0 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/GetServlet.java @@ -0,0 +1,47 @@ +package ru.fizteh.fivt.students.torunova.servlet.server.servlets; + +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.students.torunova.servlet.database.Transaction; +import ru.fizteh.fivt.students.torunova.servlet.database.TransactionDatabase; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Created by nastya on 25.12.14. + */ +public class GetServlet extends MainServlet { + public GetServlet(TransactionDatabase db) { + super(db); + } + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + int transactionId = 0; + try { + transactionId = Integer.parseInt(request.getParameter("tid")); + //checkTransactionId(transactionId); + } catch (IllegalArgumentException e) { + response.sendError(BAD_REQUEST, "Transaction id shoud be a number with 5 digits"); + } + String key = request.getParameter("key"); + Transaction transaction = db.getTransactionById(transactionId); + if (transaction == null) { + response.sendError(BAD_REQUEST, "Transaction not found"); + } + Storeable value = null; + try { + value = transaction.get(key); + } catch (RuntimeException e) { + response.sendError(SERVER_ERROR, e.getMessage()); + } + if (value == null) { + response.sendError(BAD_REQUEST, "Key not found"); + } + response.setStatus(OK); + response.getWriter().write(transaction.serialize(value)); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/MainServlet.java b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/MainServlet.java new file mode 100644 index 000000000..9a908c9b4 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/MainServlet.java @@ -0,0 +1,24 @@ +package ru.fizteh.fivt.students.torunova.servlet.server.servlets; + +import ru.fizteh.fivt.students.torunova.servlet.database.TransactionDatabase; + +import javax.servlet.http.HttpServlet; + +/** + * Created by nastya on 25.12.14. + */ +public class MainServlet extends HttpServlet { + protected TransactionDatabase db; + protected static final int OK = 200; + protected static final int BAD_REQUEST = 400; + protected static final int SERVER_ERROR = 500; + protected static final int TID_NUMBER_DIGITS = 5; + MainServlet(TransactionDatabase db) { + this.db = db; + } + void checkTransactionId(int id) { + if (String.valueOf(id).length() != TID_NUMBER_DIGITS) { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/PutServlet.java b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/PutServlet.java new file mode 100644 index 000000000..97bb4c590 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/PutServlet.java @@ -0,0 +1,50 @@ +package ru.fizteh.fivt.students.torunova.servlet.server.servlets; + +import ru.fizteh.fivt.students.torunova.servlet.database.Transaction; +import ru.fizteh.fivt.students.torunova.servlet.database.TransactionDatabase; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Created by nastya on 25.12.14. + */ +public class PutServlet extends MainServlet { + public PutServlet(TransactionDatabase db) { + super(db); + } + + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + int transactionId = 0; + try { + transactionId = Integer.parseInt(request.getParameter("tid")); + //checkTransactionId(transactionId); + } catch (IllegalArgumentException e) { + response.sendError(BAD_REQUEST, "Transaction id shoud be a number with 5 digits"); + } + Transaction transaction = db.getTransactionById(transactionId); + + if (transaction == null) { + response.sendError(BAD_REQUEST, "Transaction not found"); + } + String key = request.getParameter("key"); + String value = request.getParameter("value"); + String oldValue = null; + try { + oldValue = transaction.serialize(transaction.put(key, transaction.deserialize(value))); + } catch (Exception e) { + response.sendError(SERVER_ERROR, e.getMessage()); + } + response.setStatus(OK); + if (oldValue == null) { + response.getWriter().write("new"); + } else { + response.getWriter().write("overwrite " + oldValue); + } + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/RollbackServlet.java b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/RollbackServlet.java new file mode 100644 index 000000000..5fa75c2ab --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/RollbackServlet.java @@ -0,0 +1,41 @@ +package ru.fizteh.fivt.students.torunova.servlet.server.servlets; + +import ru.fizteh.fivt.students.torunova.servlet.database.Transaction; +import ru.fizteh.fivt.students.torunova.servlet.database.TransactionDatabase; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Created by nastya on 25.12.14. + */ +public class RollbackServlet extends MainServlet { + public RollbackServlet(TransactionDatabase db) { + super(db); + } + + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + int transactionId = 0; + try { + transactionId = Integer.parseInt(request.getParameter("tid")); + //checkTransactionId(transactionId); + } catch (IllegalArgumentException e) { + response.sendError(BAD_REQUEST, "Transaction id shoud be a number with 5 digits"); + } + Transaction transaction = db.getTransactionById(transactionId); + if (transaction == null) { + response.sendError(BAD_REQUEST, "Transaction not found"); + } + int numberOfRevertedChanges = 0; + + numberOfRevertedChanges = transaction.rollback(); + + response.setStatus(OK); + response.getWriter().write("diff = " + numberOfRevertedChanges); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/SizeServlet.java b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/SizeServlet.java new file mode 100644 index 000000000..26fd6df37 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/server/servlets/SizeServlet.java @@ -0,0 +1,37 @@ +package ru.fizteh.fivt.students.torunova.servlet.server.servlets; + +import ru.fizteh.fivt.students.torunova.servlet.database.Transaction; +import ru.fizteh.fivt.students.torunova.servlet.database.TransactionDatabase; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Created by nastya on 25.12.14. + */ +public class SizeServlet extends MainServlet { + public SizeServlet(TransactionDatabase db) { + super(db); + } + + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + int transactionId = 0; + try { + transactionId = Integer.parseInt(request.getParameter("tid")); + //checkTransactionId(transactionId); + } catch (IllegalArgumentException e) { + response.sendError(BAD_REQUEST, "Transaction id shoud be a number with 5 digits"); + } + Transaction transaction = db.getTransactionById(transactionId); + if (transaction == null) { + response.sendError(BAD_REQUEST, "Transaction not found"); + } + response.setStatus(OK); + response.getWriter().print(transaction.size()); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/tests/LoggingProxyFactoryTest.java b/src/ru/fizteh/fivt/students/torunova/servlet/tests/LoggingProxyFactoryTest.java new file mode 100644 index 000000000..a351741cb --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/tests/LoggingProxyFactoryTest.java @@ -0,0 +1,90 @@ +package ru.fizteh.fivt.students.torunova.servlet.tests; + +import org.junit.Before; +import org.junit.Test; +import ru.fizteh.fivt.proxy.LoggingProxyFactory; +import ru.fizteh.fivt.students.torunova.servlet.ProxyFactory; + +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +public class LoggingProxyFactoryTest { + LoggingProxyFactory factory = new ProxyFactory(); + TestInterface proxy; + StringWriter writer; + @Before + public void setUp() { + writer = new StringWriter(); + proxy = (TestInterface) factory.wrap(writer, new TestInterfaceImpl(), TestInterface.class); + } + @Test + public void testReturnsPrimitiveType() { + proxy.methodReturnsBool(false); + String buffer = writer.getBuffer().toString(); + //System.out.println(buffer); + assertTrue(buffer.matches(".*name=\"methodReturnsBool\".*false.*false.*")); + proxy.methodReturnsInt(42); + buffer = writer.getBuffer().toString(); + assertTrue(buffer.matches(".*name=\"methodReturnsInt\".*42.*42.*")); + proxy.methodReturnsLong(42L); + buffer = writer.getBuffer().toString(); + assertTrue(buffer.matches(".*name=\"methodReturnsLong\".*42.*42.*")); + proxy.methodReturnsFloat(42.42f); + buffer = writer.getBuffer().toString(); + assertTrue(buffer.matches(".*name=\"methodReturnsFloat\".*42\\.42.*42\\.42.*")); + proxy.methodReturnsDouble(42.42); + buffer = writer.getBuffer().toString(); + assertTrue(buffer.matches(".*name=\"methodReturnsDouble\".*42\\.42.*42\\.42.*")); + proxy.methodReturnsShort((short) 42); + buffer = writer.getBuffer().toString(); + assertTrue(buffer.matches(".*name=\"methodReturnsShort\".*42.*42.*")); + proxy.methodReturnsChar('c'); + buffer = writer.getBuffer().toString(); + assertTrue(buffer.matches(".*name=\"methodReturnsChar\".*c.*c.*")); + proxy.methodReturnsByte((byte) 42); + buffer = writer.getBuffer().toString(); + assertTrue(buffer.matches(".*name=\"methodReturnsByte\".*42.*42.*")); + } + @Test + public void testThrowsException() { + TestException e = new TestException(); + try { + proxy.methodWithException(e); + } catch (TestException e1) { + //ignored + } + String buffer = writer.getBuffer().toString(); + assertTrue(buffer.matches(".*ru.fizteh.fivt.students.torunova.servlet.tests.TestException.*")); + } + @Test + public void testReturnsVoid() { + proxy.methodReturnsVoid(); + String buffer = writer.getBuffer().toString(); + assertTrue(buffer.matches(".*.*.*")); + } + @Test + public void testReturnsNotCyclicIterable() { + List list = new ArrayList<>(); + list.add("Some string"); + list.add(42); + proxy.methodReturnsIterable(list); + String buffer = writer.getBuffer().toString(); + assertTrue(buffer.matches(".*Some string42.*")); + } + @Test + public void testReturnsCyclicIterable() { + List list = new ArrayList<>(); + list.add("Some string"); + list.add(42); + list.add(list); + proxy.methodReturnsIterable(list); + String buffer = writer.getBuffer().toString(); + System.out.println(buffer); + assertTrue(buffer.matches(".*cyclic.*")); + + } + +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/tests/StoreableTest.java b/src/ru/fizteh/fivt/students/torunova/servlet/tests/StoreableTest.java new file mode 100644 index 000000000..e154bf931 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/tests/StoreableTest.java @@ -0,0 +1,104 @@ +package ru.fizteh.fivt.students.torunova.servlet.tests; + +import org.junit.Before; +import org.junit.Test; +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.students.torunova.servlet.database.StoreableType; + +import static org.junit.Assert.assertEquals; + +public class StoreableTest { + StoreableType storeable; + @Before + public void setUp() throws Exception { + storeable = new StoreableType(String.class, + Integer.class, Long.class, Float.class, + Double.class, Byte.class, Boolean.class); + } + @Test + public void testSetColumnAt() throws Exception { + storeable.setColumnAt(0, "Some string"); + assertEquals("Some string", storeable.getColumnAt(0)); + } + @Test(expected = ColumnFormatException.class) + public void testSetColumnAtWithException() throws Exception { + storeable.setColumnAt(0, 12); + } + + @Test + public void testGetColumnAt() throws Exception { + storeable.setColumnAt(1, 42); + assertEquals(42, storeable.getColumnAt(1)); + } + + @Test + public void testGetIntAt() throws Exception { + storeable.setColumnAt(1, 42); + assertEquals(42, (int) storeable.getIntAt(1)); + } + @Test(expected = ColumnFormatException.class) + public void testGetIntAtWithException() throws Exception { + storeable.getIntAt(2); + } + + @Test + public void testGetLongAt() throws Exception { + storeable.setColumnAt(2, 42L); + assertEquals(42, (long) storeable.getLongAt(2)); + } + @Test(expected = ColumnFormatException.class) + public void testGetLongAtWithException() throws Exception { + storeable.getLongAt(4); + } + + @Test + public void testGetByteAt() throws Exception { + storeable.setColumnAt(5, (byte) 42); + assertEquals(42, (byte) storeable.getByteAt(5)); + } + @Test(expected = ColumnFormatException.class) + public void testGetByteAtWithException() throws Exception { + storeable.getByteAt(0); + } + + @Test + public void testGetFloatAt() throws Exception { + storeable.setColumnAt(3, 1.42f); + assertEquals(1.42, (float) storeable.getFloatAt(3), 0.001); + } + @Test(expected = ColumnFormatException.class) + public void testGetFloatAtWithException() throws Exception { + storeable.getFloatAt(5); + } + + @Test + public void testGetDoubleAt() throws Exception { + storeable.setColumnAt(4, 1.42); + assertEquals(1.42, (double) storeable.getDoubleAt(4), 0.001); + } + + @Test(expected = ColumnFormatException.class) + public void testGetDoubleAtWithException() throws Exception { + storeable.getDoubleAt(5); + } + + @Test + public void testGetBooleanAt() throws Exception { + storeable.setColumnAt(6, false); + assertEquals(false, storeable.getBooleanAt(6)); + } + @Test(expected = ColumnFormatException.class) + public void testGetBooleanAtWithException() throws Exception { + storeable.getBooleanAt(5); + } + + @Test + public void testGetStringAt() throws Exception { + storeable.setColumnAt(0, "Some string"); + assertEquals("Some string", storeable.getStringAt(0)); + } + @Test(expected = ColumnFormatException.class) + public void testGetStringAtWithException() throws Exception { + storeable.getStringAt(5); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/tests/TableProviderFactoryTest.java b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TableProviderFactoryTest.java new file mode 100644 index 000000000..a058b1c79 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TableProviderFactoryTest.java @@ -0,0 +1,42 @@ +package ru.fizteh.fivt.students.torunova.servlet.tests; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import ru.fizteh.fivt.storage.structured.TableProviderFactory; +import ru.fizteh.fivt.students.torunova.servlet.database.DatabaseFactoryWrapper; +import ru.fizteh.fivt.students.torunova.servlet.database.DatabaseWrapper; + +import java.io.File; + +import static org.junit.Assert.assertEquals; + +public class TableProviderFactoryTest { + TableProviderFactory tableProviderFactory; + File testDirectory; + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + @Before + public void setUp() throws Exception { + tableProviderFactory = new DatabaseFactoryWrapper(); + testDirectory = folder.newFolder("db"); + } + @Test + public void testCreate() throws Exception { + DatabaseWrapper db = new DatabaseWrapper(testDirectory.getAbsolutePath()); + assertEquals(db, tableProviderFactory.create(testDirectory.getAbsolutePath())); + } + @Test(expected = IllegalArgumentException.class) + public void testCreateTableProviderWithNullName() throws Exception { + tableProviderFactory.create(null); + } + @Test(expected = IllegalArgumentException.class) + public void testCreateTableProviderWithIllegalName1() throws Exception { + tableProviderFactory.create("."); + } + @Test(expected = IllegalArgumentException.class) + public void testCreateTableProviderWithIllegalName2() throws Exception { + tableProviderFactory.create(".."); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/tests/TableProviderTest.java b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TableProviderTest.java new file mode 100644 index 000000000..435340f4e --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TableProviderTest.java @@ -0,0 +1,110 @@ +package ru.fizteh.fivt.students.torunova.servlet.tests; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import ru.fizteh.fivt.students.torunova.servlet.database.StoreableType; +import ru.fizteh.fivt.students.torunova.servlet.database.TableWrapper; +import ru.fizteh.fivt.students.torunova.servlet.database.DatabaseWrapper; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class TableProviderTest { + DatabaseWrapper db; + File testDirectory; + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + @Before + public void setUp() throws Exception { + testDirectory = folder.newFolder("db"); + db = new DatabaseWrapper(testDirectory.getAbsolutePath()); + } + @Test + public void testGetTable() throws Exception { + db.createTable("table", Arrays.asList(String.class, Integer.class, Boolean.class)); + File table = new File(testDirectory, "table"); + TableWrapper t = new TableWrapper(table.getAbsolutePath(), db, + String.class, Integer.class, Boolean.class); + assertEquals(t, db.getTable("table")); + } + + @Test + public void testCreateNotExistingTable() throws Exception { + File table = new File(testDirectory, "table"); + TableWrapper t = new TableWrapper(table.getAbsolutePath(), db, + String.class, Integer.class, Boolean.class); + assertEquals(t, db.createTable("table", Arrays.asList(String.class, Integer.class, Boolean.class))); + } + + @Test + public void testCreateExistingTable() throws Exception { + db.createTable("table", Arrays.asList(String.class, Integer.class, Boolean.class)); + assertEquals(null, db.createTable("table", Arrays.asList(String.class, Integer.class, Boolean.class))); + } + + @Test + public void testRemoveExistingTable() throws Exception { + db.createTable("table", Arrays.asList(String.class, Integer.class, Boolean.class)); + db.removeTable("table"); + assertNotEquals(null, db.createTable("table", Arrays.asList(String.class, + Integer.class, Boolean.class))); + } + + @Test(expected = IllegalStateException.class) + public void testRemoveNotExistingTable() throws Exception { + db.removeTable("table"); + } + + @Test + public void testDeserialize() throws Exception { + TableWrapper table = (TableWrapper) db.createTable("table", Arrays.asList( + String.class, Integer.class, Boolean.class)); + StoreableType value = (StoreableType) db.createFor(table, Arrays.asList("String value", 42, false)); + assertEquals(value, db.deserialize(table, "[\"String value\", 42, false]")); + } + + @Test + public void testSerialize() throws Exception { + TableWrapper table = (TableWrapper) db.createTable("table", Arrays.asList( + String.class, Integer.class, Boolean.class)); + StoreableType value = (StoreableType) db.createFor(table, Arrays.asList("String value", 42, false)); + assertEquals("[\"String value\", 42, false]", db.serialize(table, value)); + } + + @Test + public void testCreateFor() throws Exception { + TableWrapper table = (TableWrapper) db.createTable("table", Arrays.asList( + String.class, Integer.class, Boolean.class)); + StoreableType value = new StoreableType(String.class, Integer.class, Boolean.class); + value.setColumnAt(0, "Some string"); + value.setColumnAt(1, 42); + value.setColumnAt(2, false); + assertEquals(value, db.createFor(table, Arrays.asList("Some string", 42, false))); + } + + @Test + public void testCreateFor1() throws Exception { + TableWrapper table = (TableWrapper) db.createTable("table", Arrays.asList( + String.class, Integer.class, Boolean.class)); + StoreableType value = new StoreableType(String.class, Integer.class, Boolean.class); + assertEquals(value, db.createFor(table)); + } + + @Test + public void testGetTableNames() throws Exception { + db.createTable("table 1", Arrays.asList(String.class, Integer.class, Boolean.class)); + db.createTable("table 2", Arrays.asList(Double.class, String.class, Long.class)); + List expected = Arrays.asList("table 1", "table 2"); + List real = db.getTableNames(); + Collections.sort(expected); + Collections.sort(real); + assertEquals(expected, real); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/tests/TableTest.java b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TableTest.java new file mode 100644 index 000000000..a8b78681b --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TableTest.java @@ -0,0 +1,113 @@ +package ru.fizteh.fivt.students.torunova.servlet.tests; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.students.torunova.servlet.database.DatabaseWrapper; +import ru.fizteh.fivt.students.torunova.servlet.database.TableWrapper; + + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class TableTest{ + + TableWrapper testTable; + DatabaseWrapper db; + private static final Object[] VALUE = {"String value", 42, false, 1.42f, 42L, (byte) 42, 1.42}; + private static final Object[] VALUE_1 = {"String value 1", 38, true, 1.38f, 38L, (byte) 38, 1.38}; + private static final Object[] VALUE_2 = {"String value 2", 31, true, 1.31f, null, (byte) 31, 1.31}; + private static final Object[] WRONG_VALUE = {"String value", false, 23, 1.42f, null, (byte) 42, 1.38}; + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + @Before + public void setUp() throws Exception { + File testDirectory = folder.newFolder("db"); + db = new DatabaseWrapper(testDirectory.getAbsolutePath()); + testTable = (TableWrapper) db.createTable("table", Arrays.asList( + String.class, Integer.class, Boolean.class, Float.class, + Long.class, Byte.class, Double.class)); + } + + @Test + public void testPutNew() throws Exception { + testTable.put("key", db.createFor(testTable, Arrays.asList(VALUE))); + assertEquals(db.createFor(testTable, Arrays.asList(VALUE)), testTable.get("key")); + } + @Test public void testPutExisting() throws Exception { + testTable.put("key", db.createFor(testTable, Arrays.asList(VALUE_1))); + assertEquals(db.createFor(testTable, Arrays.asList(VALUE_1)), testTable.put("key", + db.createFor(testTable, Arrays.asList(VALUE_2)))); + } + @Test(expected = ColumnFormatException.class) + public void testPutWrongValue() { + testTable.put("key", db.createFor(testTable, Arrays.asList(WRONG_VALUE))); + } + + @Test + public void testRemoveExistingValue() throws Exception { + testTable.put("key", db.createFor(testTable, Arrays.asList(VALUE))); + assertEquals(db.createFor(testTable, Arrays.asList(VALUE)), testTable.remove("key")); + } + + @Test + public void testRemoveNotExistingValue() throws Exception { + assertEquals(null, testTable.remove("key")); + } + + @Test + public void testSize() throws Exception { + testTable.put("key 1", db.createFor(testTable, Arrays.asList(VALUE_1))); + testTable.put("key 2", db.createFor(testTable, Arrays.asList(VALUE_2))); + testTable.remove("key 1"); + assertEquals(1, testTable.size()); + } + + @Test + public void testList() throws Exception { + testTable.put("key 1", db.createFor(testTable, Arrays.asList(VALUE_1))); + testTable.put("key 2", db.createFor(testTable, Arrays.asList(VALUE_2))); + List expectedKeys = Arrays.asList("key 1", "key 2"); + List realKeys = testTable.list(); + Collections.sort(expectedKeys); + Collections.sort(realKeys); + assertEquals(expectedKeys, realKeys); + } + + @Test + public void testCommit() throws Exception { + testTable.put("key 1", db.createFor(testTable, Arrays.asList(VALUE_1))); + testTable.put("key 2", db.createFor(testTable, Arrays.asList(VALUE_2))); + assertEquals(2, testTable.commit()); + } + + @Test + public void testRollback() throws Exception { + testTable.put("key 1", db.createFor(testTable, Arrays.asList(VALUE_1))); + testTable.put("key 2", db.createFor(testTable, Arrays.asList(VALUE_2))); + assertEquals(2, testTable.rollback()); + } + + @Test + public void testGetNumberOfUncommittedChanges() throws Exception { + testTable.put("key 1", db.createFor(testTable, Arrays.asList(VALUE_1))); + testTable.put("key 2", db.createFor(testTable, Arrays.asList(VALUE_2))); + assertEquals(2, testTable.getNumberOfUncommittedChanges()); + } + + @Test + public void testGetColumnsCount() throws Exception { + assertEquals(7, testTable.getColumnsCount()); + } + + @Test + public void testGetColumnType() throws Exception { + assertEquals(Boolean.class, testTable.getColumnType(2)); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/tests/TestException.java b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TestException.java new file mode 100644 index 000000000..d312e186e --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TestException.java @@ -0,0 +1,10 @@ +package ru.fizteh.fivt.students.torunova.servlet.tests; + +/** + * Created by nastya on 21.12.14. + */ +public class TestException extends Exception { + TestException() { + super(); + } +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/tests/TestInterface.java b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TestInterface.java new file mode 100644 index 000000000..20468232c --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TestInterface.java @@ -0,0 +1,19 @@ +package ru.fizteh.fivt.students.torunova.servlet.tests; + + +/** + * Created by nastya on 08.12.14. + */ +public interface TestInterface { + Iterable methodReturnsIterable(Iterable iterable); + void methodReturnsVoid(); + Object methodWithException(TestException e) throws TestException; + int methodReturnsInt(int i); + short methodReturnsShort(short sh); + long methodReturnsLong(long l); + float methodReturnsFloat(float f); + double methodReturnsDouble(double d); + char methodReturnsChar(char ch); + byte methodReturnsByte(byte b); + boolean methodReturnsBool(boolean bool); +} diff --git a/src/ru/fizteh/fivt/students/torunova/servlet/tests/TestInterfaceImpl.java b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TestInterfaceImpl.java new file mode 100644 index 000000000..7c7377b05 --- /dev/null +++ b/src/ru/fizteh/fivt/students/torunova/servlet/tests/TestInterfaceImpl.java @@ -0,0 +1,61 @@ +package ru.fizteh.fivt.students.torunova.servlet.tests; + +/** + * Created by nastya on 08.12.14. + */ +public class TestInterfaceImpl implements TestInterface { + @Override + public Iterable methodReturnsIterable(Iterable iterable) { + return iterable; + } + + @Override + public void methodReturnsVoid() { + //doing nothing. + } + + @Override + public Object methodWithException(TestException e) throws TestException { + throw e; + } + + @Override + public int methodReturnsInt(int i) { + return i; + } + + @Override + public short methodReturnsShort(short sh) { + return sh; + } + + @Override + public long methodReturnsLong(long l) { + return l; + } + + @Override + public float methodReturnsFloat(float f) { + return f; + } + + @Override + public double methodReturnsDouble(double d) { + return d; + } + + @Override + public char methodReturnsChar(char ch) { + return ch; + } + + @Override + public byte methodReturnsByte(byte b) { + return b; + } + + @Override + public boolean methodReturnsBool(boolean bool) { + return bool; + } +}