Merge pull request #210 from Doha2012/master

move oauth module to reddit app
This commit is contained in:
Eugen
2015-05-13 16:21:43 +01:00
49 changed files with 0 additions and 17568 deletions
-29
View File
@@ -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>
-48
View File
@@ -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>
-15
View File
@@ -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
View File
@@ -1 +0,0 @@
web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT target/*.war
File diff suppressed because it is too large Load Diff
@@ -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

Some files were not shown because too many files have changed in this diff Show More