2016-11-01 9 views
0

私は、Spring-MVC(4.2.5)とSpring-Security(4.0.3)を使ってWebアプリケーションを開発しています。私の問題は、30秒ごとなどの設定された時間間隔で実行されるメソッドが必要ですが、実行前にユーザーがログインしていることを確認する必要があります。Spring 4コンテナのスケジュールされたメソッド

ユーザーがログインフォームを送信すると、HTTP要求は外部サーバーに送信され、資格情報が正しい場合は、セッショントークンを含むHTTP応答が返されます。

セッショントークンは、タイムドメソッド内で転送したいものを含め、他のすべてのリクエストにとって基本的なものです。私のコントローラで

私はすべての瞬間には、春のセキュリティコンテキストからのセッショントークンを取得することができます。

String token = SecurityContextHolder.getContext().getAuthentication().getDetails().toString(); 

は私が動作しないようスケジュールされたタスクを使用しようとしましたが、残念ながら、これはで開始されているため、セッションがなく、サーブレットコンテナから分離されたスレッドで実行されている場合、アプリケーションの起動。 SecurityContextHolder.getContext()がnullであるため、次の例外がスローされます。

GRAVE: Unexpected error occurred in scheduled task. 
java.lang.NullPointerException 
    at service.RetrieveBettingOdds.retrieveMatchByNationality(RetrieveBettingOdds.java:54) 
    at tasks.OddsTask.run(OddsTask.java:36) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:497) 
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) 
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) 
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) 
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) 
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) 
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at java.lang.Thread.run(Thread.java:745) 

OddsTask

@Component 
public class OddsTask { 
    @Autowired 
    private BettingService bettingService; 
    @Autowired 
    private MatchDao matchDao; 
    private List<Match> betfairMatches; 
    private List<Match> databaseMatches; 

    public void run() { 
     betfairMatches = new ArrayList<>(); 
     databaseMatches = new ArrayList<>(); 
     databaseMatches = matchDao.retrieveAllMatches(); 

     for(Country country : Country.values()) { 
      betfairMatches = bettingService.retrieveMatchByNationality(country.name(), getCompetitionId(country.name())); 
     } 
    //cut code 
} 

RetrieveBettingOdds

@Service 
public class RetrieveBettingOdds implements BettingService { 
    private static final String BETTING_END_POINT = "https://api.betfair.com/exchange/betting/json-rpc/v1"; 
    private static final String APP_KEY = "my_app_key"; 
    private String token; 

    @Override 
    public List<Match> retrieveMatchByNationality(String country, String competitionId) { 
     token = SecurityContextHolder.getContext().getAuthentication().getDetails().toString(); 
    /** 
    * Cut the following code, in which I create an HTTP request including the token 
    **/ 
} 

春-mvc.xml

<bean id="oddsTask" class="tasks.OddsTask"> 
</bean> 

<task:scheduler id="myScheduler" pool-size="10" /> 

<task:scheduled-tasks scheduler="myScheduler"> 
    <task:scheduled ref="oddsTask" method="run" 
     fixed-delay="10000" initial-delay="60000" /> 
</task:scheduled-tasks> 

何ができます代わりに使う?どうすれば修正できますか?

+0

スコープセッションでBeanを追加すると、そのBeanはスケジュールされたタスクを実行しますか? – Schrieveslaach

+0

[HttpSessionListener](https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpSessionListener.html)を定義する方法と、セッションが作成されるたびに、グローバルデータ構造(静的リストやスプリングシングルトンBeanなど)を使用すると、セッションが終了するたびにそのセッションを削除できます。スケジュールされたタスクが実行されると、その構造内のすべてのタスクに対して実行されます。 –

+0

あなたは「あなたがログインしていることを確認してください」と言っています。一度に1人のユーザーだけがログインすることを期待していますか?もしそうでなければ、エドウィンは言っているように、ログインしているすべてのユーザーのために実行されるバックグラウンドジョブを書く方が良いです。誰が自分でログインしているかを管理するか、dbテーブルをスキャンするか、SpringのSessionRegistryを使用することができます(http://stackoverflow.com/questions/11271449/how-can-i-have-list-of-all-users-logged -in-via-spring-security-my-web-applicat) –

答えて

0

残念ながら私はこの時点でテストすることはできませんが、何らかの方法でこれが役立つか、少なくともこれがどのようにできるかについての他のアイデアを提供したいと考えています。

アクティブなセッション?でデータ構造を更新するHttpイベントリスナーを定義するのはどうですか?その後で

public class ActiveUsers implements HttpSessionListener { 

    private ActiveUsersDao active = new ActiveUsersDao<>(); 

    @Override 
    public void sessionCreated(HttpSessionEvent event) { 
     active.add(event.getSession().getId()); 

    } 

    @Override 
    public void sessionDestroyed(HttpSessionEvent event) { 
     active.remove(event.getSession().getId()); 
    }   
} 

web.xmlあなたがあなたのDAOからデータを取得することができ、あなたの予定の豆で、

<listener> 
    <listener-class>com.stackoverflow.ActiveUsers</listener-class> 
</listener> 

最後にリスナーを登録します。

@Service 
public class TaskScheduler 

    @Auotwired ActiveUsersDao activeUsers; 

    @Scheduled 
    public void run() { 
     Set<String> snapshot = activeUsers.findAll(); 
     for(String user: snapshot) { 
     //do what you've got to do 
     } 
    } 
+0

まずは答えに感謝します。それを整理するのは良い方法だと思っていましたが、まだ問題があります:1)ActiveUsersDaoはリストを表すオブジェクトですか?またはDaoクラス+インターフェイス? 2)event.getSessionId()とは何ですか? 3)sessionCreatedとsessionDestroyedは、Spring Securityによって作成/破棄されたセッションをインターセプトします。しかし、ログインしたイベントが発生した後にスケジュールされたメソッドを強制的に実行させるにはどうすればよいですか? – andy

+0

1人のユーザーしかWebアプリを使用できないように指定するのを忘れましたが、今後この制限はありません – andy

関連する問題