Merge pull request #210 from Doha2012/master
move oauth module to reddit app
This commit is contained in:
commit
0b03522131
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry including="**/*.java" kind="src" path="src/main/resources"/>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry including="**/*.java" kind="src" output="target/test-classes" path="src/test/resources"/>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
|
@ -1,48 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>spring-security-oauth</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.springframework.ide.eclipse.core.springbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.validation.validationbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
|
||||
<nature>org.springframework.ide.eclipse.core.springnature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
|
||||
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beansProjectDescription>
|
||||
<version>1</version>
|
||||
<pluginVersion><![CDATA[3.6.3.201411271034-RELEASE]]></pluginVersion>
|
||||
<configSuffixes>
|
||||
<configSuffix><![CDATA[xml]]></configSuffix>
|
||||
</configSuffixes>
|
||||
<enableImports><![CDATA[true]]></enableImports>
|
||||
<configs>
|
||||
</configs>
|
||||
<autoconfigs>
|
||||
</autoconfigs>
|
||||
<configSets>
|
||||
</configSets>
|
||||
</beansProjectDescription>
|
|
@ -1 +0,0 @@
|
|||
web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT target/*.war
|
|
@ -1,501 +0,0 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.baeldung</groupId>
|
||||
<artifactId>spring-security-oauth</artifactId>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
|
||||
<name>spring-security-oauth</name>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Spring Security -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>${org.springframework.security.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>${org.springframework.security.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-taglibs</artifactId>
|
||||
<version>${org.springframework.security.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security.oauth</groupId>
|
||||
<artifactId>spring-security-oauth2</artifactId>
|
||||
<version>2.0.6.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- web -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>${httpclient.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpcore</artifactId>
|
||||
<version>${httpcore.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- thymeleaf -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-spring4</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf.extras</groupId>
|
||||
<artifactId>thymeleaf-extras-springsecurity3</artifactId>
|
||||
<version>2.1.2.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<!-- persistence -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-orm</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-jpa</artifactId>
|
||||
<version>${spring-data-jpa.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
<version>${hibernate.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>xml-apis</groupId>
|
||||
<artifactId>xml-apis</artifactId>
|
||||
<version>1.4.01</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<version>3.19.0-GA</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql-connector-java.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- h2 -->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.187</version>
|
||||
</dependency>
|
||||
|
||||
<!-- postgresql -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>9.4-1201-jdbc41</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- web -->
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>jstl</artifactId>
|
||||
<version>1.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- apache mahout -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.mahout</groupId>
|
||||
<artifactId>mahout-core</artifactId>
|
||||
<version>0.9</version>
|
||||
</dependency>
|
||||
|
||||
<!-- marshalling -->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>1.4.8</version>
|
||||
</dependency>
|
||||
|
||||
<!-- util -->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- logging -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${org.slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
<!-- <scope>runtime</scope> -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>${org.slf4j.version}</version>
|
||||
<!-- <scope>runtime</scope> --> <!-- some spring dependencies need to compile against jcl -->
|
||||
</dependency>
|
||||
<dependency> <!-- needed to bridge to slf4j for projects that use the log4j APIs directly -->
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
<version>${org.slf4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- test scoped -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit-dep</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
<version>${org.hamcrest.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-library</artifactId>
|
||||
<version>${org.hamcrest.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.jayway.restassured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>spring-security-oauth</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>${maven-war-plugin.version}</version>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<testFailureIgnore>true</testFailureIgnore>
|
||||
<excludes>
|
||||
<exclude>**/*IntegrationTest.java</exclude>
|
||||
<exclude>**/*LiveTest.java</exclude>
|
||||
</excludes>
|
||||
<systemPropertyVariables>
|
||||
<!-- <provPersistenceTarget>h2</provPersistenceTarget> -->
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.cargo</groupId>
|
||||
<artifactId>cargo-maven2-plugin</artifactId>
|
||||
<version>${cargo-maven2-plugin.version}</version>
|
||||
<configuration>
|
||||
<wait>true</wait>
|
||||
<container>
|
||||
<containerId>jetty8x</containerId>
|
||||
<type>embedded</type>
|
||||
<systemProperties>
|
||||
<!-- <provPersistenceTarget>cargo</provPersistenceTarget> -->
|
||||
</systemProperties>
|
||||
</container>
|
||||
<configuration>
|
||||
<properties>
|
||||
<cargo.servlet.port>8080</cargo.servlet.port>
|
||||
</properties>
|
||||
</configuration>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- webapp runner -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals><goal>copy</goal></goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>com.github.jsimone</groupId>
|
||||
<artifactId>webapp-runner</artifactId>
|
||||
<version>7.0.57.2</version>
|
||||
<destFileName>webapp-runner.jar</destFileName>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>integration</id>
|
||||
<build>
|
||||
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>none</exclude>
|
||||
</excludes>
|
||||
<includes>
|
||||
<include>**/*IntegrationTest.java</include>
|
||||
<include>**/*LiveTest.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<test.mime>json</test.mime>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.cargo</groupId>
|
||||
<artifactId>cargo-maven2-plugin</artifactId>
|
||||
<configuration>
|
||||
<wait>false</wait>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>start-server</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>start</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>stop-server</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>stop</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<properties>
|
||||
<!-- Spring -->
|
||||
<org.springframework.version>4.1.6.RELEASE</org.springframework.version>
|
||||
<org.springframework.security.version>3.2.7.RELEASE</org.springframework.security.version>
|
||||
|
||||
<!-- persistence -->
|
||||
<hibernate.version>4.3.8.Final</hibernate.version>
|
||||
<mysql-connector-java.version>5.1.35</mysql-connector-java.version>
|
||||
<spring-data-jpa.version>1.7.1.RELEASE</spring-data-jpa.version>
|
||||
|
||||
<!-- marshalling -->
|
||||
|
||||
<jackson.version>2.5.1</jackson.version>
|
||||
|
||||
<!-- logging -->
|
||||
<org.slf4j.version>1.7.12</org.slf4j.version>
|
||||
<logback.version>1.1.3</logback.version>
|
||||
|
||||
<!-- various -->
|
||||
<hibernate-validator.version>5.1.3.Final</hibernate-validator.version>
|
||||
|
||||
<!-- util -->
|
||||
<guava.version>18.0</guava.version>
|
||||
<commons-lang3.version>3.3.2</commons-lang3.version>
|
||||
|
||||
<!-- testing -->
|
||||
<org.hamcrest.version>1.3</org.hamcrest.version>
|
||||
<junit.version>4.11</junit.version>
|
||||
<mockito.version>1.10.19</mockito.version>
|
||||
|
||||
<httpcore.version>4.4</httpcore.version>
|
||||
<httpclient.version>4.4</httpclient.version>
|
||||
|
||||
<rest-assured.version>2.4.0</rest-assured.version>
|
||||
|
||||
<!-- Maven plugins -->
|
||||
<maven-compiler-plugin.version>3.3</maven-compiler-plugin.version>
|
||||
<maven-war-plugin.version>2.6</maven-war-plugin.version>
|
||||
<maven-surefire-plugin.version>2.18.1</maven-surefire-plugin.version>
|
||||
<cargo-maven2-plugin.version>1.4.12</cargo-maven2-plugin.version>
|
||||
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -1,114 +0,0 @@
|
|||
package org.baeldung.config;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.oauth2.client.filter.state.DefaultStateKeyGenerator;
|
||||
import org.springframework.security.oauth2.client.filter.state.StateKeyGenerator;
|
||||
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
|
||||
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
|
||||
import org.springframework.security.oauth2.client.resource.UserApprovalRequiredException;
|
||||
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
|
||||
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider;
|
||||
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
public class MyAuthorizationCodeAccessTokenProvider extends AuthorizationCodeAccessTokenProvider implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3822611002661972274L;
|
||||
|
||||
private final StateKeyGenerator stateKeyGenerator = new DefaultStateKeyGenerator();
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request) throws UserRedirectRequiredException, UserApprovalRequiredException, AccessDeniedException, OAuth2AccessDeniedException {
|
||||
final AuthorizationCodeResourceDetails resource = (AuthorizationCodeResourceDetails) details;
|
||||
|
||||
if (request.getAuthorizationCode() == null) {
|
||||
if (request.getStateKey() == null) {
|
||||
throw getRedirectForAuthorization(resource, request);
|
||||
}
|
||||
obtainAuthorizationCode(resource, request);
|
||||
}
|
||||
return retrieveToken(request, resource, getParametersForTokenRequest(resource, request), getHeadersForTokenRequest(request));
|
||||
}
|
||||
|
||||
private HttpHeaders getHeadersForTokenRequest(AccessTokenRequest request) {
|
||||
final HttpHeaders headers = new HttpHeaders();
|
||||
return headers;
|
||||
}
|
||||
|
||||
private MultiValueMap<String, String> getParametersForTokenRequest(AuthorizationCodeResourceDetails resource, AccessTokenRequest request) {
|
||||
final MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
|
||||
form.set("grant_type", "authorization_code");
|
||||
form.set("code", request.getAuthorizationCode());
|
||||
|
||||
final Object preservedState = request.getPreservedState();
|
||||
if (request.getStateKey() != null) {
|
||||
if (preservedState == null) {
|
||||
throw new InvalidRequestException("Possible CSRF detected - state parameter was present but no state could be found");
|
||||
}
|
||||
}
|
||||
|
||||
String redirectUri = null;
|
||||
if (preservedState instanceof String) {
|
||||
redirectUri = String.valueOf(preservedState);
|
||||
} else {
|
||||
redirectUri = resource.getRedirectUri(request);
|
||||
}
|
||||
|
||||
if ((redirectUri != null) && !"NONE".equals(redirectUri)) {
|
||||
form.set("redirect_uri", redirectUri);
|
||||
}
|
||||
|
||||
return form;
|
||||
}
|
||||
|
||||
private UserRedirectRequiredException getRedirectForAuthorization(AuthorizationCodeResourceDetails resource, AccessTokenRequest request) {
|
||||
final TreeMap<String, String> requestParameters = new TreeMap<String, String>();
|
||||
requestParameters.put("response_type", "code");
|
||||
requestParameters.put("client_id", resource.getClientId());
|
||||
requestParameters.put("duration", "permanent");
|
||||
|
||||
final String redirectUri = resource.getRedirectUri(request);
|
||||
if (redirectUri != null) {
|
||||
requestParameters.put("redirect_uri", redirectUri);
|
||||
}
|
||||
|
||||
if (resource.isScoped()) {
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final List<String> scope = resource.getScope();
|
||||
|
||||
if (scope != null) {
|
||||
final Iterator<String> scopeIt = scope.iterator();
|
||||
while (scopeIt.hasNext()) {
|
||||
builder.append(scopeIt.next());
|
||||
if (scopeIt.hasNext()) {
|
||||
builder.append(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestParameters.put("scope", builder.toString());
|
||||
}
|
||||
|
||||
final UserRedirectRequiredException redirectException = new UserRedirectRequiredException(resource.getUserAuthorizationUri(), requestParameters);
|
||||
|
||||
final String stateKey = stateKeyGenerator.generateKey(resource);
|
||||
redirectException.setStateKey(stateKey);
|
||||
request.setStateKey(stateKey);
|
||||
redirectException.setStateToPreserve(redirectUri);
|
||||
request.setPreservedState(redirectUri);
|
||||
|
||||
return redirectException;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
package org.baeldung.config;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
@PropertySource({ "classpath:persistence-${envTarget:dev}.properties" })
|
||||
@ComponentScan({ "org.baeldung.persistence" })
|
||||
@EnableJpaRepositories(basePackages = "org.baeldung.persistence.dao")
|
||||
public class PersistenceJPAConfig {
|
||||
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
public PersistenceJPAConfig() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
|
||||
em.setDataSource(dataSource());
|
||||
em.setPackagesToScan(new String[] { "org.baeldung.persistence.model" });
|
||||
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
|
||||
em.setJpaVendorAdapter(vendorAdapter);
|
||||
em.setJpaProperties(additionalProperties());
|
||||
return em;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
|
||||
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
|
||||
dataSource.setUrl(env.getProperty("jdbc.url"));
|
||||
dataSource.setUsername(env.getProperty("jdbc.user"));
|
||||
dataSource.setPassword(env.getProperty("jdbc.pass"));
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JpaTransactionManager transactionManager() {
|
||||
final JpaTransactionManager transactionManager = new JpaTransactionManager();
|
||||
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
|
||||
return transactionManager;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
|
||||
return new PersistenceExceptionTranslationPostProcessor();
|
||||
}
|
||||
|
||||
final Properties additionalProperties() {
|
||||
final Properties hibernateProperties = new Properties();
|
||||
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto", "create-drop"));
|
||||
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
|
||||
return hibernateProperties;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package org.baeldung.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web.ignoring().antMatchers("/resources/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.anonymous().disable()
|
||||
.csrf().disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/home.html","/post","/postSchedule","/posts").hasRole("USER")
|
||||
.and()
|
||||
.httpBasic().authenticationEntryPoint(oauth2AuthenticationEntryPoint())
|
||||
.and()
|
||||
.logout()
|
||||
.deleteCookies("JSESSIONID")
|
||||
.logoutUrl("/logout")
|
||||
.logoutSuccessUrl("/");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private LoginUrlAuthenticationEntryPoint oauth2AuthenticationEntryPoint() {
|
||||
return new LoginUrlAuthenticationEntryPoint("/login");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package org.baeldung.config;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
|
||||
|
||||
public class ServletInitializer extends AbstractDispatcherServletInitializer {
|
||||
|
||||
@Override
|
||||
protected WebApplicationContext createServletApplicationContext() {
|
||||
final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.register(PersistenceJPAConfig.class, WebConfig.class, SecurityConfig.class, ThymeleafConfig.class);
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getServletMappings() {
|
||||
return new String[] { "/" };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WebApplicationContext createRootApplicationContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||
super.onStartup(servletContext);
|
||||
servletContext.addListener(new SessionListener());
|
||||
registerProxyFilter(servletContext, "oauth2ClientContextFilter");
|
||||
registerProxyFilter(servletContext, "springSecurityFilterChain");
|
||||
}
|
||||
|
||||
private void registerProxyFilter(ServletContext servletContext, String name) {
|
||||
final DelegatingFilterProxy filter = new DelegatingFilterProxy(name);
|
||||
filter.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
|
||||
servletContext.addFilter(name, filter).addMappingForUrlPatterns(null, false, "/*");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package org.baeldung.config;
|
||||
|
||||
import javax.servlet.http.HttpSessionEvent;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
|
||||
import org.baeldung.reddit.util.MyFeatures;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SessionListener implements HttpSessionListener {
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
public void sessionCreated(HttpSessionEvent event) {
|
||||
logger.info("==== Session is created ====");
|
||||
event.getSession().setMaxInactiveInterval(60 * 60);
|
||||
event.getSession().setAttribute("PREDICTION_FEATURE", MyFeatures.PREDICTION_FEATURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionDestroyed(HttpSessionEvent event) {
|
||||
logger.info("==== Session is destroyed ====");
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package org.baeldung.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
import org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect;
|
||||
import org.thymeleaf.spring4.SpringTemplateEngine;
|
||||
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
|
||||
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
|
||||
import org.thymeleaf.templateresolver.TemplateResolver;
|
||||
|
||||
@Configuration
|
||||
public class ThymeleafConfig {
|
||||
@Bean
|
||||
public TemplateResolver templateResolver() {
|
||||
final ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
|
||||
templateResolver.setPrefix("/WEB-INF/jsp/");
|
||||
templateResolver.setSuffix(".jsp");
|
||||
|
||||
return templateResolver;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringTemplateEngine templateEngine() {
|
||||
final SpringTemplateEngine templateEngine = new SpringTemplateEngine();
|
||||
templateEngine.setTemplateResolver(templateResolver());
|
||||
templateEngine.addDialect(new SpringSecurityDialect());
|
||||
return templateEngine;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ViewResolver viewResolver() {
|
||||
final ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
|
||||
viewResolver.setTemplateEngine(templateEngine());
|
||||
viewResolver.setOrder(1);
|
||||
|
||||
return viewResolver;
|
||||
}
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
package org.baeldung.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.baeldung.persistence.dao.PostRepository;
|
||||
import org.baeldung.reddit.classifier.RedditClassifier;
|
||||
import org.baeldung.reddit.util.MyFeatures;
|
||||
import org.baeldung.reddit.util.UserAgentInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.security.oauth2.client.OAuth2ClientContext;
|
||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenProvider;
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenProviderChain;
|
||||
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider;
|
||||
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
|
||||
import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider;
|
||||
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@EnableScheduling
|
||||
@EnableAsync
|
||||
@ComponentScan({ "org.baeldung.web" })
|
||||
public class WebConfig extends WebMvcConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@Bean
|
||||
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ViewResolver viewResolver() {
|
||||
final InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
|
||||
viewResolver.setPrefix("/WEB-INF/jsp/");
|
||||
viewResolver.setSuffix(".jsp");
|
||||
return viewResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
|
||||
configurer.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addViewControllers(final ViewControllerRegistry registry) {
|
||||
super.addViewControllers(registry);
|
||||
registry.addViewController("/home.html");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OAuth2RestTemplate schedulerRedditTemplate(OAuth2ProtectedResourceDetails reddit) {
|
||||
final List<ClientHttpRequestInterceptor> list = new ArrayList<ClientHttpRequestInterceptor>();
|
||||
list.add(new UserAgentInterceptor());
|
||||
final OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(reddit);
|
||||
restTemplate.setInterceptors(list);
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedditClassifier redditClassifier() throws IOException {
|
||||
final RedditClassifier redditClassifier = new RedditClassifier();
|
||||
if (MyFeatures.PREDICTION_FEATURE.isActive()) {
|
||||
final Resource file = new ClassPathResource("data.csv");
|
||||
redditClassifier.trainClassifier(file.getFile().getAbsolutePath());
|
||||
}
|
||||
return redditClassifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableOAuth2Client
|
||||
@PropertySource("classpath:reddit.properties")
|
||||
protected static class ResourceConfiguration {
|
||||
|
||||
@Value("${accessTokenUri}")
|
||||
private String accessTokenUri;
|
||||
|
||||
@Value("${userAuthorizationUri}")
|
||||
private String userAuthorizationUri;
|
||||
|
||||
@Value("${clientID}")
|
||||
private String clientID;
|
||||
|
||||
@Value("${clientSecret}")
|
||||
private String clientSecret;
|
||||
|
||||
@Autowired
|
||||
private PostRepository repo;
|
||||
|
||||
@Bean
|
||||
public OAuth2ProtectedResourceDetails reddit() {
|
||||
final AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
|
||||
details.setId("reddit");
|
||||
details.setClientId(clientID);
|
||||
details.setClientSecret(clientSecret);
|
||||
details.setAccessTokenUri(accessTokenUri);
|
||||
details.setUserAuthorizationUri(userAuthorizationUri);
|
||||
details.setTokenName("oauth_token");
|
||||
details.setScope(Arrays.asList("identity", "read", "submit", "edit"));
|
||||
details.setGrantType("authorization_code");
|
||||
details.setPreEstablishedRedirectUri("http://localhost:8080/spring-security-oauth/login");
|
||||
details.setUseCurrentUri(false);
|
||||
return details;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OAuth2RestTemplate redditRestTemplate(OAuth2ClientContext clientContext) {
|
||||
final OAuth2RestTemplate template = new OAuth2RestTemplate(reddit(), clientContext);
|
||||
final List<ClientHttpRequestInterceptor> list = new ArrayList<ClientHttpRequestInterceptor>();
|
||||
list.add(new UserAgentInterceptor());
|
||||
template.setInterceptors(list);
|
||||
final AccessTokenProviderChain accessTokenProvider = new AccessTokenProviderChain(Arrays.<AccessTokenProvider> asList(new MyAuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider(),
|
||||
new ClientCredentialsAccessTokenProvider()));
|
||||
template.setAccessTokenProvider(accessTokenProvider);
|
||||
return template;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void startupCheck() {
|
||||
if (StringUtils.isBlank(accessTokenUri) || StringUtils.isBlank(userAuthorizationUri) || StringUtils.isBlank(clientID) || StringUtils.isBlank(clientSecret)) {
|
||||
throw new RuntimeException("Incomplete reddit properties");
|
||||
}
|
||||
repo.findAll();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package org.baeldung.persistence.dao;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.baeldung.persistence.model.Post;
|
||||
import org.baeldung.persistence.model.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface PostRepository extends JpaRepository<Post, Long> {
|
||||
|
||||
List<Post> findBySubmissionDateBeforeAndIsSent(Date date, boolean isSent);
|
||||
|
||||
List<Post> findByUser(User user);
|
||||
|
||||
List<Post> findByRedditIDNotNullAndNoOfAttemptsGreaterThan(int attempts);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package org.baeldung.persistence.dao;
|
||||
|
||||
import org.baeldung.persistence.model.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
|
||||
User findByUsername(String username);
|
||||
|
||||
User findByAccessToken(String token);
|
||||
}
|
|
@ -1,163 +0,0 @@
|
|||
package org.baeldung.persistence.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
@Entity
|
||||
public class Post {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String title;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String subreddit;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String url;
|
||||
|
||||
private boolean sendReplies;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Date submissionDate;
|
||||
|
||||
private boolean isSent;
|
||||
|
||||
private String submissionResponse;
|
||||
|
||||
private String redditID;
|
||||
|
||||
private int noOfAttempts;
|
||||
|
||||
private int timeInterval;
|
||||
|
||||
private int minScoreRequired;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
public Post() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getSubreddit() {
|
||||
return subreddit;
|
||||
}
|
||||
|
||||
public void setSubreddit(String subreddit) {
|
||||
this.subreddit = subreddit;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public boolean isSendReplies() {
|
||||
return sendReplies;
|
||||
}
|
||||
|
||||
public void setSendReplies(boolean sendReplies) {
|
||||
this.sendReplies = sendReplies;
|
||||
}
|
||||
|
||||
public Date getSubmissionDate() {
|
||||
return submissionDate;
|
||||
}
|
||||
|
||||
public void setSubmissionDate(Date submissionDate) {
|
||||
this.submissionDate = submissionDate;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public boolean isSent() {
|
||||
return isSent;
|
||||
}
|
||||
|
||||
public void setSent(boolean isSent) {
|
||||
this.isSent = isSent;
|
||||
}
|
||||
|
||||
public String getSubmissionResponse() {
|
||||
return submissionResponse;
|
||||
}
|
||||
|
||||
public void setSubmissionResponse(String submissionResponse) {
|
||||
this.submissionResponse = submissionResponse;
|
||||
}
|
||||
|
||||
public String getRedditID() {
|
||||
return redditID;
|
||||
}
|
||||
|
||||
public void setRedditID(String redditID) {
|
||||
this.redditID = redditID;
|
||||
}
|
||||
|
||||
public int getNoOfAttempts() {
|
||||
return noOfAttempts;
|
||||
}
|
||||
|
||||
public void setNoOfAttempts(int noOfAttempts) {
|
||||
this.noOfAttempts = noOfAttempts;
|
||||
}
|
||||
|
||||
public int getTimeInterval() {
|
||||
return timeInterval;
|
||||
}
|
||||
|
||||
public void setTimeInterval(int timeInterval) {
|
||||
this.timeInterval = timeInterval;
|
||||
}
|
||||
|
||||
public int getMinScoreRequired() {
|
||||
return minScoreRequired;
|
||||
}
|
||||
|
||||
public void setMinScoreRequired(int minScoreRequired) {
|
||||
this.minScoreRequired = minScoreRequired;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Post [title=" + title + ", subreddit=" + subreddit + ", url=" + url + ", submissionDate=" + submissionDate + ", user=" + user + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
package org.baeldung.persistence.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "APP_USER")
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String username;
|
||||
|
||||
private String accessToken;
|
||||
|
||||
private String refreshToken;
|
||||
|
||||
private Date tokenExpiration;
|
||||
|
||||
private boolean needCaptcha;
|
||||
|
||||
public User() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(final String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(final String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
public void setRefreshToken(final String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
public Date getTokenExpiration() {
|
||||
return tokenExpiration;
|
||||
}
|
||||
|
||||
public void setTokenExpiration(final Date tokenExpiration) {
|
||||
this.tokenExpiration = tokenExpiration;
|
||||
}
|
||||
|
||||
public boolean isCaptchaNeeded() {
|
||||
return needCaptcha;
|
||||
}
|
||||
|
||||
public void setNeedCaptcha(final boolean needCaptcha) {
|
||||
this.needCaptcha = needCaptcha;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = (prime * result) + ((username == null) ? 0 : username.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final User user = (User) obj;
|
||||
if (!username.equals(user.username)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User [username=" + username + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
package org.baeldung.persistence.service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.baeldung.persistence.dao.PostRepository;
|
||||
import org.baeldung.persistence.model.Post;
|
||||
import org.baeldung.persistence.model.User;
|
||||
import org.baeldung.reddit.util.RedditApiConstants;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
@Service
|
||||
public class RedditService {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("schedulerRedditTemplate")
|
||||
private OAuth2RestTemplate redditRestTemplate;
|
||||
|
||||
@Autowired
|
||||
private PostRepository postReopsitory;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
public void submitPost(final Post post) {
|
||||
try {
|
||||
submitPostInternal(post);
|
||||
} catch (final Exception e) {
|
||||
logger.error("Error occurred while submitting post " + post.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getPostScore(String redditId) {
|
||||
final JsonNode node = redditRestTemplate.getForObject("https://oauth.reddit.com/api/info?id=t3_" + redditId, JsonNode.class);
|
||||
logger.info(node.toString());
|
||||
final int score = node.get("data").get("children").get(0).get("data").get("score").asInt();
|
||||
logger.info("post score = " + score);
|
||||
return score;
|
||||
}
|
||||
|
||||
public void deletePost(String redditId) {
|
||||
final MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>();
|
||||
param.add("id", "t3_" + redditId);
|
||||
final JsonNode node = redditRestTemplate.postForObject("https://oauth.reddit.com/api/del.json", param, JsonNode.class);
|
||||
logger.info(node.toString());
|
||||
}
|
||||
|
||||
public void checkAndReSubmit(Post post) {
|
||||
try {
|
||||
checkAndReSubmitInternal(post);
|
||||
} catch (final Exception e) {
|
||||
logger.error("Error occurred while check post " + post.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// === private methods
|
||||
|
||||
private void submitPostInternal(final Post post) {
|
||||
final User user = post.getUser();
|
||||
final DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(user.getAccessToken());
|
||||
token.setRefreshToken(new DefaultOAuth2RefreshToken((user.getRefreshToken())));
|
||||
token.setExpiration(user.getTokenExpiration());
|
||||
redditRestTemplate.getOAuth2ClientContext().setAccessToken(token);
|
||||
//
|
||||
final UsernamePasswordAuthenticationToken userAuthToken = new UsernamePasswordAuthenticationToken(user.getUsername(), token.getValue(), Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
|
||||
SecurityContextHolder.getContext().setAuthentication(userAuthToken);
|
||||
//
|
||||
final MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>();
|
||||
param.add(RedditApiConstants.TITLE, post.getTitle());
|
||||
param.add(RedditApiConstants.SR, post.getSubreddit());
|
||||
param.add(RedditApiConstants.URL, post.getUrl());
|
||||
param.add(RedditApiConstants.API_TYPE, "json");
|
||||
param.add(RedditApiConstants.KIND, "link");
|
||||
param.add(RedditApiConstants.RESUBMIT, "true");
|
||||
param.add(RedditApiConstants.THEN, "comments");
|
||||
if (post.isSendReplies()) {
|
||||
param.add(RedditApiConstants.SENDREPLIES, "true");
|
||||
}
|
||||
|
||||
logger.info("Submit link with these parameters: " + param.entrySet());
|
||||
final JsonNode node = redditRestTemplate.postForObject("https://oauth.reddit.com/api/submit", param, JsonNode.class);
|
||||
parseResponse(node, post);
|
||||
}
|
||||
|
||||
private void parseResponse(JsonNode node, Post post) {
|
||||
final JsonNode errorNode = node.get("json").get("errors").get(0);
|
||||
if (errorNode == null) {
|
||||
post.setSent(true);
|
||||
post.setSubmissionResponse("Successfully sent");
|
||||
post.setRedditID(node.get("json").get("data").get("id").asText());
|
||||
post.setNoOfAttempts(post.getNoOfAttempts() - 1);
|
||||
postReopsitory.save(post);
|
||||
logger.info("Successfully sent " + post.toString());
|
||||
} else {
|
||||
post.setSubmissionResponse(errorNode.toString());
|
||||
postReopsitory.save(post);
|
||||
logger.info("Error occurred: " + errorNode.toString() + "while submitting post " + post.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAndReSubmitInternal(Post post) {
|
||||
if (didIntervalPassed(post.getSubmissionDate(), post.getTimeInterval())) {
|
||||
final int score = getPostScore(post.getRedditID());
|
||||
if (score < post.getMinScoreRequired()) {
|
||||
deletePost(post.getRedditID());
|
||||
resetPost(post);
|
||||
} else {
|
||||
post.setNoOfAttempts(0);
|
||||
postReopsitory.save(post);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean didIntervalPassed(Date submissonDate, int postInterval) {
|
||||
final long currentTime = new Date().getTime();
|
||||
final long interval = currentTime - submissonDate.getTime();
|
||||
final long intervalInMinutes = TimeUnit.MINUTES.convert(interval, TimeUnit.MILLISECONDS);
|
||||
return intervalInMinutes > postInterval;
|
||||
}
|
||||
|
||||
private void resetPost(Post post) {
|
||||
long time = new Date().getTime();
|
||||
time += TimeUnit.MILLISECONDS.convert(post.getTimeInterval(), TimeUnit.MINUTES);
|
||||
post.setRedditID(null);
|
||||
post.setSubmissionDate(new Date(time));
|
||||
post.setSent(false);
|
||||
post.setSubmissionResponse("Not sent yet");
|
||||
postReopsitory.save(post);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
package org.baeldung.reddit.classifier;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.mahout.classifier.sgd.AdaptiveLogisticRegression;
|
||||
import org.apache.mahout.classifier.sgd.CrossFoldLearner;
|
||||
import org.apache.mahout.classifier.sgd.L2;
|
||||
import org.apache.mahout.math.NamedVector;
|
||||
import org.apache.mahout.math.RandomAccessSparseVector;
|
||||
import org.apache.mahout.math.Vector;
|
||||
import org.apache.mahout.vectorizer.encoders.AdaptiveWordValueEncoder;
|
||||
import org.apache.mahout.vectorizer.encoders.FeatureVectorEncoder;
|
||||
import org.apache.mahout.vectorizer.encoders.StaticWordValueEncoder;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
public class RedditClassifier {
|
||||
public static int GOOD = 0;
|
||||
public static int BAD = 1;
|
||||
|
||||
private final int[] trainCount = { 0, 0 };
|
||||
private final int[] evalCount = { 0, 0 };
|
||||
private final int[] correctCount = { 0, 0 };
|
||||
|
||||
private final AdaptiveLogisticRegression classifier;
|
||||
private final FeatureVectorEncoder titleEncoder;
|
||||
private final FeatureVectorEncoder domainEncoder;
|
||||
private final int noOfFeatures;
|
||||
private final int minScore;
|
||||
private CrossFoldLearner learner;
|
||||
private double accuracy;
|
||||
|
||||
public RedditClassifier() {
|
||||
this(150, 1000, 7);
|
||||
}
|
||||
|
||||
public RedditClassifier(final int poolSize, final int noOfFeatures, int minScore) {
|
||||
this.minScore = minScore;
|
||||
this.noOfFeatures = noOfFeatures;
|
||||
classifier = new AdaptiveLogisticRegression(2, noOfFeatures, new L2());
|
||||
classifier.setPoolSize(poolSize);
|
||||
titleEncoder = new AdaptiveWordValueEncoder("title");
|
||||
titleEncoder.setProbes(1);
|
||||
domainEncoder = new StaticWordValueEncoder("domain");
|
||||
domainEncoder.setProbes(1);
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
public void trainClassifier(final String fileName) throws IOException {
|
||||
final List<NamedVector> vectors = extractVectors(readDataFile(fileName));
|
||||
final int size = vectors.size();
|
||||
final int noOfTraining = (int) (size * 0.8);
|
||||
final List<NamedVector> trainingData = vectors.subList(0, noOfTraining);
|
||||
final List<NamedVector> testData = vectors.subList(noOfTraining, size);
|
||||
int category;
|
||||
for (final NamedVector vector : trainingData) {
|
||||
category = (vector.getName() == "GOOD") ? GOOD : BAD;
|
||||
classifier.train(category, vector);
|
||||
trainCount[category]++;
|
||||
}
|
||||
System.out.println("Training count ========= Good = " + trainCount[0] + " ___ Bad = " + trainCount[1]);
|
||||
System.out.println("----------------------------------------------------------------- \n");
|
||||
evaluateClassifier(testData);
|
||||
}
|
||||
|
||||
public Vector convertPost(final String title, final String domain, final int hour) {
|
||||
final Vector vector = new RandomAccessSparseVector(noOfFeatures);
|
||||
final List<String> words = Splitter.onPattern("\\W").omitEmptyStrings().splitToList(title);
|
||||
vector.set(0, hour);
|
||||
vector.set(1, words.size());
|
||||
domainEncoder.addToVector(domain, vector);
|
||||
for (final String word : words) {
|
||||
titleEncoder.addToVector(word, vector);
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
|
||||
public int classify(final Vector features) {
|
||||
if (learner == null) {
|
||||
learner = classifier.getBest().getPayload().getLearner();
|
||||
}
|
||||
return learner.classifyFull(features).maxValueIndex();
|
||||
}
|
||||
|
||||
public double getAccuracy() {
|
||||
return accuracy;
|
||||
}
|
||||
|
||||
// ==== Private methods
|
||||
|
||||
private void evaluateClassifier(final List<NamedVector> vectors) throws IOException {
|
||||
int category, result;
|
||||
int correct = 0;
|
||||
int wrong = 0;
|
||||
for (final NamedVector vector : vectors) {
|
||||
category = (vector.getName() == "GOOD") ? GOOD : BAD;
|
||||
result = classify(vector);
|
||||
|
||||
evalCount[category]++;
|
||||
if (category == result) {
|
||||
correct++;
|
||||
correctCount[result]++;
|
||||
} else {
|
||||
wrong++;
|
||||
}
|
||||
}
|
||||
System.out.println("Eval count =================== Good = " + evalCount[0] + " ----- Bad = " + evalCount[1] + "\n");
|
||||
System.out.println("Overall Evaluation ============= Correct prediction = " + correct + " ----- Wrong prediction = " + wrong);
|
||||
System.out.println("Correctly Evaluated =========== Correct Good = " + correctCount[0] + " ----- Correct Bad = " + correctCount[1]);
|
||||
System.out.println("Correctly Evaluated (%) ======== Good accuracy = " + (correctCount[0] / (evalCount[0] + 0.0)) + " ----- Bad accuracy = " + (correctCount[1] / (evalCount[1] + 0.0)));
|
||||
this.accuracy = correct / (wrong + correct + 0.0);
|
||||
}
|
||||
|
||||
private List<String> readDataFile(final String fileName) throws IOException {
|
||||
List<String> lines = Files.readLines(new File(fileName), Charset.forName("utf-8"));
|
||||
if ((lines == null) || (lines.size() == 0)) {
|
||||
new RedditDataCollector().collectData();
|
||||
lines = Files.readLines(new File(fileName), Charset.forName("utf-8"));
|
||||
}
|
||||
lines.remove(0);
|
||||
return lines;
|
||||
}
|
||||
|
||||
private List<NamedVector> extractVectors(final List<String> lines) {
|
||||
final List<NamedVector> vectors = new ArrayList<NamedVector>(lines.size());
|
||||
for (final String line : lines) {
|
||||
vectors.add(extractVector(line));
|
||||
}
|
||||
return vectors;
|
||||
}
|
||||
|
||||
private NamedVector extractVector(final String line) {
|
||||
final String[] items = line.split(",");
|
||||
final String numberOfVotes = items[0];
|
||||
final String time = items[1];
|
||||
final String numberOfWordInTitle = items[2];
|
||||
final String title = items[3];
|
||||
final String theRootDomain = items[4];
|
||||
|
||||
final RandomAccessSparseVector internalVector = new RandomAccessSparseVector(noOfFeatures);
|
||||
|
||||
final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
cal.setTimeInMillis(Long.parseLong(time) * 1000);
|
||||
internalVector.set(0, cal.get(Calendar.HOUR_OF_DAY)); // hour of day
|
||||
|
||||
internalVector.set(1, Integer.parseInt(numberOfWordInTitle)); // number of words in the title
|
||||
|
||||
domainEncoder.addToVector(theRootDomain, internalVector);
|
||||
final List<String> words = Splitter.on(' ').splitToList(title);
|
||||
words.stream().filter(word -> word.length() > 2).forEach(word -> titleEncoder.addToVector(word, internalVector));
|
||||
|
||||
final String category = extractCategory(Integer.parseInt(numberOfVotes));
|
||||
return new NamedVector(internalVector, category);
|
||||
}
|
||||
|
||||
private String extractCategory(final int score) {
|
||||
return (score < minScore) ? "BAD" : "GOOD";
|
||||
}
|
||||
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
package org.baeldung.reddit.classifier;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.baeldung.reddit.util.UserAgentInterceptor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
|
||||
public class RedditDataCollector {
|
||||
public static final String DATA_FILE = "src/main/resources/data.csv";
|
||||
public static final int DATA_SIZE = 20000;
|
||||
public static final int LIMIT = 100;
|
||||
public static final Long YEAR = 31536000L;
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private Long timestamp;
|
||||
private final RestTemplate restTemplate;
|
||||
private final String subreddit;
|
||||
|
||||
public RedditDataCollector() {
|
||||
this("java");
|
||||
}
|
||||
|
||||
public RedditDataCollector(String subreddit) {
|
||||
restTemplate = new RestTemplate();
|
||||
final List<ClientHttpRequestInterceptor> list = new ArrayList<ClientHttpRequestInterceptor>();
|
||||
list.add(new UserAgentInterceptor());
|
||||
restTemplate.setInterceptors(list);
|
||||
this.subreddit = subreddit;
|
||||
}
|
||||
|
||||
public void collectData() throws IOException {
|
||||
final int noOfRounds = DATA_SIZE / LIMIT;
|
||||
timestamp = System.currentTimeMillis() / 1000;
|
||||
final FileWriter writer = new FileWriter(DATA_FILE);
|
||||
writer.write("Score, Timestamp in utc, Number of wrods in title, Title, Domain \n");
|
||||
for (int i = 0; i < noOfRounds; i++) {
|
||||
getPosts(writer);
|
||||
System.out.println(i);
|
||||
}
|
||||
writer.close();
|
||||
}
|
||||
|
||||
// ==== Private
|
||||
|
||||
private void getPosts(FileWriter writer) {
|
||||
final String fullUrl = "http://www.reddit.com/r/" + subreddit + "/search.json?sort=new&q=timestamp:" + (timestamp - YEAR) + ".." + timestamp + "&restrict_sr=on&syntax=cloudsearch&limit=" + LIMIT;
|
||||
try {
|
||||
final JsonNode node = restTemplate.getForObject(fullUrl, JsonNode.class);
|
||||
parseNode(node, writer);
|
||||
Thread.sleep(3000);
|
||||
} catch (final Exception e) {
|
||||
logger.error("server error", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void parseNode(JsonNode node, FileWriter writer) throws IOException {
|
||||
String line;
|
||||
List<String> words;
|
||||
int score;
|
||||
for (final JsonNode child : node.get("data").get("children")) {
|
||||
score = child.get("data").get("score").asInt();
|
||||
words = Splitter.onPattern("\\W").omitEmptyStrings().splitToList(child.get("data").get("title").asText());
|
||||
timestamp = child.get("data").get("created_utc").asLong();
|
||||
|
||||
line = score + ",";
|
||||
line += timestamp + ",";
|
||||
line += words.size() + "," + Joiner.on(' ').join(words) + ",";
|
||||
line += child.get("data").get("domain").asText() + "\n";
|
||||
writer.write(line);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
final RedditDataCollector collector = new RedditDataCollector();
|
||||
collector.collectData();
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package org.baeldung.reddit.util;
|
||||
|
||||
public enum MyFeatures {
|
||||
|
||||
PREDICTION_FEATURE(false);
|
||||
|
||||
private boolean active;
|
||||
|
||||
private MyFeatures(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package org.baeldung.reddit.util;
|
||||
|
||||
/**
|
||||
* Specified by Reddit API at http://www.reddit.com/dev/api#POST_api_submit
|
||||
*/
|
||||
public final class RedditApiConstants {
|
||||
public static final String API_TYPE = "api_type";
|
||||
public static final String URL = "url";
|
||||
public static final String SR = "sr";
|
||||
public static final String TITLE = "title";
|
||||
public static final String THEN = "then";
|
||||
public static final String SENDREPLIES = "sendreplies";
|
||||
public static final String RESUBMIT = "resubmit";
|
||||
public static final String KIND = "kind";
|
||||
|
||||
private RedditApiConstants() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package org.baeldung.reddit.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
public class UserAgentInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
|
||||
|
||||
final HttpHeaders headers = request.getHeaders();
|
||||
headers.add("User-Agent", "Schedule with Reddit");
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
}
|
|
@ -1,275 +0,0 @@
|
|||
package org.baeldung.web;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.baeldung.persistence.dao.PostRepository;
|
||||
import org.baeldung.persistence.dao.UserRepository;
|
||||
import org.baeldung.persistence.model.Post;
|
||||
import org.baeldung.persistence.model.User;
|
||||
import org.baeldung.reddit.classifier.RedditClassifier;
|
||||
import org.baeldung.reddit.util.RedditApiConstants;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
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.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
@Controller
|
||||
public class RedditController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
private final SimpleDateFormat dfHour = new SimpleDateFormat("HH");
|
||||
|
||||
@Autowired
|
||||
@Qualifier("redditRestTemplate")
|
||||
private OAuth2RestTemplate redditRestTemplate;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userReopsitory;
|
||||
|
||||
@Autowired
|
||||
private PostRepository postReopsitory;
|
||||
|
||||
@Autowired
|
||||
private RedditClassifier redditClassifier;
|
||||
|
||||
@RequestMapping("/login")
|
||||
public final String redditLogin() {
|
||||
final JsonNode node = redditRestTemplate.getForObject("https://oauth.reddit.com/api/v1/me", JsonNode.class);
|
||||
loadAuthentication(node.get("name").asText(), redditRestTemplate.getAccessToken());
|
||||
return "redirect:home.html";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/submit", method = RequestMethod.POST)
|
||||
public final String submit(final Model model, @RequestParam final Map<String, String> formParams) {
|
||||
final MultiValueMap<String, String> param1 = constructParams(formParams);
|
||||
|
||||
logger.info("Submitting Link with these parameters: " + param1);
|
||||
final JsonNode node = redditRestTemplate.postForObject("https://oauth.reddit.com/api/submit", param1, JsonNode.class);
|
||||
logger.info("Submitted Link - Full Response from Reddit: " + node.toString());
|
||||
final String responseMsg = parseResponse(node);
|
||||
model.addAttribute("msg", responseMsg);
|
||||
return "submissionResponse";
|
||||
}
|
||||
|
||||
@RequestMapping("/post")
|
||||
public final String showSubmissionForm(final Model model) {
|
||||
final boolean isCaptchaNeeded = getCurrentUser().isCaptchaNeeded();
|
||||
if (isCaptchaNeeded) {
|
||||
final String iden = getNewCaptcha();
|
||||
model.addAttribute("iden", iden);
|
||||
}
|
||||
return "submissionForm";
|
||||
}
|
||||
|
||||
@RequestMapping("/postSchedule")
|
||||
public final String showSchedulePostForm(final Model model) {
|
||||
final boolean isCaptchaNeeded = getCurrentUser().isCaptchaNeeded();
|
||||
if (isCaptchaNeeded) {
|
||||
model.addAttribute("msg", "Sorry, You do not have enought karma");
|
||||
return "submissionResponse";
|
||||
}
|
||||
return "schedulePostForm";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/schedule", method = RequestMethod.POST)
|
||||
public final String schedule(final Model model, @RequestParam final Map<String, String> formParams) throws ParseException {
|
||||
logger.info("User scheduling Post with these parameters: " + formParams.entrySet());
|
||||
final User user = getCurrentUser();
|
||||
final Post post = new Post();
|
||||
post.setUser(user);
|
||||
post.setSent(false);
|
||||
post.setTitle(formParams.get("title"));
|
||||
post.setSubreddit(formParams.get("sr"));
|
||||
post.setUrl(formParams.get("url"));
|
||||
post.setNoOfAttempts(Integer.parseInt(formParams.get("attempt")));
|
||||
post.setTimeInterval(Integer.parseInt(formParams.get("interval")));
|
||||
post.setMinScoreRequired(Integer.parseInt(formParams.get("score")));
|
||||
|
||||
if (formParams.containsKey("sendreplies")) {
|
||||
post.setSendReplies(true);
|
||||
}
|
||||
post.setSubmissionDate(dateFormat.parse(formParams.get("date")));
|
||||
post.setSubmissionResponse("Not sent yet");
|
||||
if (post.getSubmissionDate().before(new Date())) {
|
||||
model.addAttribute("msg", "Invalid date");
|
||||
return "submissionResponse";
|
||||
}
|
||||
postReopsitory.save(post);
|
||||
final List<Post> posts = postReopsitory.findByUser(user);
|
||||
model.addAttribute("posts", posts);
|
||||
return "postListView";
|
||||
}
|
||||
|
||||
@RequestMapping("/posts")
|
||||
public final String getScheduledPosts(final Model model) {
|
||||
final User user = getCurrentUser();
|
||||
final List<Post> posts = postReopsitory.findByUser(user);
|
||||
model.addAttribute("posts", posts);
|
||||
return "postListView";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/predicatePostResponse", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public final String predicatePostResponse(@RequestParam(value = "title") final String title, @RequestParam(value = "domain") final String domain) {
|
||||
final int hour = Integer.parseInt(dfHour.format(new Date()));
|
||||
final int result = redditClassifier.classify(redditClassifier.convertPost(title, domain, hour));
|
||||
return (result == RedditClassifier.GOOD) ? "{Good Response}" : "{Bad response}";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/checkIfAlreadySubmitted", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public String checkIfAlreadySubmitted(@RequestParam("url") final String url, @RequestParam("sr") final String sr) {
|
||||
logger.info("check if already submitted");
|
||||
final JsonNode node = redditRestTemplate.getForObject("https://oauth.reddit.com/r/" + sr + "/search?q=url:" + url + "&restrict_sr=on", JsonNode.class);
|
||||
logger.info(node.toString());
|
||||
return node.get("data").get("children").toString();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/subredditAutoComplete")
|
||||
@ResponseBody
|
||||
public String subredditAutoComplete(@RequestParam("term") final String term) {
|
||||
final MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>();
|
||||
param.add("query", term);
|
||||
final JsonNode node = redditRestTemplate.postForObject("https://oauth.reddit.com//api/search_reddit_names", param, JsonNode.class);
|
||||
return node.get("names").toString();
|
||||
}
|
||||
|
||||
// === post actions
|
||||
|
||||
@RequestMapping(value = "/deletePost/{id}", method = RequestMethod.DELETE)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public void deletePost(@PathVariable("id") final Long id) {
|
||||
postReopsitory.delete(id);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/editPost/{id}", method = RequestMethod.GET)
|
||||
public String showEditPostForm(final Model model, @PathVariable Long id) {
|
||||
final Post post = postReopsitory.findOne(id);
|
||||
model.addAttribute("post", post);
|
||||
model.addAttribute("dateValue", dateFormat.format(post.getSubmissionDate()));
|
||||
return "editPostForm";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/updatePost/{id}", method = RequestMethod.POST)
|
||||
public String updatePost(Model model, @PathVariable("id") final Long id, @RequestParam final Map<String, String> formParams) throws ParseException {
|
||||
final Post post = postReopsitory.findOne(id);
|
||||
post.setTitle(formParams.get("title"));
|
||||
post.setSubreddit(formParams.get("sr"));
|
||||
post.setUrl(formParams.get("url"));
|
||||
|
||||
post.setNoOfAttempts(Integer.parseInt(formParams.get("attempt")));
|
||||
post.setTimeInterval(Integer.parseInt(formParams.get("interval")));
|
||||
post.setMinScoreRequired(Integer.parseInt(formParams.get("score")));
|
||||
|
||||
if (formParams.containsKey("sendreplies")) {
|
||||
post.setSendReplies(true);
|
||||
} else {
|
||||
post.setSendReplies(false);
|
||||
}
|
||||
post.setSubmissionDate(dateFormat.parse(formParams.get("date")));
|
||||
if (post.getSubmissionDate().before(new Date())) {
|
||||
model.addAttribute("msg", "Invalid date");
|
||||
return "submissionResponse";
|
||||
}
|
||||
postReopsitory.save(post);
|
||||
return "redirect:/posts";
|
||||
}
|
||||
|
||||
// === private
|
||||
|
||||
private User getCurrentUser() {
|
||||
return (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
}
|
||||
|
||||
private final MultiValueMap<String, String> constructParams(final Map<String, String> formParams) {
|
||||
final MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>();
|
||||
param.add(RedditApiConstants.API_TYPE, "json");
|
||||
param.add(RedditApiConstants.KIND, "link");
|
||||
param.add(RedditApiConstants.RESUBMIT, "true");
|
||||
param.add(RedditApiConstants.THEN, "comments");
|
||||
for (final Map.Entry<String, String> entry : formParams.entrySet()) {
|
||||
param.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
private final String needsCaptcha() {
|
||||
final String result = redditRestTemplate.getForObject("https://oauth.reddit.com/api/needs_captcha.json", String.class);
|
||||
return result;
|
||||
}
|
||||
|
||||
private final String getNewCaptcha() {
|
||||
final Map<String, String> param = new HashMap<String, String>();
|
||||
param.put("api_type", "json");
|
||||
|
||||
final String result = redditRestTemplate.postForObject("https://oauth.reddit.com/api/new_captcha", param, String.class, param);
|
||||
final String[] split = result.split("\"");
|
||||
return split[split.length - 2];
|
||||
}
|
||||
|
||||
private final String parseResponse(final JsonNode node) {
|
||||
String result = "";
|
||||
final JsonNode errorNode = node.get("json").get("errors").get(0);
|
||||
if (errorNode != null) {
|
||||
for (final JsonNode child : errorNode) {
|
||||
result = result + child.toString().replaceAll("\"|null", "") + "<br>";
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
if ((node.get("json").get("data") != null) && (node.get("json").get("data").get("url") != null)) {
|
||||
return "Post submitted successfully " + node.get("json").get("data").get("url").asText();
|
||||
} else {
|
||||
return "Error Occurred";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAuthentication(final String name, final OAuth2AccessToken token) {
|
||||
User user = userReopsitory.findByUsername(name);
|
||||
if (user == null) {
|
||||
user = new User();
|
||||
user.setUsername(name);
|
||||
}
|
||||
|
||||
if (needsCaptcha().equalsIgnoreCase("true")) {
|
||||
user.setNeedCaptcha(true);
|
||||
} else {
|
||||
user.setNeedCaptcha(false);
|
||||
}
|
||||
|
||||
user.setAccessToken(token.getValue());
|
||||
user.setRefreshToken(token.getRefreshToken().getValue());
|
||||
user.setTokenExpiration(token.getExpiration());
|
||||
userReopsitory.save(user);
|
||||
|
||||
final UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, token.getValue(), Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package org.baeldung.web;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
|
||||
import org.springframework.security.oauth2.client.resource.UserApprovalRequiredException;
|
||||
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||
|
||||
@ControllerAdvice
|
||||
public class RestExceptionHandler extends ResponseEntityExceptionHandler implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3861125729653781371L;
|
||||
|
||||
public RestExceptionHandler() {
|
||||
super();
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
// 4xx
|
||||
|
||||
@ExceptionHandler({ OAuth2AccessDeniedException.class })
|
||||
public ModelAndView handleOAuth2AccessDeniedException(final OAuth2AccessDeniedException ex, final WebRequest request) {
|
||||
logger.error("403 Status Code", ex);
|
||||
final String response = "Error Occurred - Forbidden: " + ex.getMessage();
|
||||
final ModelAndView model = new ModelAndView("submissionResponse");
|
||||
model.addObject("msg", response);
|
||||
return model;
|
||||
// return handleExceptionInternal(ex, response, new HttpHeaders(), HttpStatus.FORBIDDEN, request);
|
||||
}
|
||||
|
||||
@ExceptionHandler({ HttpClientErrorException.class })
|
||||
public ModelAndView handleHttpClientErrorException(final HttpClientErrorException ex, final WebRequest request) {
|
||||
logger.error("400 Status Code", ex);
|
||||
final String response = "Error Occurred - To Many Requests: " + ex.getMessage();
|
||||
final ModelAndView model = new ModelAndView("submissionResponse");
|
||||
model.addObject("msg", response);
|
||||
return model;
|
||||
// return handleExceptionInternal(ex, response, new HttpHeaders(), HttpStatus.TOO_MANY_REQUESTS, request);
|
||||
}
|
||||
|
||||
// HttpClientErrorException
|
||||
// 500
|
||||
|
||||
@ExceptionHandler({ UserApprovalRequiredException.class, UserRedirectRequiredException.class })
|
||||
public ResponseEntity<Object> handleRedirect(final RuntimeException ex, final WebRequest request) {
|
||||
logger.info(ex.getLocalizedMessage());
|
||||
throw ex;
|
||||
}
|
||||
|
||||
@ExceptionHandler({ Exception.class })
|
||||
public ModelAndView handleInternal(final RuntimeException ex, final WebRequest request, final HttpServletResponse response) {
|
||||
logger.info(response.getHeader("x-ratelimit-remaining"));
|
||||
logger.error("500 Status Code", ex);
|
||||
final String message = "Error Occurred: " + ex.getLocalizedMessage();
|
||||
final ModelAndView model = new ModelAndView("submissionResponse");
|
||||
model.addObject("msg", message);
|
||||
return model;
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package org.baeldung.web.schedule;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.baeldung.persistence.dao.PostRepository;
|
||||
import org.baeldung.persistence.model.Post;
|
||||
import org.baeldung.persistence.service.RedditService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class RedditScheduler {
|
||||
|
||||
@Autowired
|
||||
private RedditService service;
|
||||
|
||||
@Autowired
|
||||
private PostRepository postReopsitory;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Scheduled(fixedRate = 1 * 60 * 1000)
|
||||
public void schedulePosts() {
|
||||
final List<Post> posts = postReopsitory.findBySubmissionDateBeforeAndIsSent(new Date(), false);
|
||||
logger.info(posts.size() + " Posts in the queue.");
|
||||
for (final Post post : posts) {
|
||||
service.submitPost(post);
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 3 * 60 * 1000)
|
||||
public void checkAndReSubmitPosts() {
|
||||
final List<Post> submitted = postReopsitory.findByRedditIDNotNullAndNoOfAttemptsGreaterThan(0);
|
||||
logger.info(submitted.size() + " Posts to check their score");
|
||||
for (final Post post : submitted) {
|
||||
service.checkAndReSubmit(post);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
persistence.properties
|
File diff suppressed because it is too large
Load Diff
|
@ -1,20 +0,0 @@
|
|||
<configuration>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>web - %date [%thread] %-5level %logger{36} - %message%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework" level="WARN" />
|
||||
<logger name="org.springframework.transaction" level="WARN" />
|
||||
|
||||
<!-- in order to debug some marshalling issues, this needs to be TRACE -->
|
||||
<logger name="org.springframework.web.servlet.mvc" level="WARN" />
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
|
@ -1,10 +0,0 @@
|
|||
################### DataSource Configuration ##########################
|
||||
jdbc.driverClassName=com.mysql.jdbc.Driver
|
||||
jdbc.url=jdbc:mysql://localhost:3306/oauth_reddit?createDatabaseIfNotExist=true
|
||||
jdbc.user=tutorialuser
|
||||
jdbc.pass=tutorialmy5ql
|
||||
init-db=false
|
||||
################### Hibernate Configuration ##########################
|
||||
hibernate.dialect=org.hibernate.dialect.MySQLDialect
|
||||
hibernate.show_sql=false
|
||||
hibernate.hbm2ddl.auto=update
|
|
@ -1,10 +0,0 @@
|
|||
################### DataSource Configuration ##########################
|
||||
jdbc.driverClassName=com.mysql.jdbc.Driver
|
||||
jdbc.url=jdbc:mysql://localhost:3306/oauth_reddit?createDatabaseIfNotExist=true
|
||||
jdbc.user=tutorialuser
|
||||
jdbc.pass=tutorialmy5ql
|
||||
init-db=false
|
||||
################### Hibernate Configuration ##########################
|
||||
hibernate.dialect=org.hibernate.dialect.MySQLDialect
|
||||
hibernate.show_sql=false
|
||||
hibernate.hbm2ddl.auto=create-drop
|
|
@ -1,9 +0,0 @@
|
|||
################### DataSource Configuration ##########################
|
||||
jdbc.driverClassName=org.postgresql.Driver
|
||||
jdbc.url=jdbc:postgresql://ec2-184-73-253-4.compute-1.amazonaws.com:5432/d3th4rkdb2g3p6?ssl=true&sslfactory=org.postgresql.ssl.NonValidatingFactory
|
||||
jdbc.user=fhjspqyblvawvp
|
||||
jdbc.pass=gRKGJbdHXslq0XgdI03j2Y4YoE
|
||||
################### Hibernate Configuration ##########################
|
||||
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
||||
hibernate.show_sql=false
|
||||
hibernate.hbm2ddl.auto=create-drop
|
|
@ -1,10 +0,0 @@
|
|||
################### DataSource Configuration ##########################
|
||||
jdbc.driverClassName=org.h2.Driver
|
||||
jdbc.url=jdbc:h2:mem:oauth_reddit;DB_CLOSE_DELAY=-1
|
||||
jdbc.user=sa
|
||||
jdbc.pass=
|
||||
init-db=false
|
||||
################### Hibernate Configuration ##########################
|
||||
hibernate.dialect=org.hibernate.dialect.H2Dialect
|
||||
hibernate.show_sql=false
|
||||
hibernate.hbm2ddl.auto=update
|
|
@ -1,4 +0,0 @@
|
|||
clientID=5iAHD5-BBmEL8Q
|
||||
clientSecret=grkII6-THVcFsMm1wVTiQDRONmw
|
||||
accessTokenUri=https://www.reddit.com/api/v1/access_token
|
||||
userAuthorizationUri=https://www.reddit.com/api/v1/authorize
|
|
@ -1,103 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
|
||||
<title>Schedule to Reddit</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" th:href="@{/resources/datetime-picker.css}" />
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
||||
<script th:src="@{/resources/datetime-picker.js}"></script>
|
||||
<script th:src="@{/resources/validator.js}"></script>
|
||||
<style type="text/css">
|
||||
.btn.disabled{
|
||||
background-color: #ddd;
|
||||
border-color: #ddd;
|
||||
}
|
||||
|
||||
.btn.disabled:hover{
|
||||
background-color: #ddd;
|
||||
border-color: #ddd;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div th:include="header"/>
|
||||
|
||||
<div class="container">
|
||||
<h1>Edit Scheduled Post</h1>
|
||||
<form th:action="@{/updatePost/{id}(id=${post.getId()})}" method="post" role="form" data-toggle="validator">
|
||||
<div class="row">
|
||||
<input type="hidden" name="id" value="${post.getId()}"/>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Title</label>
|
||||
<span class="col-sm-9"><input name="title" placeholder="title" class="form-control" th:value="${post.getTitle()}" required="required" data-minlength="3"/></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Url</label>
|
||||
<span class="col-sm-9"><input name="url" type="url" placeholder="url" class="form-control" th:value="${post.getUrl()}" required="required" data-minlength="3"/></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Subreddit</label>
|
||||
<span class="col-sm-9"><input name="sr" placeholder="Subreddit" class="form-control" th:value="${post.getSubreddit()}" required="required" data-minlength="3"/></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<div>
|
||||
<label class="col-sm-3">Send replies to my inbox</label>
|
||||
<span class="col-sm-9">
|
||||
<input th:if="${post.isSendReplies()=='true'}" type="checkbox" name="sendreplies" value="true" checked="checked"/>
|
||||
<input th:if="${post.isSendReplies()=='false'}" type="checkbox" name="sendreplies" value="true" checked="checked"/>
|
||||
</span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<br/>
|
||||
<hr/>
|
||||
<br/>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Resubmit If:</label>
|
||||
|
||||
<span class="col-sm-2">Votes didn't exceed </span>
|
||||
<span class="col-sm-1">
|
||||
<input type="number" class="form-control input-sm" th:value="${post.getMinScoreRequired()}" name="score" required="required"/>
|
||||
</span>
|
||||
|
||||
<span class="col-sm-3">within
|
||||
<select name="interval">
|
||||
<option value="0" th:selected="${post.getTimeInterval() == 0}">None</option>
|
||||
<option value="45" th:selected="${post.getTimeInterval() == 45}">45 minutes</option>
|
||||
<option value="60" th:selected="${post.getTimeInterval() == 60}">1 hour</option>
|
||||
<option value="120" th:selected="${post.getTimeInterval() == 120}">2 hours</option>
|
||||
</select>
|
||||
</span>
|
||||
|
||||
<span class="col-sm-3">try resubmitting
|
||||
<select name="attempt">
|
||||
<option value="0" th:selected="${post.getNoOfAttempts() == 0}">No</option>
|
||||
<option value="1" th:selected="${post.getNoOfAttempts() == 1}">1</option>
|
||||
<option value="2" th:selected="${post.getNoOfAttempts() == 2}">2</option>
|
||||
<option value="3" th:selected="${post.getNoOfAttempts() == 3}">3</option>
|
||||
<option value="4" th:selected="${post.getNoOfAttempts() == 4}">4</option>
|
||||
<option value="5" th:selected="${post.getNoOfAttempts() == 5}">5</option>
|
||||
</select>
|
||||
times.
|
||||
</span>
|
||||
|
||||
|
||||
</div>
|
||||
<br/><br/>
|
||||
<label class="col-sm-3">Submission Date (<span th:text="${#dates.format(#calendars.createToday(), 'z')}">UTC</span>)</label>
|
||||
<span class="col-sm-9"><input type="text" name="date" class="form-control" th:value="${dateValue}" readonly="readonly"/></span>
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
$('*[name=date]').appendDtpicker({"inline": true});
|
||||
});
|
||||
</script>
|
||||
|
||||
<br/><br/>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,23 +0,0 @@
|
|||
<div>
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" th:href="@{/home.html}">Schedule to Reddit</a>
|
||||
</div>
|
||||
|
||||
<p class="navbar-text navbar-right">Logged in as
|
||||
<b><span sec:authentication="principal.username">Bob</span></b>
|
||||
<a th:href="@{/logout}">Logout</a>
|
||||
</p>
|
||||
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a th:href="@{/posts}">My Scheduled Posts</a></li>
|
||||
<li><a th:href="@{/post}">Post to Reddit</a></li>
|
||||
<li><a th:href="@{/postSchedule}">Schedule Post to Reddit</a></li>
|
||||
</ul>
|
||||
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</div><!-- /.container-fluid -->
|
||||
</nav>
|
||||
</div>
|
|
@ -1,19 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
|
||||
<title>Schedule to Reddit</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div th:include="header"/>
|
||||
|
||||
<div class="container">
|
||||
<h1>Welcome, <small><span sec:authentication="principal.username">Bob</span></small></h1>
|
||||
<br/>
|
||||
<a href="posts" class="btn btn-primary">My Scheduled Posts</a>
|
||||
<a href="post" class="btn btn-primary">Post to Reddit</a>
|
||||
<a href="postSchedule" class="btn btn-primary">Schedule Post to Reddit</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,57 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
|
||||
<title>Schedule to Reddit</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div th:include="header"/>
|
||||
|
||||
<div class="container">
|
||||
<h1>My Scheduled Posts</h1>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Post title</th>
|
||||
<th>Submission Date</th>
|
||||
<th>Status</th>
|
||||
<th>Resubmit Attempts left</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr th:each="post : ${posts}">
|
||||
<td th:text="${post.getTitle()}"></td>
|
||||
<td th:text="${#calendars.format(post.getSubmissionDate(),'dd MMMM yyyy HH:mm z')}"></td>
|
||||
<td th:text="${post.getSubmissionResponse()}"></td>
|
||||
<td th:if="${post.getNoOfAttempts() > 0}" th:text="${post.getNoOfAttempts()}"></td>
|
||||
<td th:unless="${post.getNoOfAttempts() > 0}">-</td>
|
||||
<td>
|
||||
<a th:href="@{/editPost/{id}(id=${post.getId()})}" class="btn btn-warning" >Edit</a>
|
||||
<a href="#" class="btn btn-danger" th:onclick="'javascript:confirmDelete(\'' +${post.getId()}+ '\') '">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
||||
<script>
|
||||
/*<![CDATA[*/
|
||||
function confirmDelete(id) {
|
||||
if (confirm("Do you really want to delete this post?") == true) {
|
||||
deletePost(id);
|
||||
}
|
||||
}
|
||||
|
||||
function deletePost(id){
|
||||
$.ajax({
|
||||
url: 'deletePost/'+id,
|
||||
type: 'DELETE',
|
||||
success: function(result) {
|
||||
window.location.href="posts"
|
||||
}
|
||||
});
|
||||
}
|
||||
/*]]>*/
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,145 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
|
||||
<title>Schedule to Reddit</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" th:href="@{/resources/datetime-picker.css}"/>
|
||||
<link rel="stylesheet" th:href="@{/resources/autocomplete.css}"/>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
|
||||
<script th:src="@{/resources/datetime-picker.js}"></script>
|
||||
<script th:src="@{/resources/validator.js}"></script>
|
||||
<style type="text/css">
|
||||
.btn.disabled{
|
||||
background-color: #ddd;
|
||||
border-color: #ddd;
|
||||
}
|
||||
|
||||
.btn.disabled:hover{
|
||||
background-color: #ddd;
|
||||
border-color: #ddd;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div th:include="header"/>
|
||||
|
||||
<div class="container">
|
||||
<h1>Schedule Post to Reddit</h1>
|
||||
<form action="schedule" method="post" role="form" data-toggle="validator">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Title</label>
|
||||
<span class="col-sm-9"><input name="title" placeholder="title" class="form-control" required="required" data-minlength="3"/></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Url</label>
|
||||
<span class="col-sm-9"><input name="url" type="url" placeholder="url" class="form-control" required="required" data-minlength="3"/></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Subreddit</label>
|
||||
<span class="col-sm-9"><input id="sr" name="sr" placeholder="Subreddit (e.g. kitten)" class="form-control" required="required" data-minlength="3"/></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<div>
|
||||
<label class="col-sm-3">Send replies to my inbox</label> <span class="col-sm-9"><input type="checkbox" name="sendreplies" value="true"/></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<div>
|
||||
<span class="col-sm-2"><a href="#" class="btn btn-default" onclick="checkIfAlreadySubmitted()">Check if already submitted</a></span>
|
||||
<span class="col-sm-1"></span>
|
||||
<span class="col-sm-9"><span id="checkResult" class="alert alert-info" style="display:none"></span></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<hr/>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Resubmit If:</label>
|
||||
|
||||
<span class="col-sm-2">Votes didn't exceed </span>
|
||||
<span class="col-sm-1">
|
||||
<input type="number" class="form-control input-sm" value="0" name="score" required="required"/>
|
||||
</span>
|
||||
|
||||
<span class="col-sm-3">within
|
||||
<select name="interval">
|
||||
<option value="0" selected="selected">None</option>
|
||||
<option value="45">45 minutes</option>
|
||||
<option value="60">1 hour</option>
|
||||
<option value="120">2 hours</option>
|
||||
</select>
|
||||
</span>
|
||||
|
||||
<span class="col-sm-3">try resubmitting
|
||||
<select name="attempt">
|
||||
<option value="0" selected="selected">No</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
</select>
|
||||
times.
|
||||
</span>
|
||||
|
||||
|
||||
</div>
|
||||
<br/><br/>
|
||||
|
||||
|
||||
<label class="col-sm-3">Submission Date (<span th:text="${#dates.format(#calendars.createToday(), 'z')}">UTC</span>)</label>
|
||||
<span class="col-sm-9"><input type="text" name="date" class="form-control" readonly="readonly"/></span>
|
||||
<script type="text/javascript">
|
||||
/*<![CDATA[*/
|
||||
$(function(){
|
||||
$('*[name=date]').appendDtpicker({"inline": true});
|
||||
});
|
||||
/*]]>*/
|
||||
</script>
|
||||
|
||||
<br/><br/>
|
||||
|
||||
|
||||
<button type="submit" class="btn btn-primary">Schedule</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
$(function() {
|
||||
$( "#sr" ).autocomplete({
|
||||
source: "subredditAutoComplete"
|
||||
});
|
||||
|
||||
$("input[name='url'],input[name='sr']").focus(function (){
|
||||
$("#checkResult").hide();
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
/*<![CDATA[*/
|
||||
function checkIfAlreadySubmitted(){
|
||||
var url = $("input[name='url']").val();
|
||||
var sr = $("input[name='sr']").val();
|
||||
console.log(url);
|
||||
if(url.length >3 && sr.length > 3){
|
||||
$.post("checkIfAlreadySubmitted",{url: url, sr: sr}, function(data){
|
||||
var result = JSON.parse(data);
|
||||
if(result.length == 0){
|
||||
$("#checkResult").show().html("Not submitted before");
|
||||
}else{
|
||||
$("#checkResult").show().html('Already submitted <b><a target="_blank" href="http://www.reddit.com'+result[0].data.permalink+'">here</a></b>');
|
||||
}
|
||||
});
|
||||
}
|
||||
else{
|
||||
$("#checkResult").show().html("Too short url and/or subreddit");
|
||||
}
|
||||
}
|
||||
/*]]>*/
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,136 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
|
||||
<title>Schedule to Reddit</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" th:href="@{/resources/autocomplete.css}"/>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script>
|
||||
<script th:src="@{/resources/validator.js}"></script>
|
||||
<style type="text/css">
|
||||
.btn.disabled{
|
||||
background-color: #ddd;
|
||||
border-color: #ddd;
|
||||
}
|
||||
|
||||
.btn.disabled:hover{
|
||||
background-color: #ddd;
|
||||
border-color: #ddd;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div th:include="header"/>
|
||||
|
||||
<div class="container">
|
||||
<h1>Post to Reddit</h1>
|
||||
<form action="submit" method="post" role="form" data-toggle="validator">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Title</label>
|
||||
<span class="col-sm-9"><input name="title" placeholder="title" class="form-control" data-minlength="3" required="required"/></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Url</label>
|
||||
<span class="col-sm-9"><input name="url" type="url" placeholder="url" class="form-control" data-minlength="3" required="required"/></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Subreddit</label>
|
||||
<span class="col-sm-9"><input id="sr" name="sr" placeholder="Subreddit (e.g. kitten)" class="form-control" data-minlength="3" required="required"/></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<div>
|
||||
<label class="col-sm-3">Send replies to my inbox</label>
|
||||
<span class="col-sm-9"><input type="checkbox" name="sendreplies" value="true"/></span>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<div>
|
||||
<span class="col-sm-2"><a href="#" class="btn btn-default" onclick="checkIfAlreadySubmitted()">Check if already submitted</a></span>
|
||||
<span class="col-sm-1"></span>
|
||||
<span class=" col-sm-9"><span id="checkResult" class="alert alert-info" style="display:none"></span></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
|
||||
<div th:if="${iden != null}">
|
||||
<input type="hidden" name="iden" value="${iden}"/>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3">Captcha</label>
|
||||
<span class="col-sm-9"><input name="captcha" placeholder="captcha" class="form-control"/></span>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<img src="http://www.reddit.com/captcha/${iden}" alt="captcha" width="200"/>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<span class="col-sm-3"><button id="submitbtn" type="submit" class="btn btn-primary">Post</button></span>
|
||||
</div>
|
||||
</form>
|
||||
<div>
|
||||
<div th:if="${session.PREDICTION_FEATURE.isActive()}">
|
||||
<button id="checkbtn" class="btn btn-default disabled" onclick="predicateResponse()">Predicate Response</button>
|
||||
<span id="prediction"></span>
|
||||
<script type="text/javascript">
|
||||
/*<![CDATA[*/
|
||||
$("input").change(function() {
|
||||
if($("#submitbtn").hasClass("disabled")){
|
||||
if(! $("#checkbtn").hasClass("disabled")){
|
||||
$("#checkbtn").addClass("disabled");
|
||||
}
|
||||
}else{
|
||||
$("#checkbtn").removeClass("disabled");
|
||||
}
|
||||
});
|
||||
function predicateResponse(){
|
||||
var title = $('input[name="title"]').val();
|
||||
var domain = $('input[name="url"]').val();
|
||||
domain = $('<a>').prop('href', domain).prop('hostname');
|
||||
console.log(domain);
|
||||
$.post("predicatePostResponse",{title: title, domain: domain} ,function(data){
|
||||
$("#prediction").addClass("alert alert-info").html(data.replace('{','').replace('}',''));
|
||||
});
|
||||
}
|
||||
/*]]>*/
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(function() {
|
||||
$( "#sr" ).autocomplete({
|
||||
source: "subredditAutoComplete"
|
||||
});
|
||||
|
||||
$("input[name='url'],input[name='sr']").focus(function (){
|
||||
$("#checkResult").hide();
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
/*<![CDATA[*/
|
||||
function checkIfAlreadySubmitted(){
|
||||
var url = $("input[name='url']").val();
|
||||
var sr = $("input[name='sr']").val();
|
||||
console.log(url);
|
||||
if(url.length >3 && sr.length > 3){
|
||||
$.post("checkIfAlreadySubmitted",{url: url, sr: sr}, function(data){
|
||||
var result = JSON.parse(data);
|
||||
if(result.length == 0){
|
||||
$("#checkResult").show().html("Not submitted before");
|
||||
}else{
|
||||
$("#checkResult").show().html('Already submitted <b><a target="_blank" href="http://www.reddit.com'+result[0].data.permalink+'">here</a></b>');
|
||||
}
|
||||
});
|
||||
}
|
||||
else{
|
||||
$("#checkResult").show().html("Too short url and/or subreddit");
|
||||
}
|
||||
}
|
||||
/*]]>*/
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,14 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
|
||||
<title>Schedule to Reddit</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div th:include="header"/>
|
||||
<div class="container">
|
||||
<h1 th:text="${msg}">Hello</h1>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,14 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Schedule to Reddit</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Schedule to Reddit</h1>
|
||||
<a href="login" class="btn btn-primary">Login with Reddit</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,25 +0,0 @@
|
|||
.ui-autocomplete {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
cursor: default;
|
||||
padding: 0;
|
||||
margin-top: 2px;
|
||||
list-style: none;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ccc
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.ui-autocomplete > li {
|
||||
padding: 3px 20px;
|
||||
}
|
||||
.ui-autocomplete > li.ui-state-focus {
|
||||
background-color: #DDD;
|
||||
}
|
||||
.ui-helper-hidden-accessible {
|
||||
display: none;
|
||||
}
|
|
@ -1,332 +0,0 @@
|
|||
/**
|
||||
* Style-sheet for dtpicker
|
||||
* https://github.com/mugifly/jquery-simple-datetimepicker
|
||||
*/
|
||||
|
||||
.datepicker {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font: 15px/1.5 "Helvetica Neue", mplus-2c, Helvetica, Arial, "Hiragino Kaku Gothic Pro", Meiryo, sans-serif;
|
||||
font-weight: 300;
|
||||
border: 1px solid #dfdfdf;
|
||||
border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
box-shadow: 0.5px 0.5px 0px #c8c8c8;
|
||||
-webkit-box-shadow: 0.5px 0.5px 3px #eeeeee;
|
||||
-moz-box-shadow: 0.5px 0.5px 3px #eeeeee;
|
||||
}
|
||||
|
||||
/*
|
||||
* datepicker_header
|
||||
*/
|
||||
|
||||
.datepicker > .datepicker_header{
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
background-color: #eeeeee;
|
||||
color: #3f3f3f;
|
||||
text-align: center;
|
||||
font-size: 9pt;
|
||||
font-weight: bold;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_header > a {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
cursor: pointer;
|
||||
color: #3b7796;
|
||||
padding: 3px 16px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_header > a:hover {
|
||||
color: #303030;
|
||||
background-color: #c8c8c8;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_header > a:active {
|
||||
color: #ffffff;
|
||||
background-color: #808080;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_header > span {
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_header > .icon-home {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
padding: 8px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_header > .icon-close {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
padding: 8px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
.datepicker > .datepicker_header > .icon-home > div {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAYAAAB3AH1ZAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjExR/NCNwAAAepJREFUSEudk71Kw2AUhnVxVnCyN1EcCjXUujmKF1AoRRCcNU4OIg5dtXF36tAb6B107qjg0iC4STt0EdTnTU8+v5rGNh54aM7Pe/J+abLmR3AZ7QeXD0+WZkI9zViaiXq9vg+5evU0Y+ksgjCqsrgPA2jBOzQs91FNPc0o70vLQtGHAbTgHRqW+6imnmaUS1PVqWL4SgijJr8juHK1H1Qb2Uxai1kivowmjODKq6Wopp5m0losA95Noh43ONXi+XpCPOsx49W9ZaIHp+CbSlFNPc24+i8DxfGX/YeZgTDqQrsgXc9AF9oFkcY9gQN7J3MjuOhs2WUS0ngGluqZmdNLs7IBTltmZsrvsZUKGaBfhik4vTSw3EBwfrdBfwhvwcX9tpVXNkBvA4bwBk4vDSw2UD27WbdLnf42mQmjIysl8ZeBWq3m9PRubWZOL43qGQOccpf8lc9tj5tWuP7g+tHaLqSR1pY7Pde78Ap7UIEPyOip5RgIOzvkzzCGF4gxtWltF9LAIgM78AxjeAF9/xk9tfy/gBuWOLVMfPIUDq08F3kGFOQlkIlPWKiXBvJfQl6+EjdvWJqJvwwoqMlErl4aabVowmmvbWEBEs2EJeLaFhZBmone8hMtAj2JIkykZYmQifRprAqa+sk3UgBXF8VWaxMAAAAASUVORK5CYII=);
|
||||
}
|
||||
.datepicker > .datepicker_header > .icon-close> div {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAYAAAB3AH1ZAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjExR/NCNwAAAfhJREFUSEvNVEtKA0EUHHBhsvZ7D0HF4PRMnERxI+hCQ4xuBE/g5yLRA+il/CCIXkDElRGt6qkZO91j1JUW1KJf1XvVn2Gif4XGUX986aRf0zIANXq0DBBv7Iwvb+192U+NHi2H0Tg+O2ycnN2DV+CcyiVQW5B2T6/KJUxr7dAkySN4bVbaiyqXMM1s3mr0wKtyDgytgQ/gu/gKbkumvgsOpJH0lieNN3s1O9iYd/EN657kyJikZ2uFDi97JDOgX8fAWyeg2MQO6IeT8Pbrao+Wtw/qJklvywBnE5ZuOAkve9SeA0PnQT+Ia7/2BoZXnK0uBkH5Oqxl4RNZYHAH9ANdMnxP9gA42X5FoMuhp6kEAnjlDPpVeIERm0B4+m0/N9AFq26Bta5sX0JvPvDCycFPTs+PbtQTUOvIHgAhu+DoJ4BH9mFgcAzyy3cDee3+c9ATq60ErjfDcP/kDPQ3hJtIM7XlwMAJ8FoBBe2bi/4m6J1QO34yrWlc740XxDffr/wm6EWP2u0GJsEnDS/DJVPvge7T0DspmT+aGQx+cUKGPriKTcCbzEhGwOn5GAZegM/gHdiSVAK1VBo9F+yRFJn2+hgGXtrBSXpn0mbQb5orbavl4ZfskZRDm5gGp1QKQM16nPACdhNpczZur3+ezAM1eoLwv0MUfQBkquZISGUUwgAAAABJRU5ErkJggg==);
|
||||
}
|
||||
.datepicker > .datepicker_header > a:hover > div, .datepicker > .datepicker_header > a:hover > div {
|
||||
background-position: -16px 0px;
|
||||
}
|
||||
|
||||
/*
|
||||
* datepicker_inner_container
|
||||
*/
|
||||
|
||||
.datepicker > .datepicker_inner_container {
|
||||
margin: -2px 0px -2px 0px;
|
||||
background-color: #d2d2d2;
|
||||
border: 1px solid #c8c8c8;
|
||||
border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
|
||||
box-shadow: 0.5px 0px 3px #c8c8c8;
|
||||
-webkit-box-shadow: 0.5px 0px 3px #c8c8c8;
|
||||
-moz-box-shadow: 0.5px 0px 3px #c8c8c8;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/*
|
||||
* datepicker_inner_container > datepicker_calendar
|
||||
*/
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar {
|
||||
float: left;
|
||||
width: 18.3em;
|
||||
|
||||
margin-top: -0.5px;
|
||||
margin-left: -1px;
|
||||
margin-bottom: -2px;
|
||||
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #c8c8c8;
|
||||
|
||||
border-top:none;
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
-webkit-border-top-left-radius: 3px;
|
||||
-webkit-border-bottom-left-radius: 3px;
|
||||
-moz-border-radius-topleft: 3px;
|
||||
-moz-border-radius-bottomleft: 3px;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > table {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/*
|
||||
* datepicker_inner_container > datepicker_calendar > datepicker_table > tbody > tr > th (WDay-cell)
|
||||
*/
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > th {
|
||||
color: #646464;
|
||||
width: 18px;
|
||||
font-size: small;
|
||||
font-weight: normal;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
/*
|
||||
* datepicker_inner_container > datepicker_calendar > datepicker_table > tbody > tr > td (Day-cell)
|
||||
*/
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td {
|
||||
color: #000000;
|
||||
font-size: small;
|
||||
text-align:center;
|
||||
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.today {
|
||||
border-bottom: #bfbfbf solid 2px;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.wday_sat {
|
||||
color: #0044aa;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.wday_sun {
|
||||
color: #e13b00;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.day_another_month {
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.day_in_past {
|
||||
cursor: default;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.day_in_unallowed {
|
||||
cursor: default;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.out_of_range {
|
||||
cursor: default;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.active {
|
||||
color: #ffffff;
|
||||
background-color: #808080;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.hover {
|
||||
color: #000000;
|
||||
background-color: #c8c8c8;
|
||||
}
|
||||
|
||||
/*
|
||||
* datepicker_inner_container > datepicker_timelist
|
||||
*/
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist {
|
||||
float: left;
|
||||
|
||||
margin-top: -0.5px;
|
||||
padding: 5px 0px;
|
||||
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
background-color: #ffffff;
|
||||
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
-webkit-border-top-right-radius: 3px;
|
||||
-webkit-border-bottom-right-radius: 3px;
|
||||
-moz-border-radius-topright: 3px;
|
||||
-moz-border-radius-bottomright: 3px;
|
||||
text-align: right;
|
||||
width: 4.9em;
|
||||
}
|
||||
|
||||
/*
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist::after {
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
*/
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar {
|
||||
overflow: hidden;
|
||||
width: 6px;
|
||||
background: #fafafa;
|
||||
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
-webkit-border-top-right-radius: 3px;
|
||||
-webkit-border-bottom-right-radius: 3px;
|
||||
-moz-border-radius-topright: 3px;
|
||||
-moz-border-radius-bottomright: 3px;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar:horizontal {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar-piece {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar-piece:start {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar-thumb {
|
||||
background: #aaaaaa;
|
||||
border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar-corner {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist > div.timelist_item {
|
||||
padding-top: 5px;
|
||||
padding-bottom:5px;
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 2px;
|
||||
font-size: small;
|
||||
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist > div.timelist_item.time_in_past {
|
||||
cursor: default;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist > div.timelist_item.out_of_range {
|
||||
cursor: default;
|
||||
color: #cccccc;
|
||||
}
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist > div.timelist_item.active {
|
||||
color: #ffffff;
|
||||
background-color: #808080;
|
||||
}
|
||||
|
||||
.datepicker > .datepicker_inner_container > .datepicker_timelist > div.timelist_item.hover {
|
||||
color: #000000;
|
||||
background-color: #c8c8c8;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,304 +0,0 @@
|
|||
/* ========================================================================
|
||||
* Bootstrap (plugin): validator.js v0.7.2
|
||||
* ========================================================================
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013 Cina Saffary.
|
||||
* Made by @1000hz in the style of Bootstrap 3 era @fat
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
|
||||
// VALIDATOR CLASS DEFINITION
|
||||
// ==========================
|
||||
|
||||
var Validator = function (element, options) {
|
||||
this.$element = $(element)
|
||||
this.options = options
|
||||
|
||||
this.$element.attr('novalidate', true) // disable automatic native validation
|
||||
this.toggleSubmit()
|
||||
|
||||
this.$element.on('input.bs.validator change.bs.validator focusout.bs.validator', $.proxy(this.validateInput, this))
|
||||
this.$element.on('submit.bs.validator', $.proxy(this.onSubmit, this))
|
||||
|
||||
this.$element.find('[data-match]').each(function () {
|
||||
var $this = $(this)
|
||||
var target = $this.data('match')
|
||||
|
||||
$(target).on('input.bs.validator', function (e) {
|
||||
$this.val() && $this.trigger('input.bs.validator')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Validator.DEFAULTS = {
|
||||
delay: 500,
|
||||
html: false,
|
||||
disable: true,
|
||||
errors: {
|
||||
match: 'Does not match',
|
||||
minlength: 'Not long enough'
|
||||
}
|
||||
}
|
||||
|
||||
Validator.VALIDATORS = {
|
||||
native: function ($el) {
|
||||
var el = $el[0]
|
||||
return el.checkValidity ? el.checkValidity() : true
|
||||
},
|
||||
match: function ($el) {
|
||||
var target = $el.data('match')
|
||||
return !$el.val() || $el.val() === $(target).val()
|
||||
},
|
||||
minlength: function ($el) {
|
||||
var minlength = $el.data('minlength')
|
||||
return !$el.val() || $el.val().length >= minlength
|
||||
}
|
||||
}
|
||||
|
||||
Validator.prototype.validateInput = function (e) {
|
||||
var $el = $(e.target)
|
||||
var prevErrors = $el.data('bs.validator.errors')
|
||||
var errors
|
||||
|
||||
if ($el.is('[type="radio"]')) $el = this.$element.find('input[name="' + $el.attr('name') + '"]')
|
||||
|
||||
this.$element.trigger(e = $.Event('validate.bs.validator', {relatedTarget: $el[0]}))
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
var self = this
|
||||
|
||||
this.runValidators($el).done(function (errors) {
|
||||
$el.data('bs.validator.errors', errors)
|
||||
|
||||
errors.length ? self.showErrors($el) : self.clearErrors($el)
|
||||
|
||||
if (!prevErrors || errors.toString() !== prevErrors.toString()) {
|
||||
e = errors.length
|
||||
? $.Event('invalid.bs.validator', {relatedTarget: $el[0], detail: errors})
|
||||
: $.Event('valid.bs.validator', {relatedTarget: $el[0], detail: prevErrors})
|
||||
|
||||
self.$element.trigger(e)
|
||||
}
|
||||
|
||||
self.toggleSubmit()
|
||||
|
||||
self.$element.trigger($.Event('validated.bs.validator', {relatedTarget: $el[0]}))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Validator.prototype.runValidators = function ($el) {
|
||||
var errors = []
|
||||
var validators = [Validator.VALIDATORS.native]
|
||||
var deferred = $.Deferred()
|
||||
var options = this.options
|
||||
|
||||
$el.data('bs.validator.deferred') && $el.data('bs.validator.deferred').reject()
|
||||
$el.data('bs.validator.deferred', deferred)
|
||||
|
||||
function getErrorMessage(key) {
|
||||
return $el.data(key + '-error')
|
||||
|| $el.data('error')
|
||||
|| key == 'native' && $el[0].validationMessage
|
||||
|| options.errors[key]
|
||||
}
|
||||
|
||||
$.each(Validator.VALIDATORS, $.proxy(function (key, validator) {
|
||||
if (($el.data(key) || key == 'native') && !validator.call(this, $el)) {
|
||||
var error = getErrorMessage(key)
|
||||
!~errors.indexOf(error) && errors.push(error)
|
||||
}
|
||||
}, this))
|
||||
|
||||
if (!errors.length && $el.val() && $el.data('remote')) {
|
||||
this.defer($el, function () {
|
||||
var data = {}
|
||||
data[$el.attr('name')] = $el.val()
|
||||
$.get($el.data('remote'), data)
|
||||
.fail(function (jqXHR, textStatus, error) { errors.push(getErrorMessage('remote') || error) })
|
||||
.always(function () { deferred.resolve(errors)})
|
||||
})
|
||||
} else deferred.resolve(errors)
|
||||
|
||||
return deferred.promise()
|
||||
}
|
||||
|
||||
Validator.prototype.validate = function () {
|
||||
var delay = this.options.delay
|
||||
|
||||
this.options.delay = 0
|
||||
this.$element.find(':input').trigger('input.bs.validator')
|
||||
this.options.delay = delay
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
Validator.prototype.showErrors = function ($el) {
|
||||
var method = this.options.html ? 'html' : 'text'
|
||||
|
||||
this.defer($el, function () {
|
||||
var $group = $el.closest('.form-group')
|
||||
var $block = $group.find('.help-block.with-errors')
|
||||
var $feedback = $group.find('.form-control-feedback')
|
||||
var errors = $el.data('bs.validator.errors')
|
||||
|
||||
if (!errors.length) return
|
||||
|
||||
errors = $('<ul/>')
|
||||
.addClass('list-unstyled')
|
||||
.append($.map(errors, function (error) { return $('<li/>')[method](error) }))
|
||||
|
||||
$block.data('bs.validator.originalContent') === undefined && $block.data('bs.validator.originalContent', $block.html())
|
||||
$block.empty().append(errors)
|
||||
$group.removeClass('has-success')
|
||||
$group.addClass('has-error')
|
||||
|
||||
$feedback.removeClass('glyphicon-ok')
|
||||
$feedback.addClass('glyphicon-warning-sign')
|
||||
})
|
||||
}
|
||||
|
||||
Validator.prototype.clearErrors = function ($el) {
|
||||
var $group = $el.closest('.form-group')
|
||||
var $block = $group.find('.help-block.with-errors')
|
||||
var $feedback = $group.find('.form-control-feedback')
|
||||
|
||||
$block.html($block.data('bs.validator.originalContent'))
|
||||
$group.removeClass('has-error')
|
||||
$group.addClass('has-success')
|
||||
$feedback.removeClass('glyphicon-warning-sign')
|
||||
$feedback.addClass('glyphicon-ok')
|
||||
}
|
||||
|
||||
Validator.prototype.hasErrors = function () {
|
||||
function fieldErrors() {
|
||||
return !!($(this).data('bs.validator.errors') || []).length
|
||||
}
|
||||
|
||||
return !!this.$element.find(':input:enabled').filter(fieldErrors).length
|
||||
}
|
||||
|
||||
Validator.prototype.isIncomplete = function () {
|
||||
function fieldIncomplete() {
|
||||
return this.type === 'checkbox' ? !this.checked :
|
||||
this.type === 'radio' ? !$('[name="' + this.name + '"]:checked').length :
|
||||
$.trim(this.value) === ''
|
||||
}
|
||||
|
||||
return !!this.$element.find(':input[required]:enabled').filter(fieldIncomplete).length
|
||||
}
|
||||
|
||||
Validator.prototype.onSubmit = function (e) {
|
||||
this.validate()
|
||||
if (this.isIncomplete() || this.hasErrors()) e.preventDefault()
|
||||
}
|
||||
|
||||
Validator.prototype.toggleSubmit = function () {
|
||||
if(!this.options.disable) return
|
||||
var $btn = this.$element.find('input[type="submit"], button[type="submit"]')
|
||||
$btn.toggleClass('disabled', this.isIncomplete() || this.hasErrors())
|
||||
.css({'pointer-events': 'all', 'cursor': 'pointer'})
|
||||
}
|
||||
|
||||
Validator.prototype.defer = function ($el, callback) {
|
||||
if (!this.options.delay) return callback()
|
||||
window.clearTimeout($el.data('bs.validator.timeout'))
|
||||
$el.data('bs.validator.timeout', window.setTimeout(callback, this.options.delay))
|
||||
}
|
||||
|
||||
Validator.prototype.destroy = function () {
|
||||
this.$element
|
||||
.removeAttr('novalidate')
|
||||
.removeData('bs.validator')
|
||||
.off('.bs.validator')
|
||||
|
||||
this.$element.find(':input')
|
||||
.off('.bs.validator')
|
||||
.removeData(['bs.validator.errors', 'bs.validator.deferred'])
|
||||
.each(function () {
|
||||
var $this = $(this)
|
||||
var timeout = $this.data('bs.validator.timeout')
|
||||
window.clearTimeout(timeout) && $this.removeData('bs.validator.timeout')
|
||||
})
|
||||
|
||||
this.$element.find('.help-block.with-errors').each(function () {
|
||||
var $this = $(this)
|
||||
var originalContent = $this.data('bs.validator.originalContent')
|
||||
|
||||
$this
|
||||
.removeData('bs.validator.originalContent')
|
||||
.html(originalContent)
|
||||
})
|
||||
|
||||
this.$element.find('input[type="submit"], button[type="submit"]').removeClass('disabled')
|
||||
|
||||
this.$element.find('.has-error').removeClass('has-error')
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
// VALIDATOR PLUGIN DEFINITION
|
||||
// ===========================
|
||||
|
||||
|
||||
function Plugin(option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var options = $.extend({}, Validator.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
var data = $this.data('bs.validator')
|
||||
|
||||
if (!data && option == 'destroy') return
|
||||
if (!data) $this.data('bs.validator', (data = new Validator(this, options)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
var old = $.fn.validator
|
||||
|
||||
$.fn.validator = Plugin
|
||||
$.fn.validator.Constructor = Validator
|
||||
|
||||
|
||||
// VALIDATOR NO CONFLICT
|
||||
// =====================
|
||||
|
||||
$.fn.validator.noConflict = function () {
|
||||
$.fn.validator = old
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
// VALIDATOR DATA-API
|
||||
// ==================
|
||||
|
||||
$(window).on('load', function () {
|
||||
$('form[data-toggle="validator"]').each(function () {
|
||||
var $form = $(this)
|
||||
Plugin.call($form, $form.data())
|
||||
})
|
||||
})
|
||||
|
||||
}(jQuery);
|
|
@ -1,44 +0,0 @@
|
|||
package org.baeldung.classifier;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.baeldung.reddit.classifier.RedditClassifier;
|
||||
import org.baeldung.reddit.classifier.RedditDataCollector;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
@Ignore
|
||||
public class RedditClassifierTest {
|
||||
|
||||
@Test
|
||||
public void whenUsingDefaultClassifier_thenAccurate() throws IOException {
|
||||
final RedditClassifier classifier = new RedditClassifier();
|
||||
classifier.trainClassifier(RedditDataCollector.DATA_FILE);
|
||||
final double result = classifier.getAccuracy();
|
||||
System.out.println("==== Default Classifier Accuracy = " + result);
|
||||
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++\n\n\n");
|
||||
assertTrue(result > 0.7);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSmallerPoolSizeAndFeatures_whenUsingCustomClassifier_thenAccurate() throws IOException {
|
||||
final RedditClassifier classifier = new RedditClassifier(100, 500, 7);
|
||||
classifier.trainClassifier(RedditDataCollector.DATA_FILE);
|
||||
final double result = classifier.getAccuracy();
|
||||
System.out.println("==== Custom Classifier (small) Accuracy = " + result);
|
||||
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++\n\n\n");
|
||||
assertTrue(result < 0.7);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLargerPoolSizeAndFeatures_whenUsingCustomClassifier_thenAccurate() throws IOException {
|
||||
final RedditClassifier classifier = new RedditClassifier(250, 2500, 7);
|
||||
classifier.trainClassifier(RedditDataCollector.DATA_FILE);
|
||||
final double result = classifier.getAccuracy();
|
||||
System.out.println("==== Custom Classifier (large) Accuracy = " + result);
|
||||
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++\n\n\n");
|
||||
assertTrue(result > 0.7);
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
package org.baeldung.persistence;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.collection.IsIn.isIn;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
|
||||
import org.baeldung.persistence.dao.PostRepository;
|
||||
import org.baeldung.persistence.dao.UserRepository;
|
||||
import org.baeldung.persistence.model.Post;
|
||||
import org.baeldung.persistence.model.User;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.transaction.TransactionConfiguration;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = { TestJPAConfig.class })
|
||||
@Transactional
|
||||
@TransactionConfiguration
|
||||
// @Ignore
|
||||
public class PersistenceJPATest {
|
||||
@Autowired
|
||||
private PostRepository postRepository;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
private Post alreadySentPost, notSentYetOld, notSentYet;
|
||||
|
||||
private User userJohn, userTom;
|
||||
|
||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
|
||||
@Before
|
||||
public void init() throws ParseException {
|
||||
userJohn = new User();
|
||||
userJohn.setUsername("John");
|
||||
userRepository.save(userJohn);
|
||||
|
||||
userTom = new User();
|
||||
userTom.setUsername("Tom");
|
||||
userRepository.save(userTom);
|
||||
|
||||
alreadySentPost = new Post();
|
||||
alreadySentPost.setTitle("First post title");
|
||||
alreadySentPost.setSent(true);
|
||||
alreadySentPost.setSubmissionDate(dateFormat.parse("2015-03-03 10:30"));
|
||||
alreadySentPost.setUser(userJohn);
|
||||
alreadySentPost.setSubreddit("funny");
|
||||
alreadySentPost.setUrl("www.example.com");
|
||||
postRepository.save(alreadySentPost);
|
||||
|
||||
notSentYetOld = new Post();
|
||||
notSentYetOld.setTitle("Second post title");
|
||||
notSentYetOld.setSent(false);
|
||||
notSentYetOld.setSubmissionDate(dateFormat.parse("2015-03-03 11:00"));
|
||||
notSentYetOld.setUser(userTom);
|
||||
notSentYetOld.setSubreddit("funny");
|
||||
notSentYetOld.setUrl("www.example.com");
|
||||
postRepository.save(notSentYetOld);
|
||||
|
||||
notSentYet = new Post();
|
||||
notSentYet.setTitle("Second post title");
|
||||
notSentYet.setSent(false);
|
||||
notSentYet.setSubmissionDate(dateFormat.parse("2015-03-03 11:30"));
|
||||
notSentYet.setUser(userJohn);
|
||||
notSentYet.setSubreddit("funny");
|
||||
notSentYet.setUrl("www.example.com");
|
||||
postRepository.save(notSentYet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGettingListOfSentPosts_thenCorrect() throws ParseException {
|
||||
final List<Post> results = postRepository.findBySubmissionDateBeforeAndIsSent(dateFormat.parse("2015-03-03 11:50"), true);
|
||||
|
||||
assertThat(alreadySentPost, isIn(results));
|
||||
assertThat(notSentYet, not(isIn(results)));
|
||||
assertThat(notSentYetOld, not(isIn(results)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGettingListOfOldPosts_thenCorrect() throws ParseException {
|
||||
final List<Post> results = postRepository.findBySubmissionDateBeforeAndIsSent(dateFormat.parse("2015-03-03 11:01"), false);
|
||||
|
||||
assertThat(notSentYetOld, isIn(results));
|
||||
assertThat(notSentYet, not(isIn(results)));
|
||||
assertThat(alreadySentPost, not(isIn(results)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGettingListOfSpecificuser_thenCorrect() throws ParseException {
|
||||
final List<Post> results = postRepository.findByUser(userTom);
|
||||
assertThat(notSentYetOld, isIn(results));
|
||||
assertThat(notSentYet, not(isIn(results)));
|
||||
assertThat(alreadySentPost, not(isIn(results)));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package org.baeldung.persistence;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
@PropertySource({ "classpath:persistence-test.properties" })
|
||||
@ComponentScan({ "org.baeldung.persistence.model", "org.baeldung.persistence.dao" })
|
||||
@EnableJpaRepositories(basePackages = "org.baeldung.persistence.dao")
|
||||
public class TestJPAConfig {
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
public TestJPAConfig() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
|
||||
em.setDataSource(dataSource());
|
||||
em.setPackagesToScan(new String[] { "org.baeldung.persistence.model" });
|
||||
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
|
||||
em.setJpaVendorAdapter(vendorAdapter);
|
||||
em.setJpaProperties(additionalProperties());
|
||||
return em;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
|
||||
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
|
||||
dataSource.setUrl(env.getProperty("jdbc.url"));
|
||||
dataSource.setUsername(env.getProperty("jdbc.user"));
|
||||
dataSource.setPassword(env.getProperty("jdbc.pass"));
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JpaTransactionManager transactionManager() {
|
||||
final JpaTransactionManager transactionManager = new JpaTransactionManager();
|
||||
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
|
||||
return transactionManager;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
|
||||
return new PersistenceExceptionTranslationPostProcessor();
|
||||
}
|
||||
|
||||
final Properties additionalProperties() {
|
||||
final Properties hibernateProperties = new Properties();
|
||||
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto", "create-drop"));
|
||||
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
|
||||
return hibernateProperties;
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
Loading…
Reference in New Issue