BBS > spring

spring long-polling chat

작성자 : 이창우 (x1wins) | 등록일 : 2013-06-25 | 목록
첨부 파일이 없습니다.
첨부 이미지가 없습니다.
package org.springframework.samples.async.chat;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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.context.request.async.DeferredResult;

@Controller
@RequestMapping("/mvc/chat")
public class ChatController {

	private final ChatRepository chatRepository;

	private final Map<DeferredResult<List<String>>, Integer> chatRequests =
			new ConcurrentHashMap<DeferredResult<List<String>>, Integer>();

	private static final Logger logger = LoggerFactory.getLogger(ChatController.class);

	@Autowired
	public ChatController(ChatRepository chatRepository) {
		this.chatRepository = chatRepository;
	}
	
	/*
	 * http://localhost:8080/spring-mvc-chat/mvc/chat?userId=0
	 * http://localhost:8080/spring-mvc-chat/mvc/chat?userId=1
	 * http://localhost:8080/spring-mvc-chat/mvc/chat?userId=2
	 * http://localhost:8080/spring-mvc-chat/mvc/chat?userId=3
	 */
	@RequestMapping(method=RequestMethod.GET)
	@ResponseBody
	public DeferredResult<List<String>> getMessages(@RequestParam int userId) {

		final DeferredResult<List<String>> deferredResult = new DeferredResult<List<String>>(null, Collections.emptyList());
		this.chatRequests.put(deferredResult, userId);
		
		logger.info("userId : " + userId);

		deferredResult.onCompletion(new Runnable() {
			@Override
			public void run() {
				chatRequests.remove(deferredResult);
				logger.info("deferredResult.onCompletion(new Runnable())");
			}
		});
		
		deferredResult.onTimeout(new Runnable() {
			@Override
			public void run() {
				logger.info("deferredResult.onTimeout(new Runnable())");
			}
		});
		
		return deferredResult;
	}

	/*
	 * http://localhost:8080/spring-mvc-chat/mvc/chat?message=tsssssssssssssssssest2&userId=1&userId=2
	 */
	@RequestMapping(method=RequestMethod.POST)
	@ResponseBody
	public void postMessage(@RequestParam String message, @RequestParam(value="userId", required=true) List<Integer> userIds) {

		this.chatRepository.addMessage(message);
		
		// Update all chat requests as part of the POST request
		// See Redis branch for a more sophisticated, non-blocking approach

		for (Entry<DeferredResult<List<String>>, Integer> entry : this.chatRequests.entrySet()) {
			
			List<String> messages = this.chatRepository.getLastMessages();
			logger.info("entry.getValue() : "+entry.getValue() + "entry.getKey() : "+entry.getKey());
			
			for(int userId : userIds) {
				if(userId == entry.getValue()) {				
					entry.getKey().setResult(messages);
					logger.info("success - entry.getValue() : "+entry.getValue() + "entry.getKey() : "+entry.getKey() + " messages : " + messages);
				}
			}
			
		}		
	}

}
/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.samples.async.chat;

import java.util.List;

public interface ChatRepository {

	public List<String> getLastMessages();
	
	List<String> getMessages(int messageIndex);

	void addMessage(String message);

}
/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.samples.async.chat;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

@Repository
public class InMemoryChatRepository implements ChatRepository {

	private final List<String> messages = new CopyOnWriteArrayList<String>();
	
	private static final Logger logger = LoggerFactory.getLogger(ChatController.class);

	public int getListSize() {
		return this.messages.size();
	}
	
	public List<String> getLastMessages() {
		if (this.messages.isEmpty()) {
			logger.info("this.messages.isEmpty()");
			return Collections.<String> emptyList();
		}
		logger.info("this.messages.subList(index, this.messages.size()");
		int lastNum = getListSize()-1;
		
		if(lastNum<0) lastNum = 0;
		
		return this.messages.subList(lastNum, this.messages.size());
	}
	
	public List<String> getMessages(int index) {
		if (this.messages.isEmpty()) {
			logger.info("this.messages.isEmpty()");
			return Collections.<String> emptyList();
		}
		Assert.isTrue((index >= 0) && (index <= this.messages.size()), "Invalid message index");
		logger.info("this.messages.subList(index, this.messages.size()");
		return this.messages.subList(index, this.messages.size());
	}

	public void addMessage(String message) {
		this.messages.add(message);
	}

}
<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.springframework.samples</groupId>
	<artifactId>spring-mvc-chat</artifactId>
	<packaging>war</packaging>
	<version>1.0.0-SNAPSHOT</version>
	<name>Chat sample using the Spring MVC 3.2 Servlet-based async support</name>

	<properties>
		<org.springframework-version>3.2.1.RELEASE</org.springframework-version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</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>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>1.0.0.RELEASE</version>
			<exclusions>
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-core</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-context</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-context-support</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-tx</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</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.apache.geronimo.specs</groupId>
			<artifactId>geronimo-servlet_3.0_spec</artifactId>
			<version>1.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
		    <groupId>org.thymeleaf</groupId>
		    <artifactId>thymeleaf-spring3</artifactId>
		    <version>2.0.5</version>
			<exclusions>
				<exclusion>
					<groupId>org.slf4j</groupId>
					<artifactId>slf4j-api</artifactId>
				</exclusion>
		    </exclusions>
		</dependency>
		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-mapper-asl</artifactId>
			<version>1.8.1</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.2.2</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.0.1</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>1.6.4</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.10</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.easymock</groupId>
			<artifactId>easymock</artifactId>
			<version>3.1</version>
			<scope>test</scope>
		</dependency>
  		<dependency>
			<groupId>org.hamcrest</groupId>
			<artifactId>hamcrest-library</artifactId>
			<version>1.3</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	
	<repositories>
		<repository>
			<id>SpringSource repository</id>
			<url>http://repo.springsource.org/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
			<releases>
				<enabled>true</enabled>
			</releases>
		</repository>
		<repository>
			<id>SpringSource snapshot repository</id>
			<url>http://repo.springsource.org/snapshot</url>
			<snapshots><enabled>true</enabled></snapshots>
			<releases><enabled>false</enabled></releases>
		</repository>
	</repositories>
	
	<pluginRepositories>
		<pluginRepository>
			<id>apache.snapshots</id>
			<url>http://repository.apache.org/content/groups/snapshots-group/</url>
		</pluginRepository>
	</pluginRepositories>	

	<build>
		<finalName>${project.artifactId}</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.2</version>
				<configuration>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-resources-plugin</artifactId>
				<version>2.5</version>
				<configuration>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.tomcat.maven</groupId>
				<artifactId>tomcat7-maven-plugin</artifactId>
				<version>2.0-SNAPSHOT</version>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-eclipse-plugin</artifactId>
				<version>2.8</version>
				<configuration>
				    <downloadSources>true</downloadSources>
				    <downloadJavadocs>false</downloadJavadocs>
				    <wtpversion>2.0</wtpversion>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>
http://blog.springsource.org/2012/05/16/spring-mvc-3-2-preview-chat-sample/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+SpringSourceTeamBlog+%28SpringSource_Team_Blog%29

의 내용을 변경하여

특정 userId에게만 메세지를 보내도록 수정하였다

http://localhost:8080/spring-mvc-chat/mvc/chat?userId=0
http://localhost:8080/spring-mvc-chat/mvc/chat?userId=1
http://localhost:8080/spring-mvc-chat/mvc/chat?userId=2
http://localhost:8080/spring-mvc-chat/mvc/chat?userId=3

위와 같이 0~3까지의 userId가 get 요청하여 대기중일때


http://localhost:8080/spring-mvc-chat/mvc/chat?message=tsssssssssssssssssest2&userId=1&userId=2

위의 post 요청으로 userId가 1,2 request에게 tsssssssssssssssssest2라는 메세지를 전송하게 된다
get요청중인 userId가 1,2인 요청은 response를 받고 통신을 종료한다.
하지만 userId가 1,2가 아닌 요청은 timeout 될때까지 response를 대기한다.
로그인 하셔야 댓글을 사용하실수 있습니다.
댓글쓰기를 누르면 로그인 페이지로 이동 후 돌아옵니다.
총 댓글 겟수 0
번호 제목 등록일 작성자 조회수
634 하이버네이트 no session (0) 2013-08-04 이창우 1456
631 hibernate join example (0) 2013-07-05 이창우 1493
630 hibernate 추론성 제거 (0) 2013-07-03 이창우 1372
629 http 406 에러 (0) 2013-07-02 이창우 4186
628 spring 3.2 에러 (0) 2013-07-02 이창우 1213
-> spring long-polling chat (0) 2013-06-25 이창우 8464
621 hibernate order (0) 2013-05-03 이창우 1432
593 자주 쓰게 될 pom? (0) 2012-11-29 이창우 1365
592 spring data (0) 2012-11-29 이창우 1358
566 hibernate 1:n duplicates (0) 2012-08-22 이창우 1678
565 hibernate no session (1) 2012-08-08 이창우 1702
562 maven tool.jar (0) 2012-08-02 이창우 1861
561 hibernate 정보 모음 (0) 2012-08-01 이창우 2468
556 메이븐 프로젝트 웹프로젝트로 시작하기 (0) 2012-07-17 이창우 1655
555 @ResponseBody sample (0) 2012-07-15 이창우 2648
549 comet with spring (0) 2012-07-05 이창우 1804
529 hinbernate Criteria join (4) 2012-05-07 이창우 2335
525 properties 이클립스 한글 (0) 2012-04-29 이창우 2052
524 jsonview xml (1) 2012-04-29 이창우 2880
523 error (0) 2012-04-25 이창우 1731
< 1 2 3 >
글쓰기 검색