diff --git a/homework-g597-moiseev/pom.xml b/homework-g597-moiseev/pom.xml index fc6d5751e..fbb982382 100644 --- a/homework-g597-moiseev/pom.xml +++ b/homework-g597-moiseev/pom.xml @@ -11,6 +11,10 @@ homework-g597-moiseev + + 1.4.2.RELEASE + + ru.mipt.java2016 @@ -24,6 +28,50 @@ 1.0.0 test + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + + + + org.springframework.boot + spring-boot-starter-jdbc + ${spring.boot.version} + + + + org.springframework.boot + spring-boot-starter-security + ${spring.boot.version} + + + + com.zaxxer + HikariCP + 2.5.1 + + + + com.h2database + h2 + 1.4.193 + + + + spring-releases + https://repo.spring.io/libs-release + + + + + + spring-releases + https://repo.spring.io/libs-release + + + \ No newline at end of file diff --git a/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task1/StackCalculator.java b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task1/StackCalculator.java index fe1f73d4b..8385ed16b 100644 --- a/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task1/StackCalculator.java +++ b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task1/StackCalculator.java @@ -164,7 +164,7 @@ private double calculateValueOfPostfixLine(String expression) throws ParsingExce } else { throw new ParsingException("Invalid expression"); } - } else if (Pattern.matches("[-+]?[0-9]*\\.?[0-9]", s)) { + } else if (Pattern.matches("[-+]?[0-9]*\\.?[0-9]*", s)) { double current = Double.parseDouble(s); // Иначе это число stack.push(current); // Кладем его в стек } else { diff --git a/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/CalculationDao.java b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/CalculationDao.java new file mode 100644 index 000000000..fd99e8c3d --- /dev/null +++ b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/CalculationDao.java @@ -0,0 +1,205 @@ +package ru.mipt.java2016.homework.g597.moiseev.task4; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; +import ru.mipt.java2016.homework.base.task1.ParsingException; + +import javax.annotation.PostConstruct; +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +@Repository +public class CalculationDao { + private static final Logger LOG = LoggerFactory.getLogger(CalculationDao.class); + + @Autowired + private DataSource dataSource; + + private JdbcTemplate jdbcTemplate; + + @PostConstruct + public void postConstruct() { + jdbcTemplate = new JdbcTemplate(dataSource, false); + initSchema(); + } + + public void initSchema() { + LOG.trace("Initializing schema"); + jdbcTemplate.execute("CREATE SCHEMA IF NOT EXISTS billing"); + jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS billing.users " + + "(username VARCHAR PRIMARY KEY, password VARCHAR, enabled BOOLEAN)"); + jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS billing.variables " + + "(username VARCHAR, name VARCHAR, value DOUBLE)"); + jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS billing.functions " + + "(username VARCHAR, name VARCHAR, arguments VARCHAR, expression VARCHAR)"); + addUserIfNotExists("username", "password", true); + } + + boolean addUserIfNotExists(String username, String password, boolean enabled) { + try { + loadUser(username); + return false; + } catch (EmptyResultDataAccessException e) { + jdbcTemplate.update("INSERT INTO billing.users VALUES (?, ?, ?)", + new Object[]{username, password, enabled}); + return true; + } + } + + public Double getVariable(String username, String variable) { + return jdbcTemplate.queryForObject( + "SELECT username, name, value FROM billing.variables WHERE username = ? AND name = ?", + new Object[]{username, variable}, + new RowMapper() { + @Override + public Double mapRow(ResultSet rs, int rowNum) throws SQLException { + return rs.getDouble("value"); + } + } + ); + } + + Map getVariables(String username) { + try { + return jdbcTemplate.queryForObject( + "SELECT username, name, value FROM billing.variables WHERE username = ?", + new Object[]{username}, + new RowMapper>() { + @Override + public HashMap mapRow(ResultSet rs, int rowNum) throws SQLException { + HashMap result = new HashMap<>(); + while (true) { + result.put(rs.getString("name"), rs.getDouble("value")); + if (!rs.next()) { + break; + } + } + return result; + } + } + ); + } catch (EmptyResultDataAccessException e) { + HashMap result = new HashMap<>(); + return result; + } + } + + boolean deleteVariable(String username, String name) throws ParsingException { + try { + getVariable(username, name); + jdbcTemplate.update("DELETE FROM billing.variables WHERE username = ? AND name = ?", + new Object[]{username, name}); + return true; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + void addVariable(String username, String name, Double value) throws ParsingException { + try { + getVariable(username, name); + jdbcTemplate.update("DELETE FROM billing.variables WHERE username = ? AND name = ?", + new Object[]{username, name}); + jdbcTemplate.update("INSERT INTO billing.variables VALUES (?, ?, ?)", + new Object[]{username, name, value}); + } catch (EmptyResultDataAccessException e) { + jdbcTemplate.update("INSERT INTO billing.variables VALUES (?, ?, ?)", + new Object[]{username, name, value}); + } + } + + public Function getFunction(String username, String function) { + return jdbcTemplate.queryForObject( + "SELECT username, name, arguments, expression FROM billing.functions WHERE username = ? AND name = ?", + new Object[]{username, function}, + new RowMapper() { + @Override + public Function mapRow(ResultSet rs, int rowNum) throws SQLException { + String name = rs.getString("name"); + List arguments = Arrays.asList(rs.getString("arguments").split(" ")); + String expression = rs.getString("expression"); + return new Function(name, arguments, expression); + } + } + ); + } + + Map getFunctions(String username) { + try { + return jdbcTemplate.queryForObject( + "SELECT username, name, arguments, expression FROM billing.functions WHERE username = ?", + new Object[]{username}, + new RowMapper>() { + @Override + public HashMap mapRow(ResultSet rs, int rowNum) throws SQLException { + HashMap result = new HashMap<>(); + while (true) { + String name = rs.getString("name"); + List arguments = Arrays.asList(rs.getString("arguments").split(" ")); + String expression = rs.getString("expression"); + result.put(name, new Function(name, arguments, expression)); + if (!rs.next()) { + break; + } + } + return result; + } + } + ); + } catch (EmptyResultDataAccessException e) { + HashMap result = new HashMap<>(); + return result; + } + } + + boolean deleteFunction(String username, String name) throws ParsingException { + try { + getFunction(username, name); + jdbcTemplate.update("DELETE FROM billing.variables WHERE username = ? AND name = ?", + new Object[]{username, name}); + return true; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + void addFunction(String username, String name, List arguments, String expression) throws ParsingException { + try { + getFunction(username, name); + jdbcTemplate.update("DELETE FROM billing.functions WHERE username = ? AND name = ?", + new Object[]{username, name}); + String stringArguments = String.join(" ", arguments); + jdbcTemplate.update("INSERT INTO billing.functions VALUES (?, ?, ?, ?)", + new Object[]{username, name, stringArguments, expression}); + } catch (EmptyResultDataAccessException e) { + String stringArguments = String.join(" ", arguments); + jdbcTemplate.update("INSERT INTO billing.functions VALUES (?, ?, ?, ?)", + new Object[]{username, name, stringArguments, expression}); + } + } + + public CalculationUser loadUser(String username) throws EmptyResultDataAccessException { + LOG.trace("Querying for user " + username); + return jdbcTemplate.queryForObject( + "SELECT username, password, enabled FROM billing.users WHERE username = ?", + new Object[]{username}, + new RowMapper() { + @Override + public CalculationUser mapRow(ResultSet rs, int rowNum) throws SQLException { + return new CalculationUser( + rs.getString("username"), + rs.getString("password"), + rs.getBoolean("enabled") + ); + } + } + ); + } +} diff --git a/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/CalculationDatabaseConfiguration.java b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/CalculationDatabaseConfiguration.java new file mode 100644 index 000000000..9d28a12a2 --- /dev/null +++ b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/CalculationDatabaseConfiguration.java @@ -0,0 +1,26 @@ +package ru.mipt.java2016.homework.g597.moiseev.task4; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.sql.DataSource; + +@Configuration +public class CalculationDatabaseConfiguration { + @Bean + public DataSource billingDataSource( + @Value("${ru.mipt.java2016.homework.g597.moiseev.task4.jdbcUrl}") String jdbcUrl, + @Value("${ru.mipt.java2016.homework.g597.moiseev.task4.username:}") String username, + @Value("${ru.mipt.java2016.homework.g597.moiseev.task4.password:}") String password + ) { + HikariConfig config = new HikariConfig(); + config.setDriverClassName(org.h2.Driver.class.getName()); + config.setJdbcUrl(jdbcUrl); + config.setUsername(username); + config.setPassword(password); + return new HikariDataSource(config); + } +} diff --git a/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/CalculationUser.java b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/CalculationUser.java new file mode 100644 index 000000000..cc5d58895 --- /dev/null +++ b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/CalculationUser.java @@ -0,0 +1,69 @@ +package ru.mipt.java2016.homework.g597.moiseev.task4; + +public class CalculationUser { + private final String username; + private final String password; + private final boolean enabled; + + public CalculationUser(String username, String password, boolean enabled) { + if (username == null) { + throw new IllegalArgumentException("Null username is not allowed"); + } + if (password == null) { + throw new IllegalArgumentException("Null password is not allowed"); + } + this.username = username; + this.password = password; + this.enabled = enabled; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public boolean isEnabled() { + return enabled; + } + + @Override + public String toString() { + return "CalculationUser{" + + "username='" + username + '\'' + + ", password='" + password + '\'' + + ", enabled=" + enabled + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + CalculationUser that = (CalculationUser) o; + + if (enabled != that.enabled) { + return false; + } + if (!username.equals(that.username)) { + return false; + } + return password.equals(that.password); + + } + + @Override + public int hashCode() { + int result = username.hashCode(); + result = 31 * result + password.hashCode(); + result = 31 * result + (enabled ? 1 : 0); + return result; + } +} diff --git a/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/Function.java b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/Function.java new file mode 100644 index 000000000..7bfd2f388 --- /dev/null +++ b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/Function.java @@ -0,0 +1,238 @@ +package ru.mipt.java2016.homework.g597.moiseev.task4; + +import ru.mipt.java2016.homework.base.task1.ParsingException; + +import java.util.*; + +public class Function { + static final Random RANDOM = new Random(); + private static final Map PREDEFINED_FUNCTIONS; + + static { + PREDEFINED_FUNCTIONS = new HashMap<>(); + PREDEFINED_FUNCTIONS.put("sin", 1); + PREDEFINED_FUNCTIONS.put("cos", 1); + PREDEFINED_FUNCTIONS.put("tg", 1); + PREDEFINED_FUNCTIONS.put("sqrt", 1); + PREDEFINED_FUNCTIONS.put("pow", 2); + PREDEFINED_FUNCTIONS.put("abs", 1); + PREDEFINED_FUNCTIONS.put("sign", 1); + PREDEFINED_FUNCTIONS.put("log", 1); + PREDEFINED_FUNCTIONS.put("log2", 1); + PREDEFINED_FUNCTIONS.put("rnd", 0); + PREDEFINED_FUNCTIONS.put("max", 2); + PREDEFINED_FUNCTIONS.put("min", 2); + } + + private List arguments = new ArrayList<>(); + private String name; + private String expression; + private int valency; + + public Function(String name, List arguments, String expression) { + this.name = name; + this.arguments = arguments; + this.expression = expression; + valency = arguments.size(); + } + + private Function(String name, int valency) { + this.name = name; + this.valency = valency; + this.arguments = new ArrayList<>(); + for (int i = 0; i < valency; i++) { + arguments.add("x" + Integer.toString(i)); + } + this.expression = null; + } + + public static String replaceFunctions(String expression, Map functions, + PowerfulCalculator powerfulCalculator) throws ParsingException { + StringBuilder result = new StringBuilder(); + + Integer[] index = {0}; + for (; index[0] < expression.length(); index[0]++) { + if (Character.isLetter(expression.charAt(index[0]))) { + result.append(Function.replaceFunction(expression, index, functions, powerfulCalculator)); + } else { + result.append(expression.charAt(index[0])); + } + } + + return new String(result); + } + + public static String replaceFunction(String expression, Integer[] index, Map functions, + PowerfulCalculator powerfulCalculator) throws ParsingException { + StringBuilder name = new StringBuilder(); + + while (Character.isLetterOrDigit(expression.charAt(index[0])) || expression.charAt(index[0]) == '_') { + name.append(expression.charAt(index[0])); + index[0]++; + } + + Function function; + + String stringName = new String(name); + if (PREDEFINED_FUNCTIONS.containsKey(stringName)) { + function = new Function(stringName, PREDEFINED_FUNCTIONS.get(stringName)); + } else { + function = functions.get(new String(name)); + if (function == null) { + throw new ParsingException("Function not found"); + } + } + + while (Character.isSpaceChar(expression.charAt(index[0]))) { + index[0]++; + } + + if (!(expression.charAt(index[0]) == '(')) { + throw new ParsingException("Illegal expression"); + } + + index[0]++; + + List argumentsValues = new ArrayList<>(); + StringBuilder arg = new StringBuilder(); + int depth = 1; + + for (; index[0] < expression.length(); index[0]++) { + char c = expression.charAt(index[0]); + + if (expression.charAt(index[0]) == ',' && depth == 1) { + argumentsValues.add(powerfulCalculator.calculate(new String(arg), null, functions).toString()); + arg = new StringBuilder(); + } else { + if (c == ')') { + depth--; + if (depth == 0) { + index[0]++; + argumentsValues.add(powerfulCalculator.calculate(new String(arg), null, functions).toString()); + return function.calculate(argumentsValues, functions, powerfulCalculator).toString(); + } else { + arg.append(c); + } + } else { + arg.append(c); + if (c == '(') { + depth++; + } + } + } + } + + throw new ParsingException("Illegal expression"); + } + + public static String replaceVariables(String expression, Map variables) throws ParsingException { + StringBuilder result = new StringBuilder(); + StringBuilder currentLettersBlock = new StringBuilder(); + boolean flag = false; + for (Character c : expression.toCharArray()) { + if (flag) { + if (c.equals('(')) { + result.append(currentLettersBlock); + currentLettersBlock = new StringBuilder(); + result.append("("); + flag = false; + } else if (Character.isSpaceChar(c)) { + continue; + } else if (Character.isLetterOrDigit(c) || c == '_') { + currentLettersBlock.append(c); + } else { + flag = false; + String variable = new String(currentLettersBlock); + currentLettersBlock = new StringBuilder(); + if (variables.containsKey(variable)) { + result.append(variables.get(variable)); + } else { + throw new ParsingException("Variable not found"); + } + result.append(c); + } + } else { + if (Character.isLetter(c)) { + flag = true; + currentLettersBlock = new StringBuilder(); + currentLettersBlock.append(c); + } else { + result.append(c); + } + } + } + + if (currentLettersBlock.length() > 0) { + String variable = new String(currentLettersBlock); + if (variables.containsKey(variable)) { + result.append(variables.get(variable)); + } else { + throw new ParsingException("Variable not found"); + } + } + + return new String(result); + } + + public Double calculate(List values, Map functions, + PowerfulCalculator powerfulCalculator) throws ParsingException { + if (values.size() != valency) { + throw new ParsingException("Invalid number of arguments"); + } + + Map calculatedArguments = new HashMap<>(); + + for (int i = 0; i < arguments.size(); i++) { + Double arg = powerfulCalculator.calculate(values.get(i), null, functions); + calculatedArguments.put(arguments.get(i), arg); + } + + if (PREDEFINED_FUNCTIONS.containsKey(name)) { + switch (name) { + case "sin": + return Math.sin(calculatedArguments.get(arguments.get(0))); + case "cos": + return Math.cos(calculatedArguments.get(arguments.get(0))); + case "tg": + return Math.tan(calculatedArguments.get(arguments.get(0))); + case "sqrt": + return Math.sqrt(calculatedArguments.get(arguments.get(0))); + case "pow": + return Math.pow(calculatedArguments.get(arguments.get(0)), + calculatedArguments.get(arguments.get(1))); + case "abs": + return Math.abs(calculatedArguments.get(arguments.get(0))); + case "sign": + return Math.signum(calculatedArguments.get(arguments.get(0))); + case "log": + return Math.log(calculatedArguments.get(arguments.get(1))) / + Math.log(calculatedArguments.get(arguments.get(0))); + case "log2": + return Math.log(calculatedArguments.get(arguments.get(1))) / + Math.log(2); + case "rnd": + synchronized (RANDOM) { + return RANDOM.nextDouble(); + } + case "max": + return Math.max(calculatedArguments.get(arguments.get(0)), + calculatedArguments.get(arguments.get(1))); + case "min": + return Math.min(calculatedArguments.get(arguments.get(0)), + calculatedArguments.get(arguments.get(1))); + default: + break; + } + } + + return powerfulCalculator.calculate(expression, calculatedArguments, functions); + } + + public List getArguments() { + return arguments; + } + + public String getExpression() { + return expression; + } +} diff --git a/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/PowerfulCalculator.java b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/PowerfulCalculator.java new file mode 100644 index 000000000..26227a816 --- /dev/null +++ b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/PowerfulCalculator.java @@ -0,0 +1,16 @@ +package ru.mipt.java2016.homework.g597.moiseev.task4; + +import ru.mipt.java2016.homework.base.task1.ParsingException; +import ru.mipt.java2016.homework.g597.moiseev.task1.StackCalculator; + +import java.util.*; + +public class PowerfulCalculator extends StackCalculator { + public Double calculate(String expression, Map variables, + Map functions) throws ParsingException { + String expressionWithoutVariables = Function.replaceVariables(expression, variables); + String expressionWithoutFunctions = Function.replaceFunctions(expressionWithoutVariables, functions, this); + + return super.calculate(expressionWithoutFunctions); + } +} diff --git a/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/RestCalculatorApplication.java b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/RestCalculatorApplication.java new file mode 100644 index 000000000..3a30f537f --- /dev/null +++ b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/RestCalculatorApplication.java @@ -0,0 +1,32 @@ +package ru.mipt.java2016.homework.g597.moiseev.task4; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.Banner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@EnableAutoConfiguration +@Configuration +@ComponentScan(basePackageClasses = RestCalculatorApplication.class) +public class RestCalculatorApplication { + @Bean + public PowerfulCalculator calculator() { + return new PowerfulCalculator(); + } + + @Bean + public EmbeddedServletContainerCustomizer customizer( + @Value("${ru.mipt.java2016.homework.g597.moiseev.task4.httpPort:9001}") int port) { + return container -> container.setPort(port); + } + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(RestCalculatorApplication.class); + application.setBannerMode(Banner.Mode.OFF); + application.run(args); + } +} diff --git a/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/RestCalculatorController.java b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/RestCalculatorController.java new file mode 100644 index 000000000..4f9a6ea4a --- /dev/null +++ b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/RestCalculatorController.java @@ -0,0 +1,121 @@ +package ru.mipt.java2016.homework.g597.moiseev.task4; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import org.springframework.security.core.Authentication; + +import ru.mipt.java2016.homework.base.task1.ParsingException; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +@RestController +public class RestCalculatorController { + private static final Logger LOG = LoggerFactory.getLogger(RestCalculatorController.class); + + @Autowired + private PowerfulCalculator calculator; + + @Autowired + private CalculationDao calculationDao; + + @RequestMapping(path = "/variable/{name}", method = RequestMethod.GET, produces = "text/plain") + public String getVariable(Authentication authentication, @PathVariable String name) throws ParsingException { + String ourName = authentication.getName(); + Double result = calculationDao.getVariable(ourName, name); + return name + " = " + result + "\n"; + } + + @RequestMapping(path = "/variable/{name}", method = RequestMethod.DELETE, produces = "text/plain") + public String deleteVariable(Authentication authentication, @PathVariable String name) throws ParsingException { + String username = authentication.getName(); + boolean success = calculationDao.deleteVariable(username, name); + if (success) { + return name + " deleted\n"; + } else { + return name + " not exists\n"; + } + } + + @RequestMapping(path = "/variable/{name}", method = RequestMethod.PUT, + consumes = "text/plain", produces = "text/plain") + public String addVariable(Authentication authentication, @PathVariable String name, @RequestBody String value) + throws ParsingException { + String username = authentication.getName(); + calculationDao.addVariable(username, name, Double.parseDouble(value)); + return "Variable added\n"; + } + + @RequestMapping(path = "/variable", method = RequestMethod.GET, produces = "text/plain") + public String getVariables(Authentication authentication) throws ParsingException { + String username = authentication.getName(); + Map result = calculationDao.getVariables(username); + return String.join(", ", result.keySet()) + "\n" + + ""; + } + + @RequestMapping(path = "/function/{name}", method = RequestMethod.GET, produces = "text/plain") + public String getFunction(Authentication authentication, @PathVariable String name) throws ParsingException { + String ourName = authentication.getName(); + Function result = calculationDao.getFunction(ourName, name); + return name + "(" + String.join(", ", result.getArguments()) + ")" + " = " + result.getExpression() + "\n"; + } + + @RequestMapping(path = "/function/{name}", method = RequestMethod.DELETE, produces = "text/plain") + public String deleteFunction(Authentication authentication, @PathVariable String name) throws ParsingException { + String username = authentication.getName(); + boolean success = calculationDao.deleteFunction(username, name); + if (success) { + return name + " deleted\n"; + } else { + return name + " not exists\n"; + } + } + + @RequestMapping(path = "/function/{name}", method = RequestMethod.PUT, + consumes = "text/plain", produces = "text/plain") + public String addFunction(Authentication authentication, @PathVariable String name, + @RequestParam(value = "args") String args, + @RequestBody String expression) + throws ParsingException { + String username = authentication.getName(); + List arguments = Arrays.asList(args.split(",")); + calculationDao.addFunction(username, name, arguments, expression); + return "Function added\n"; + } + + @RequestMapping(path = "/function", method = RequestMethod.GET, produces = "text/plain") + public String getFunctions(Authentication authentication) throws ParsingException { + String username = authentication.getName(); + Map result = calculationDao.getFunctions(username); + return String.join(", ", result.keySet()) + "\n"; + } + + @RequestMapping(path = "/calculate", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain") + public String calculate(Authentication authentication, @RequestBody String expression) throws ParsingException { + LOG.debug("Calculation request: [" + expression + "]"); + String username = authentication.getName(); + Map variables = calculationDao.getVariables(username); + Map functions = calculationDao.getFunctions(username); + return calculator.calculate(expression, variables, functions) + "\n"; + } + + @RequestMapping(path = "/register/{username}", method = RequestMethod.PUT, + consumes = "text/plain", produces = "text/plain") + public String register(@PathVariable String username, @RequestBody String pswd) + throws ParsingException { + LOG.debug("New user: [" + username + ' ' + pswd + "]"); + boolean success = calculationDao.addUserIfNotExists(username, pswd, true); + if (success) { + LOG.trace("Success"); + return "You have been successfully registered\n"; + } else { + LOG.trace("Fail"); + return "This user already exists\n"; + } + } +} diff --git a/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/SecurityServiceConfiguration.java b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/SecurityServiceConfiguration.java new file mode 100644 index 000000000..d100d1937 --- /dev/null +++ b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/SecurityServiceConfiguration.java @@ -0,0 +1,57 @@ +package ru.mipt.java2016.homework.g597.moiseev.task4; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import java.util.Collections; + +@Configuration +@EnableWebSecurity +public class SecurityServiceConfiguration extends WebSecurityConfigurerAdapter { + private static final Logger LOG = LoggerFactory.getLogger(SecurityServiceConfiguration.class); + + @Autowired + private CalculationDao calculationDao; + + @Override + protected void configure(HttpSecurity http) throws Exception { + LOG.info("Configuring security"); + http + .httpBasic().realmName("Calculator").and() + .formLogin().disable() + .logout().disable() + .csrf().disable() + .authorizeRequests() + .antMatchers("/calculate/**").authenticated() + .antMatchers("/variable/**").authenticated() + .antMatchers("/function/**").authenticated() + .anyRequest().permitAll(); + } + + @Autowired + public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception { + LOG.info("Registering global user details service"); + auth.userDetailsService(username -> { + try { + CalculationUser user = calculationDao.loadUser(username); + return new User( + user.getUsername(), + user.getPassword(), + Collections.singletonList(() -> "AUTH") + ); + } catch (EmptyResultDataAccessException e) { + LOG.warn("No such user: " + username); + throw new UsernameNotFoundException(username); + } + }); + } +} \ No newline at end of file diff --git a/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/requests.txt b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/requests.txt new file mode 100644 index 000000000..5815f74fd --- /dev/null +++ b/homework-g597-moiseev/src/main/java/ru/mipt/java2016/homework/g597/moiseev/task4/requests.txt @@ -0,0 +1,72 @@ +Calculate expression: +curl http://localhost:9001/calculate \ + -X POST \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" \ + --data-raw "1+1" + +Add variable: +curl http://localhost:9001/variable/t \ + -X PUT \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" \ + --data-raw "3" + +Get variable: +curl http://localhost:9001/variable/t \ + -X GET \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" + +Get variables: +curl http://localhost:9001/variable \ + -X GET \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" + +Calculate another expression: +curl http://localhost:9001/calculate \ + -X POST \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" \ + --data-raw "1+t*t" + +Add function: +curl http://localhost:9001/function/f?args=x \ + -X PUT \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" \ + --data-raw "x*x" + +Get function: +curl http://localhost:9001/function/f \ + -X GET \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" + +Get functions: +curl http://localhost:9001/function \ + -X GET \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" + +Calculate fantastic expression: +curl http://localhost:9001/calculate \ + -X POST \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" \ + --data-raw "t+f(t) + sqrt(f(9))" + +Add another function: +curl http://localhost:9001/function/g?args=x,y \ + -X PUT \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" \ + --data-raw "x*y" + +Third expression: +curl http://localhost:9001/calculate \ + -X POST \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" \ + --data-raw "g(t, 4)" \ No newline at end of file