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));
+ }
}