From 007b5da40364dbfd67ca6ce8af68ac4fb89681d3 Mon Sep 17 00:00:00 2001 From: Julia Ivanova Date: Tue, 20 Dec 2016 00:20:27 +0300 Subject: [PATCH 1/9] Add task 4 --- homework-g596-ivanova/pom.xml | 65 ++++++++++++++++- .../g596/ivanova/task4/BillingDao.java | 73 +++++++++++++++++++ .../task4/BillingDatabaseConfiguration.java | 26 +++++++ .../g596/ivanova/task4/BillingUser.java | 69 ++++++++++++++++++ .../task4/RestCalculatorApplication.java | 33 +++++++++ .../task4/RestCalculatorController.java | 26 +++++++ .../task4/SecurityServiceConfiguration.java | 55 ++++++++++++++ 7 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java create mode 100644 homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDatabaseConfiguration.java create mode 100644 homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingUser.java create mode 100644 homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java create mode 100644 homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java create mode 100644 homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/SecurityServiceConfiguration.java diff --git a/homework-g596-ivanova/pom.xml b/homework-g596-ivanova/pom.xml index 7be63c0fc..00d589ff0 100644 --- a/homework-g596-ivanova/pom.xml +++ b/homework-g596-ivanova/pom.xml @@ -4,12 +4,17 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> mipt-java-2016 - ru.mipt.java2016 + + ru.mipt.java2016 1.0.0 4.0.0 homework-g596-ivanova + + + 1.4.2.RELEASE + ru.mipt.java2016 @@ -35,8 +40,66 @@ 3.21.0-GA jar + + com.zaxxer + HikariCP + RELEASE + + + com.fasterxml.jackson.core + jackson-annotations + RELEASE + + + net.sourceforge.jeval + jeval + 0.9.4 + + + 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 + + + diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java new file mode 100644 index 000000000..e9f73c603 --- /dev/null +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java @@ -0,0 +1,73 @@ +package ru.mipt.java2016.homework.g596.ivanova.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 BillingDao { + private static final Logger LOG = LoggerFactory.getLogger(BillingDao.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 BillingUser 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 BillingUser mapRow(ResultSet rs, int rowNum) throws SQLException { + return new BillingUser( + rs.getString("username"), + rs.getString("password"), + rs.getBoolean("enabled") + ); + } + } + ); + } +} \ No newline at end of file diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDatabaseConfiguration.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDatabaseConfiguration.java new file mode 100644 index 000000000..f1e6a0ef3 --- /dev/null +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDatabaseConfiguration.java @@ -0,0 +1,26 @@ +package ru.mipt.java2016.homework.g596.ivanova.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 BillingDatabaseConfiguration { + @Bean + public DataSource billingDataSource( + @Value("${ru.mipt.java2016.homework.g596.ivanova.task4.jdbcUrl}") String jdbcUrl, + @Value("${ru.mipt.java2016.homework.g596.ivanova.task4.username:}") String username, + @Value("${ru.mipt.java2016.homework.g596.ivanova.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-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingUser.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingUser.java new file mode 100644 index 000000000..6770fd0c5 --- /dev/null +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingUser.java @@ -0,0 +1,69 @@ +package ru.mipt.java2016.homework.g596.ivanova.task4; + +public class BillingUser { + private final String username; + private final String password; + private final boolean enabled; + + public BillingUser(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 "BillingUser{" + + "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; + } + + BillingUser that = (BillingUser) 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-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java new file mode 100644 index 000000000..edf649d38 --- /dev/null +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java @@ -0,0 +1,33 @@ +package ru.mipt.java2016.homework.g596.ivanova.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; +import ru.mipt.java2016.homework.g596.ivanova.task1.BestCalculatorEver; + +@EnableAutoConfiguration +@Configuration +@ComponentScan(basePackageClasses = RestCalculatorApplication.class) +public class RestCalculatorApplication { + @Bean + public BestCalculatorEver calculator() { + return new BestCalculatorEver(); + } + + @Bean + public EmbeddedServletContainerCustomizer customizer( + @Value("${ru.mipt.java2016.homework.g596.ivanova.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); + } +} \ No newline at end of file diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java new file mode 100644 index 000000000..bc410465d --- /dev/null +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java @@ -0,0 +1,26 @@ +package ru.mipt.java2016.homework.g596.ivanova.task4; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import ru.mipt.java2016.homework.base.task1.ParsingException; +import ru.mipt.java2016.homework.g596.ivanova.task1.BestCalculatorEver; + +@RestController +public class RestCalculatorController { + @Autowired + private BestCalculatorEver calculator; + + @Autowired + private BillingDao billingDao; + + @RequestMapping(path = "/calculate", method = RequestMethod.POST, + consumes = "text/plain", produces = "text/plain") + public String calculate(Authentication authentication, @RequestBody String expression) throws + ParsingException { + return calculator.calculate(expression) + "\n"; + } +} diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/SecurityServiceConfiguration.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/SecurityServiceConfiguration.java new file mode 100644 index 000000000..41edb3dab --- /dev/null +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/SecurityServiceConfiguration.java @@ -0,0 +1,55 @@ +package ru.mipt.java2016.homework.g596.ivanova.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 BillingDao billingDao; + + @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() + .anyRequest().permitAll(); + } + + @Autowired + public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception { + LOG.info("Registering global user details service"); + auth.userDetailsService(username -> { + try { + BillingUser user = billingDao.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 From cd90f553f87f70ebe6488fdb6e05a39e841095d3 Mon Sep 17 00:00:00 2001 From: Julia Ivanova Date: Tue, 20 Dec 2016 00:57:01 +0300 Subject: [PATCH 2/9] Remove unused imports --- .../mipt/java2016/homework/g596/ivanova/task4/BillingDao.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java index e9f73c603..abe6112cd 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java @@ -7,13 +7,11 @@ 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 BillingDao { From a3467b7c980698b2e31f51f815d1d39b272c7cfc Mon Sep 17 00:00:00 2001 From: Julia Ivanova Date: Tue, 20 Dec 2016 14:43:21 +0300 Subject: [PATCH 3/9] Fixed naming --- .../homework/g596/ivanova/task4/RestCalculatorApplication.java | 3 ++- .../homework/g596/ivanova/task4/RestCalculatorController.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java index edf649d38..a47619362 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java @@ -8,6 +8,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import ru.mipt.java2016.homework.base.task1.Calculator; import ru.mipt.java2016.homework.g596.ivanova.task1.BestCalculatorEver; @EnableAutoConfiguration @@ -15,7 +16,7 @@ @ComponentScan(basePackageClasses = RestCalculatorApplication.class) public class RestCalculatorApplication { @Bean - public BestCalculatorEver calculator() { + public Calculator calculator() { return new BestCalculatorEver(); } diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java index bc410465d..9f958abb2 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java @@ -6,13 +6,14 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import ru.mipt.java2016.homework.base.task1.Calculator; import ru.mipt.java2016.homework.base.task1.ParsingException; import ru.mipt.java2016.homework.g596.ivanova.task1.BestCalculatorEver; @RestController public class RestCalculatorController { @Autowired - private BestCalculatorEver calculator; + private Calculator calculator; @Autowired private BillingDao billingDao; From 4836b89e23ca8a6293ab973fba8fcdabc31d885a Mon Sep 17 00:00:00 2001 From: Julia Ivanova Date: Tue, 20 Dec 2016 14:45:38 +0300 Subject: [PATCH 4/9] Remove unused import --- .../homework/g596/ivanova/task4/RestCalculatorController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java index 9f958abb2..8c91bf0df 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java @@ -8,7 +8,6 @@ import org.springframework.web.bind.annotation.RestController; import ru.mipt.java2016.homework.base.task1.Calculator; import ru.mipt.java2016.homework.base.task1.ParsingException; -import ru.mipt.java2016.homework.g596.ivanova.task1.BestCalculatorEver; @RestController public class RestCalculatorController { From 91f40c2b027e39e990ca9e2934f54294e4a0f940 Mon Sep 17 00:00:00 2001 From: Julia Ivanova Date: Tue, 20 Dec 2016 15:32:03 +0300 Subject: [PATCH 5/9] Add query template --- .../homework/g596/ivanova/task4/BillingDao.java | 4 ---- .../ivanova/task4/RestCalculatorApplication.java | 14 +++++++++++--- .../ivanova/task4/RestCalculatorController.java | 4 ++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java index abe6112cd..7b9f36fea 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java @@ -33,10 +33,6 @@ public void initSchema() { 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); } diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java index a47619362..136613a7c 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java @@ -8,7 +8,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import ru.mipt.java2016.homework.base.task1.Calculator; import ru.mipt.java2016.homework.g596.ivanova.task1.BestCalculatorEver; @EnableAutoConfiguration @@ -16,7 +15,7 @@ @ComponentScan(basePackageClasses = RestCalculatorApplication.class) public class RestCalculatorApplication { @Bean - public Calculator calculator() { + public BestCalculatorEver calculator() { return new BestCalculatorEver(); } @@ -31,4 +30,13 @@ public static void main(String[] args) { application.setBannerMode(Banner.Mode.OFF); application.run(args); } -} \ No newline at end of file +} + +/** + * Query template. + curl http://localhost:9001/calculate \ + -X POST \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" \ + --data-raw "" + */ \ No newline at end of file diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java index 8c91bf0df..bc410465d 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java @@ -6,13 +6,13 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import ru.mipt.java2016.homework.base.task1.Calculator; import ru.mipt.java2016.homework.base.task1.ParsingException; +import ru.mipt.java2016.homework.g596.ivanova.task1.BestCalculatorEver; @RestController public class RestCalculatorController { @Autowired - private Calculator calculator; + private BestCalculatorEver calculator; @Autowired private BillingDao billingDao; From 93582cba90c1d2371ab92c1d8bcd6e044d4b0b9e Mon Sep 17 00:00:00 2001 From: Julia Ivanova Date: Wed, 21 Dec 2016 03:06:17 +0300 Subject: [PATCH 6/9] Add variable and function putting --- .../g596/ivanova/task4/BillingDao.java | 126 ++++++++++++++++++ .../homework/g596/ivanova/task4/Function.java | 32 +++++ .../task4/RestCalculatorApplication.java | 11 +- .../task4/RestCalculatorController.java | 85 +++++++++++- 4 files changed, 249 insertions(+), 5 deletions(-) create mode 100644 homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/Function.java diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java index 7b9f36fea..dc4069f45 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java @@ -1,5 +1,9 @@ package ru.mipt.java2016.homework.g596.ivanova.task4; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -12,6 +16,7 @@ import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; +import ru.mipt.java2016.homework.base.task1.ParsingException; @Repository public class BillingDao { @@ -33,6 +38,10 @@ public void initSchema() { 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); } @@ -47,6 +56,123 @@ boolean addUserIfNotExists(String username, String password, boolean enabled) { } } + 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}, (rs, rowNum) -> { + 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}, (rs, rowNum) -> { + String name = rs.getString("name"); + List arguments = Arrays.asList(rs.getString("arguments").split(" ")); + String expression = rs.getString("expression"); + return new Function(name, arguments, expression); + }); + } + + 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}); + } + } + + Map getFunctions(String username) { + try { + return jdbcTemplate.queryForObject( + "SELECT username, name, arguments, expression FROM billing.functions WHERE username = ?", + new Object[]{username}, (rs, rowNum) -> { + 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; + } + } + public BillingUser loadUser(String username) throws EmptyResultDataAccessException { LOG.trace("Querying for user " + username); return jdbcTemplate.queryForObject( diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/Function.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/Function.java new file mode 100644 index 000000000..a88cecef5 --- /dev/null +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/Function.java @@ -0,0 +1,32 @@ +package ru.mipt.java2016.homework.g596.ivanova.task4; + +import java.util.*; + +public class Function { + private List arguments = new ArrayList<>(); + private String name; + private String expression; + + public Function(String name, List arguments, String expression) { + this.name = name; + this.arguments = arguments; + this.expression = expression; + } + + private Function(String name, int valency) { + this.name = name; + this.arguments = new ArrayList<>(); + for (int i = 0; i < valency; i++) { + arguments.add("x" + Integer.toString(i)); + } + this.expression = null; + } + + public List getArguments() { + return arguments; + } + + public String getExpression() { + return expression; + } +} \ No newline at end of file diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java index 136613a7c..38df3a88c 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorApplication.java @@ -21,7 +21,7 @@ public BestCalculatorEver calculator() { @Bean public EmbeddedServletContainerCustomizer customizer( - @Value("${ru.mipt.java2016.homework.g596.ivanova.task4.httpPort:9001}") int port) { + @Value("${ru.mipt.java2016.homework.g596.ivanova.task4.httpPort:8080}") int port) { return container -> container.setPort(port); } @@ -34,9 +34,16 @@ public static void main(String[] args) { /** * Query template. - curl http://localhost:9001/calculate \ + curl http://localhost:8080/calculate \ -X POST \ -H "Content-Type: text/plain" \ -H "Authorization: Basic $(echo -n "username:password" | base64)" \ --data-raw "" + + + curl http://localhost:8080/variable/x \ + -X GET \ + -H "Content-Type: text/plain" \ + -H "Authorization: Basic $(echo -n "username:password" | base64)" \ + --data-raw "" */ \ No newline at end of file diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java index bc410465d..f7aaf4525 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java @@ -1,10 +1,15 @@ package ru.mipt.java2016.homework.g596.ivanova.task4; +import java.util.Arrays; +import java.util.List; +import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import ru.mipt.java2016.homework.base.task1.ParsingException; import ru.mipt.java2016.homework.g596.ivanova.task1.BestCalculatorEver; @@ -17,10 +22,84 @@ public class RestCalculatorController { @Autowired private BillingDao billingDao; - @RequestMapping(path = "/calculate", method = RequestMethod.POST, + @RequestMapping(path = "/variable/{name}", method = RequestMethod.GET, produces = "text/plain") + public String getVariable(Authentication authentication, @PathVariable(value = "name") String variable) + throws ParsingException { + String userName = authentication.getName(); + Double result = billingDao.getVariable(userName, variable); + return variable + " = " + result + "\n"; + } + + @RequestMapping(path = "/variable/{name}", method = RequestMethod.DELETE, produces = "text/plain") + public String deleteVariable(Authentication authentication, @PathVariable(value = "name") String variable) + throws ParsingException { + String userName = authentication.getName(); + if (billingDao.deleteVariable(userName, variable)) { + return variable + " deleted\n"; + } else { + return variable + " not exists\n"; + } + } + + @RequestMapping(path = "/variable/{name}", method = RequestMethod.PUT, consumes = "text/plain", produces = "text/plain") - public String calculate(Authentication authentication, @RequestBody String expression) throws - ParsingException { + public String addVariable(Authentication authentication, + @PathVariable(value = "name") String variable, + @RequestBody String value) + throws ParsingException { + String username = authentication.getName(); + billingDao.addVariable(username, variable, Double.parseDouble(value)); + return "Variable " + 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 = billingDao.getVariables(userName); + return String.join(", ", result.keySet()) + "\n" + ""; + } + + @RequestMapping(path = "/function/{name}", method = RequestMethod.GET, produces = "text/plain") + public String getFunction(Authentication authentication, @PathVariable(value = "name") String function) + throws ParsingException { + String userName = authentication.getName(); + Function result = billingDao.getFunction(userName, function); + return function + "(" + String.join(", ", result.getArguments()) + ")" + " = " + result.getExpression() + "\n"; + } + + @RequestMapping(path = "/function/{name}", method = RequestMethod.PUT, + consumes = "text/plain", produces = "text/plain") + public String addFunction(Authentication authentication, @PathVariable(value = "name") String function, + @RequestParam(value = "args") String args, + @RequestBody String expression) + throws ParsingException { + String userName = authentication.getName(); + List arguments = Arrays.asList(args.split(",")); + billingDao.addFunction(userName, function, arguments, expression); + return "Function " + 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 = billingDao.getFunctions(userName); + return String.join(", ", result.keySet()) + "\n"; + } + + @RequestMapping(path = "/function/{name}", method = RequestMethod.DELETE, produces = "text/plain") + public String deleteFunction(Authentication authentication, @PathVariable(value = "name") String function) + throws ParsingException { + String username = authentication.getName(); + if (billingDao.deleteFunction(username, function)) { + return function + " deleted\n"; + } else { + return function + " not exists\n"; + } + } + + @RequestMapping(path = "/calculate", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain") + public String calculate(Authentication authentication, @RequestBody String expression) + throws ParsingException { return calculator.calculate(expression) + "\n"; } } From 78e22e757f4916d9fc2e3631942508bc28d59fa9 Mon Sep 17 00:00:00 2001 From: Julia Ivanova Date: Wed, 21 Dec 2016 03:08:56 +0300 Subject: [PATCH 7/9] Small fix --- .../java2016/homework/g596/ivanova/task4/Function.java | 9 --------- .../g596/ivanova/task4/RestCalculatorController.java | 5 +++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/Function.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/Function.java index a88cecef5..23b1ede5d 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/Function.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/Function.java @@ -13,15 +13,6 @@ public Function(String name, List arguments, String expression) { this.expression = expression; } - private Function(String name, int valency) { - this.name = name; - this.arguments = new ArrayList<>(); - for (int i = 0; i < valency; i++) { - arguments.add("x" + Integer.toString(i)); - } - this.expression = null; - } - public List getArguments() { return arguments; } diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java index f7aaf4525..252a95a1a 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/RestCalculatorController.java @@ -97,8 +97,9 @@ public String deleteFunction(Authentication authentication, @PathVariable(value } } - @RequestMapping(path = "/calculate", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain") - public String calculate(Authentication authentication, @RequestBody String expression) + @RequestMapping(path = "/calculate", method = RequestMethod.POST, + consumes = "text/plain", produces = "text/plain") + public String calculate(@RequestBody String expression) throws ParsingException { return calculator.calculate(expression) + "\n"; } From 5fde354acd3efd7186c737dda2200b730fbc4dfd Mon Sep 17 00:00:00 2001 From: Julia Ivanova Date: Wed, 21 Dec 2016 03:19:53 +0300 Subject: [PATCH 8/9] Fix codestyle --- .../g596/ivanova/task4/BillingDao.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java index dc4069f45..c96adff6b 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java @@ -72,17 +72,17 @@ public Double mapRow(ResultSet rs, int rowNum) throws SQLException { Map getVariables(String username) { try { return jdbcTemplate.queryForObject( - "SELECT username, name, value FROM billing.variables WHERE username = ?", - new Object[]{username}, (rs, rowNum) -> { - HashMap result = new HashMap<>(); - while (true) { - result.put(rs.getString("name"), rs.getDouble("value")); - if (!rs.next()) { - break; - } + "SELECT username, name, value FROM billing.variables WHERE username = ?", + new Object[] {username}, (rs, rowNum) -> { + HashMap result = new HashMap<>(); + while (true) { + result.put(rs.getString("name"), rs.getDouble("value")); + if (!rs.next()) { + break; } - return result; - }); + } + return result; + }); } catch (EmptyResultDataAccessException e) { HashMap result = new HashMap<>(); return result; @@ -117,11 +117,11 @@ 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}, (rs, rowNum) -> { - String name = rs.getString("name"); - List arguments = Arrays.asList(rs.getString("arguments").split(" ")); - String expression = rs.getString("expression"); - return new Function(name, arguments, expression); - }); + String name = rs.getString("name"); + List arguments = Arrays.asList(rs.getString("arguments").split(" ")); + String expression = rs.getString("expression"); + return new Function(name, arguments, expression); + }); } void addFunction(String username, String name, List arguments, String expression) throws ParsingException { From 3292fd9e7d662d2b923bb9117a3945c02697b31d Mon Sep 17 00:00:00 2001 From: Julia Ivanova Date: Wed, 21 Dec 2016 03:29:40 +0300 Subject: [PATCH 9/9] Fix codestyle --- .../g596/ivanova/task4/BillingDao.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java index c96adff6b..865daea34 100644 --- a/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java +++ b/homework-g596-ivanova/src/main/java/ru/mipt/java2016/homework/g596/ivanova/task4/BillingDao.java @@ -144,18 +144,18 @@ Map getFunctions(String username) { return jdbcTemplate.queryForObject( "SELECT username, name, arguments, expression FROM billing.functions WHERE username = ?", new Object[]{username}, (rs, rowNum) -> { - 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; - } + 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; - }); + } + return result; + }); } catch (EmptyResultDataAccessException e) { HashMap result = new HashMap<>(); return result;