diff --git a/pom.xml b/pom.xml index ac574cc..ba0d124 100644 --- a/pom.xml +++ b/pom.xml @@ -1,46 +1,47 @@ - ru.hh.school - 4.0.0 - java-stdlib - jar - Homework exercise for 'Java Standard Library' lecture - 1.0-SNAPSHOT + ru.hh.school + 4.0.0 + java-stdlib + jar + Homework exercise for 'Java Standard Library' lecture + 1.0-SNAPSHOT - - - - org.apache.maven.plugins - maven-shade-plugin - 1.4 - - - package - - shade - - - ${project.artifactId} - - - - ru.hh.school.stdlib.Launcher - - - - - - - - - + + + + org.apache.maven.plugins + maven-shade-plugin + 1.4 + + + package + + shade + + + ${project.artifactId} + + + + ru.hh.school.stdlib.Launcher + + + + + + + + + - - - junit - junit - 4.7 - test - - + + + junit + junit + 4.7 + test + + diff --git a/src/main/java/ru/hh/school/stdlib/ClientHandler.java b/src/main/java/ru/hh/school/stdlib/ClientHandler.java new file mode 100644 index 0000000..4438390 --- /dev/null +++ b/src/main/java/ru/hh/school/stdlib/ClientHandler.java @@ -0,0 +1,65 @@ +package ru.hh.school.stdlib; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.Socket; +import java.util.Scanner; +import java.util.concurrent.TimeUnit; + +public class ClientHandler implements Runnable { + private Socket incoming; + private final Substitutor3000 sbst; + private final Server server; + + + public ClientHandler(Socket i, Server srv, Substitutor3000 sb) { + incoming = i; + server = srv; + sbst = sb; + } + + @Override + public void run() { + try { + try { + Scanner in = new Scanner(incoming.getInputStream()); + PrintWriter out = new PrintWriter(incoming.getOutputStream()); + String line = in.nextLine().trim(); + String[] req = line.split(" ", 3); + int len = req.length; + if (req[0].equals("GET")) { + TimeUnit.MILLISECONDS.sleep(server.getSleepTime()); + if (len >= 2) { + try { + String temp; + temp = sbst.get(req[1], null); + out.println("VALUE"); + out.println(temp); + } catch (InfiniteRecursionException e) { + out.println("ERROR " + e.getMessage()); + } + } else out.println("ERROR GET syntax: GET key"); + } else if ((req[0].equals("SET")) && (req[1].equals("SLEEP"))) { + if (len == 3) { + server.setSleepTime(Long.valueOf(req[2])); + } else out.println("ERROR SET syntax: SET SLEEP sleeptime"); + } else if (req[0].equals("PUT")) { + TimeUnit.MILLISECONDS.sleep(server.getSleepTime()); + if (len == 3) { + sbst.put(req[1], req[2]); + out.println("OK"); + } else out.println("ERROR PUT syntax: PUT key value"); + } else out.println("ERROR Unsupported request! We accept GET, PUT or SET SLEEP here!"); + out.flush(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + incoming.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + + } + +} diff --git a/src/main/java/ru/hh/school/stdlib/InfiniteRecursionException.java b/src/main/java/ru/hh/school/stdlib/InfiniteRecursionException.java new file mode 100644 index 0000000..1c7eddb --- /dev/null +++ b/src/main/java/ru/hh/school/stdlib/InfiniteRecursionException.java @@ -0,0 +1,7 @@ +package ru.hh.school.stdlib; + +public class InfiniteRecursionException extends Exception { + public InfiniteRecursionException(String s) { + super(s); + } +} diff --git a/src/main/java/ru/hh/school/stdlib/Launcher.java b/src/main/java/ru/hh/school/stdlib/Launcher.java index 1bee1a8..92639b5 100644 --- a/src/main/java/ru/hh/school/stdlib/Launcher.java +++ b/src/main/java/ru/hh/school/stdlib/Launcher.java @@ -4,26 +4,27 @@ import java.net.InetSocketAddress; public class Launcher { - public static void main(String[] args) throws IOException { - String host; - int port; - try { - if (args.length == 0) { - host = "127.0.0.1"; - port = 9129; - } else if (args.length == 3) { - host = args[1]; - port = Integer.parseInt(args[2]); - } else { - throw new IllegalArgumentException(); - } - } catch (Exception e) { - System.err.printf("Usage: %s [host port]%n", args[0]); - System.exit(1); - return; // попробуйте закомментировать этот return + public static void main(String[] args) throws IOException { + String host; + int port; + try { + if (args.length == 0) { + host = "127.0.0.1"; + port = 9129; + } else if (args.length == 2) { + host = args[0]; + port = Integer.parseInt(args[1]); + } else { + throw new IllegalArgumentException(); + } + } catch (Exception e) { + System.err.printf("Usage: %s [host port]%n", args[0]); + for (String s : args) System.out.println(s); + e.printStackTrace(); + System.exit(1); + return; // попробуйте закомментировать этот return + } + InetSocketAddress addr = InetSocketAddress.createUnresolved(host, port); + new Server(addr).run(); } - InetSocketAddress addr = InetSocketAddress.createUnresolved(host, port); - - new Server(addr).run(); - } } diff --git a/src/main/java/ru/hh/school/stdlib/Server.java b/src/main/java/ru/hh/school/stdlib/Server.java index 8284c32..a4ec112 100644 --- a/src/main/java/ru/hh/school/stdlib/Server.java +++ b/src/main/java/ru/hh/school/stdlib/Server.java @@ -2,17 +2,46 @@ import java.io.IOException; import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class Server { - public Server(InetSocketAddress addr) { - throw new UnsupportedOperationException(); - } + private ServerSocket s; + private Substitutor3000 sbst; + private Long sleepTime; - public void run() throws IOException { - throw new UnsupportedOperationException(); - } + public Server(InetSocketAddress addr) { + try { + s = new ServerSocket(addr.getPort()); + sbst = new Substitutor3000(); + sleepTime = 0L; + } catch (Exception e) { + System.err.print("ERROR "); + e.printStackTrace(); + System.exit(1); + } + } - public int getPort() { - throw new UnsupportedOperationException(); - } + public void run() throws IOException { + System.out.printf("Server started on port %d%n", this.getPort()); + ExecutorService exec = Executors.newCachedThreadPool(); + while (true) { + Socket incoming = s.accept(); + exec.execute(new ClientHandler(incoming, this, sbst)); + } + } + + public int getPort() { + return s.getLocalPort(); + } + + public synchronized Long getSleepTime() { + return sleepTime; + } + + public synchronized void setSleepTime(Long sleepTime) { + this.sleepTime = sleepTime; + } } diff --git a/src/main/java/ru/hh/school/stdlib/Substitutor3000.java b/src/main/java/ru/hh/school/stdlib/Substitutor3000.java index 728598c..fa342fd 100644 --- a/src/main/java/ru/hh/school/stdlib/Substitutor3000.java +++ b/src/main/java/ru/hh/school/stdlib/Substitutor3000.java @@ -1,11 +1,39 @@ package ru.hh.school.stdlib; +import java.util.HashMap; +import java.util.HashSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class Substitutor3000 { - public void put(String key, String value) { - throw new UnsupportedOperationException(); - } - public String get(String key) { - throw new UnsupportedOperationException(); - } + private HashMap storage; + + public Substitutor3000() { + storage = new HashMap(); + } + + public synchronized void put(String key, String value) { + storage.put(key, value); + } + + public synchronized String get(String key, HashSet inProgress) throws InfiniteRecursionException { + if (inProgress == null) + inProgress = new HashSet(); + else if (inProgress.contains(key)) + throw new InfiniteRecursionException("Infinite recursion."); + String out = storage.get(key); + if (out == null) return ""; + inProgress.add(key); + Pattern pattern = Pattern.compile("\\p{Sc}\\{(\\S+)\\}"); + Matcher matcher = pattern.matcher(out); + while (matcher.find()) { + String part = get(matcher.group(1), inProgress); + if (part == null) + part = ""; + out = out.replace(matcher.group(0), part); + } + inProgress.remove(key); + return out; + } } diff --git a/src/test/java/ru/hh/school/stdlib/BaseFunctionalTest.java b/src/test/java/ru/hh/school/stdlib/BaseFunctionalTest.java index 5fb134c..5cd3493 100644 --- a/src/test/java/ru/hh/school/stdlib/BaseFunctionalTest.java +++ b/src/test/java/ru/hh/school/stdlib/BaseFunctionalTest.java @@ -5,36 +5,36 @@ import java.net.Socket; public class BaseFunctionalTest { - private static Server server; + private static Server server; - protected BaseFunctionalTest() { - synchronized (BaseFunctionalTest.class) { - try { - if (server == null) { - server = new Server(new InetSocketAddress("127.0.0.1", 0)); - new Thread(new Runnable() { - public void run() { - try { - server.run(); - } catch (IOException e) { + protected BaseFunctionalTest() { + synchronized (BaseFunctionalTest.class) { + try { + if (server == null) { + server = new Server(new InetSocketAddress("127.0.0.1", 0)); + new Thread(new Runnable() { + public void run() { + try { + server.run(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }).start(); + Thread.sleep(100); + System.out.printf("Server started on port %d%n", server.getPort()); + } + } catch (Exception e) { throw new RuntimeException(e); - } } - }).start(); - Thread.sleep(100); - System.out.printf("Server started on port %d%n", server.getPort()); } - } catch (Exception e) { - throw new RuntimeException(e); - } } - } - - protected Socket connect() { - try { - return new Socket("127.0.0.1", server.getPort()); - } catch (IOException e) { - throw new RuntimeException(e); + + protected Socket connect() { + try { + return new Socket("127.0.0.1", server.getPort()); + } catch (IOException e) { + throw new RuntimeException(e); + } } - } } diff --git a/src/test/java/ru/hh/school/stdlib/GetPutTest.java b/src/test/java/ru/hh/school/stdlib/GetPutTest.java new file mode 100644 index 0000000..12eb752 --- /dev/null +++ b/src/test/java/ru/hh/school/stdlib/GetPutTest.java @@ -0,0 +1,56 @@ +package ru.hh.school.stdlib; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.*; +import java.net.Socket; + + +public class GetPutTest extends BaseFunctionalTest { + @Test + public void getPut() throws IOException { + Socket s = connect(); + + Writer out = new PrintWriter(s.getOutputStream()); + BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("PUT k1 one\n").flush(); + Assert.assertEquals("OK", in.readLine()); + + s = connect(); + out = new PrintWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("GET k1\n").flush(); + Assert.assertEquals("VALUE", in.readLine()); + Assert.assertEquals("one", in.readLine()); + + s = connect(); + out = new PrintWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("PUT keys ${k1} ${k2}\n").flush(); + Assert.assertEquals("OK", in.readLine()); + + s = connect(); + out = new PrintWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("GET keys\n").flush(); + Assert.assertEquals("VALUE", in.readLine()); + Assert.assertEquals("one ", in.readLine()); + + s = connect(); + out = new PrintWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("PUT k2 two\n").flush(); + Assert.assertEquals("OK", in.readLine()); + + s = connect(); + out = new PrintWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("GET keys\n").flush(); + Assert.assertEquals("VALUE", in.readLine()); + Assert.assertEquals("one two", in.readLine()); + + s.close(); + } + +} diff --git a/src/test/java/ru/hh/school/stdlib/RecursionTest.java b/src/test/java/ru/hh/school/stdlib/RecursionTest.java new file mode 100644 index 0000000..43ec6c6 --- /dev/null +++ b/src/test/java/ru/hh/school/stdlib/RecursionTest.java @@ -0,0 +1,39 @@ +package ru.hh.school.stdlib; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.*; +import java.net.Socket; + +public class RecursionTest extends BaseFunctionalTest { + //This one tests protection from infinite recursion + @Test + public void recursivePut() throws IOException { + Socket s = connect(); + Writer out = new PrintWriter(s.getOutputStream()); + BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("PUT keys3 ${l1} ${l2}\n").flush(); + Assert.assertEquals("OK", in.readLine()); + + s = connect(); + out = new PrintWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("PUT l2 two\n").flush(); + Assert.assertEquals("OK", in.readLine()); + + s = connect(); + out = new PrintWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("PUT l1 ${k4} ${keys3}\n").flush(); + Assert.assertEquals("OK", in.readLine()); + + s = connect(); + out = new PrintWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("GET keys3\n").flush(); + Assert.assertEquals("Error: Infinite recursion.", in.readLine()); + + s.close(); + } +} diff --git a/src/test/java/ru/hh/school/stdlib/RecursiveGet.java b/src/test/java/ru/hh/school/stdlib/RecursiveGet.java new file mode 100644 index 0000000..61d8c76 --- /dev/null +++ b/src/test/java/ru/hh/school/stdlib/RecursiveGet.java @@ -0,0 +1,40 @@ +package ru.hh.school.stdlib; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.*; +import java.net.Socket; + +public class RecursiveGet extends BaseFunctionalTest { + //This is not a test for infinite recursion protection. + @Test + public void recursiveGet() throws IOException { + Socket s = connect(); + Writer out = new PrintWriter(s.getOutputStream()); + BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("PUT keys2 ${k5} ${k2}\n").flush(); + Assert.assertEquals("OK", in.readLine()); + + s = connect(); + out = new PrintWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("PUT k4 two\n").flush(); + Assert.assertEquals("OK", in.readLine()); + + s = connect(); + out = new PrintWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("PUT k5 k1 ${k4}\n").flush(); + Assert.assertEquals("OK", in.readLine()); + + s = connect(); + out = new PrintWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + out.append("GET keys2\n").flush(); + Assert.assertEquals("VALUE", in.readLine()); + Assert.assertEquals("k1 two two", in.readLine()); + + s.close(); + } +} diff --git a/src/test/java/ru/hh/school/stdlib/SimpleGetTest.java b/src/test/java/ru/hh/school/stdlib/SimpleGetTest.java index 0fe1b49..72fc795 100644 --- a/src/test/java/ru/hh/school/stdlib/SimpleGetTest.java +++ b/src/test/java/ru/hh/school/stdlib/SimpleGetTest.java @@ -1,26 +1,23 @@ package ru.hh.school.stdlib; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.io.Writer; -import java.net.Socket; import org.junit.Assert; import org.junit.Test; +import java.io.*; +import java.net.Socket; + public class SimpleGetTest extends BaseFunctionalTest { - @Test - public void simpleGet() throws IOException { - Socket s = connect(); + @Test + public void simpleGet() throws IOException { + Socket s = connect(); + + Writer out = new PrintWriter(s.getOutputStream()); + out.append("GET k3\n").flush(); + BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); + + Assert.assertEquals("VALUE", in.readLine()); + Assert.assertEquals("", in.readLine()); - Writer out = new PrintWriter(s.getOutputStream()); - out.append("GET k1\n").flush(); - BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); - - Assert.assertEquals("VALUE", in.readLine()); - Assert.assertEquals("", in.readLine()); - - s.close(); - } + s.close(); + } } diff --git a/src/test/java/ru/hh/school/stdlib/Substitutor3000Test.java b/src/test/java/ru/hh/school/stdlib/Substitutor3000Test.java index 3d63300..48e7f17 100644 --- a/src/test/java/ru/hh/school/stdlib/Substitutor3000Test.java +++ b/src/test/java/ru/hh/school/stdlib/Substitutor3000Test.java @@ -4,21 +4,21 @@ import org.junit.Test; public class Substitutor3000Test { - @Test - public void replacement() { - Substitutor3000 sbst = new Substitutor3000(); - sbst.put("k1", "one"); - sbst.put("k2", "two"); - sbst.put("keys", "1: ${k1}, 2: ${k2}"); - - Assert.assertEquals("1: one, 2: two", sbst.get("keys")); - } + @Test + public void replacement() throws Exception { + Substitutor3000 sbst = new Substitutor3000(); + sbst.put("k1", "one"); + sbst.put("k2", "two"); + sbst.put("keys", "1: ${k1}, 2: ${k2}"); - @Test - public void emptyReplacement() { - Substitutor3000 sbst = new Substitutor3000(); - sbst.put("k", "bla-${inexistent}-bla"); - - Assert.assertEquals("bla--bla", sbst.get("k")); - } + Assert.assertEquals("1: one, 2: two", sbst.get("keys", null)); + } + + @Test + public void emptyReplacement() throws Exception { + Substitutor3000 sbst = new Substitutor3000(); + sbst.put("k", "bla-${inexistent}-bla"); + + Assert.assertEquals("bla--bla", sbst.get("k", null)); + } }