diff --git a/pom.xml b/pom.xml
index f1ca82f29..aecd99186 100644
--- a/pom.xml
+++ b/pom.xml
@@ -207,6 +207,11 @@
spring-webmvc
${spring.version}
+
+ org.springframework
+ spring-websocket
+ ${spring.version}
+
org.springframework
spring-jdbc
diff --git a/rpc/src/main/java/com/navercorp/pinpoint/rpc/util/TimerFactory.java b/rpc/src/main/java/com/navercorp/pinpoint/rpc/util/TimerFactory.java
index 036197981..8af04a03c 100644
--- a/rpc/src/main/java/com/navercorp/pinpoint/rpc/util/TimerFactory.java
+++ b/rpc/src/main/java/com/navercorp/pinpoint/rpc/util/TimerFactory.java
@@ -30,6 +30,11 @@ public class TimerFactory {
public static HashedWheelTimer createHashedWheelTimer(String threadName, long tickDuration, TimeUnit unit, int ticksPerWheel) {
final PinpointThreadFactory threadFactory = new PinpointThreadFactory(threadName, true);
+ return createHashedWheelTimer(threadFactory, tickDuration, unit, ticksPerWheel);
+ }
+
+ public static HashedWheelTimer createHashedWheelTimer(PinpointThreadFactory threadFactory, long tickDuration, TimeUnit unit, int ticksPerWheel) {
return new HashedWheelTimer(threadFactory, ThreadNameDeterminer.CURRENT, tickDuration, unit, ticksPerWheel);
}
+
}
diff --git a/web/pom.xml b/web/pom.xml
index 2cfaa7f96..c10fabff2 100644
--- a/web/pom.xml
+++ b/web/pom.xml
@@ -121,6 +121,10 @@
org.springframework
spring-webmvc
+
+ org.springframework
+ spring-websocket
+
org.springframework
spring-jdbc
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/config/WebSocketConfig.java b/web/src/main/java/com/navercorp/pinpoint/web/config/WebSocketConfig.java
new file mode 100644
index 000000000..e77ad980b
--- /dev/null
+++ b/web/src/main/java/com/navercorp/pinpoint/web/config/WebSocketConfig.java
@@ -0,0 +1,55 @@
+/*
+ *
+ * * Copyright 2014 NAVER Corp.
+ * *
+ * * 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 com.navercorp.pinpoint.web.config;
+
+
+import com.navercorp.pinpoint.web.websocket.PinpointWebSocketHandler;
+import com.navercorp.pinpoint.web.websocket.WebSocketHandlerRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
+
+/**
+ * @Author Taejin Koo
+ */
+@Configuration
+@EnableWebSocket
+@Component
+public class WebSocketConfig implements WebSocketConfigurer {
+
+ private static final String WEBSOCKET_SUFFIX = ".pinpointws";
+
+ @Autowired
+ private WebSocketHandlerRepository handlerRepository;
+
+ @Override
+ public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
+ for (PinpointWebSocketHandler handler : handlerRepository.getWebSocketHandlerRepository()) {
+ registry.addHandler(handler, handler.getRequestMapping() + WEBSOCKET_SUFFIX).addInterceptors(new HttpSessionHandshakeInterceptor());
+ }
+ }
+
+}
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/server/PinpointSocketManager.java b/web/src/main/java/com/navercorp/pinpoint/web/server/PinpointSocketManager.java
index 9b13a2082..30cd4ad3e 100644
--- a/web/src/main/java/com/navercorp/pinpoint/web/server/PinpointSocketManager.java
+++ b/web/src/main/java/com/navercorp/pinpoint/web/server/PinpointSocketManager.java
@@ -26,6 +26,7 @@ import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
+import com.navercorp.pinpoint.common.bo.AgentInfoBo;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -113,6 +114,10 @@ public class PinpointSocketManager {
return serverAcceptor.getWritableServerList();
}
+ public PinpointServer getCollector(AgentInfoBo agentInfo) {
+ return getCollector(agentInfo.getApplicationName(), agentInfo.getAgentId(), agentInfo.getStartTime());
+ }
+
public PinpointServer getCollector(String applicationName, String agentId, long startTimeStamp) {
List agentNameList = clusterManager.getRegisteredAgentList(applicationName, agentId, startTimeStamp);
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/service/AgentService.java b/web/src/main/java/com/navercorp/pinpoint/web/service/AgentService.java
new file mode 100644
index 000000000..ac4582a3e
--- /dev/null
+++ b/web/src/main/java/com/navercorp/pinpoint/web/service/AgentService.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * * Copyright 2014 NAVER Corp.
+ * *
+ * * 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 com.navercorp.pinpoint.web.service;
+
+import com.navercorp.pinpoint.common.bo.AgentInfoBo;
+import com.navercorp.pinpoint.thrift.dto.command.TActiveThreadResponse;
+import org.apache.thrift.TBase;
+import org.apache.thrift.TException;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Author Taejin Koo
+ */
+public interface AgentService {
+
+ List get(String applicationName);
+
+ Map getActiveThreadStatus(List agentInfoList) throws TException;
+
+ Map getActiveThreadStatus(List agentInfoList, byte[] payload) throws TException;
+
+}
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/service/AgentServiceImpl.java b/web/src/main/java/com/navercorp/pinpoint/web/service/AgentServiceImpl.java
new file mode 100644
index 000000000..20386e4aa
--- /dev/null
+++ b/web/src/main/java/com/navercorp/pinpoint/web/service/AgentServiceImpl.java
@@ -0,0 +1,133 @@
+/*
+ *
+ * * Copyright 2014 NAVER Corp.
+ * *
+ * * 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 com.navercorp.pinpoint.web.service;
+
+import com.navercorp.pinpoint.common.bo.AgentInfoBo;
+import com.navercorp.pinpoint.rpc.Future;
+import com.navercorp.pinpoint.rpc.ResponseMessage;
+import com.navercorp.pinpoint.rpc.server.PinpointServer;
+import com.navercorp.pinpoint.rpc.util.ListUtils;
+import com.navercorp.pinpoint.thrift.dto.command.TActiveThread;
+import com.navercorp.pinpoint.thrift.dto.command.TActiveThreadResponse;
+import com.navercorp.pinpoint.thrift.dto.command.TCommandTransfer;
+import com.navercorp.pinpoint.thrift.io.DeserializerFactory;
+import com.navercorp.pinpoint.thrift.io.HeaderTBaseDeserializer;
+import com.navercorp.pinpoint.thrift.io.HeaderTBaseSerializer;
+import com.navercorp.pinpoint.thrift.io.SerializerFactory;
+import com.navercorp.pinpoint.thrift.util.SerializationUtils;
+import com.navercorp.pinpoint.web.server.PinpointSocketManager;
+import com.navercorp.pinpoint.web.vo.Range;
+import org.apache.thrift.TBase;
+import org.apache.thrift.TException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * @Author Taejin Koo
+ */
+@Service
+public class AgentServiceImpl implements AgentService {
+
+ @Autowired
+ private AgentInfoService agentInfoService;
+
+ @Autowired
+ private PinpointSocketManager pinpointSocketManager;
+
+ @Autowired
+ private SerializerFactory commandSerializerFactory;
+
+ @Autowired
+ private DeserializerFactory commandDeserializerFactory;
+
+ @Override
+ public List get(String applicationName) {
+ List agentInfoList = new ArrayList();
+
+ long currentTime = System.currentTimeMillis();
+ Range range = new Range(currentTime, currentTime);
+ SortedMap> applicationAgentList = agentInfoService.getApplicationAgentList(applicationName, range);
+ for (Map.Entry> entry : applicationAgentList.entrySet()) {
+ AgentInfoBo agentInfo = ListUtils.getFirst(entry.getValue(), null);
+ ListUtils.addIfValueNotNull(agentInfoList, agentInfo);
+ }
+
+ return agentInfoList;
+ }
+
+ @Override
+ public Map getActiveThreadStatus(List agentInfoList) throws TException {
+ byte[] activeThread = serialize(new TActiveThread());
+ return getActiveThreadStatus(agentInfoList, activeThread);
+ }
+
+ @Override
+ public Map getActiveThreadStatus(List agentInfoList, byte[] payload) throws TException {
+ Map> futureMap = invoke(agentInfoList, payload);
+
+ Map responseMap = new HashMap();
+ for (Map.Entry> futureEntry : futureMap.entrySet()) {
+ String hostName = futureEntry.getKey();
+ Future future = futureEntry.getValue();
+ future.await();
+
+ ResponseMessage responseMessage = future.getResult();
+ TBase result = deserialize(responseMessage.getMessage());
+ if (result instanceof TActiveThreadResponse) {
+ responseMap.put(hostName, (TActiveThreadResponse) result);
+ }
+ }
+
+ return responseMap;
+ }
+
+ private Map> invoke(List agentInfoList, byte[] payload) throws TException {
+ Map> futureMap = new HashMap>();
+ for (AgentInfoBo agentInfo : agentInfoList) {
+ TCommandTransfer transferObject = createCommandTransferObject(agentInfo, payload);
+ PinpointServer collector = pinpointSocketManager.getCollector(agentInfo);
+ Future future = collector.request(serialize(transferObject));
+
+ futureMap.put(agentInfo.getHostName(), future);
+ }
+ return futureMap;
+ }
+
+ private byte[] serialize(TBase tBase) throws TException {
+ return SerializationUtils.serialize(tBase, commandSerializerFactory);
+ }
+
+ private TBase deserialize(byte[] objectData) throws TException {
+ return SerializationUtils.deserialize(objectData, commandDeserializerFactory);
+ }
+
+ private TCommandTransfer createCommandTransferObject(AgentInfoBo agentInfo, byte[] payload) {
+ TCommandTransfer transferObject = new TCommandTransfer();
+ transferObject.setApplicationName(agentInfo.getApplicationName());
+ transferObject.setAgentId(agentInfo.getAgentId());
+ transferObject.setStartTime(agentInfo.getStartTime());
+ transferObject.setPayload(payload);
+
+ return transferObject;
+ }
+
+}
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadStatusList.java b/web/src/main/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadStatusList.java
new file mode 100644
index 000000000..d4df9ed2c
--- /dev/null
+++ b/web/src/main/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadStatusList.java
@@ -0,0 +1,97 @@
+/*
+ *
+ * * Copyright 2014 NAVER Corp.
+ * *
+ * * 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 com.navercorp.pinpoint.web.vo;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.navercorp.pinpoint.thrift.dto.command.TActiveThreadResponse;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Author Taejin Koo
+ */
+@JsonSerialize(using = AgentActiveThreadStatusListSerializer.class)
+public class AgentActiveThreadStatusList {
+
+ private final Map agentActiveThreadReposioty;
+
+ public AgentActiveThreadStatusList(Map agentActiveThreadReposioty) {
+ this.agentActiveThreadReposioty = new HashMap();
+ }
+
+ public AgentActiveThreadStatusList(int initialCapacity) {
+ agentActiveThreadReposioty = new HashMap(initialCapacity);
+ }
+
+ public void add(String hostName, TActiveThreadResponse activeThreadStatus) {
+ agentActiveThreadReposioty.put(hostName, activeThreadStatus);
+ }
+
+ public void addAll(Map activeThreadStatuses) {
+ agentActiveThreadReposioty.putAll(activeThreadStatuses);
+ }
+
+ public Map getAgentActiveThreadReposioty() {
+ return agentActiveThreadReposioty;
+ }
+
+}
+
+class AgentActiveThreadStatusListSerializer extends JsonSerializer
+{
+ @Override
+ public void serialize(AgentActiveThreadStatusList agentActiveThreadStatusList, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
+ Map agentActiveThreadReposioty = agentActiveThreadStatusList.getAgentActiveThreadReposioty();
+
+ jgen.writeStartObject();
+ for (Map.Entry entry : agentActiveThreadReposioty.entrySet()) {
+ List activeThreadStatus = entry.getValue().getActiveThreadCount();
+ if (activeThreadStatus == null || activeThreadStatus.size() < 4) {
+ continue;
+ }
+
+ jgen.writeFieldName(entry.getKey());
+ jgen.writeStartObject();
+
+ jgen.writeFieldName("status");
+ jgen.writeStartArray();
+ jgen.writeNumber(activeThreadStatus.get(0));
+ jgen.writeNumber(activeThreadStatus.get(1));
+ jgen.writeNumber(activeThreadStatus.get(2));
+ jgen.writeNumber(activeThreadStatus.get(3));
+ jgen.writeEndArray();
+
+ jgen.writeNumberField("code", 0);
+ // will be added codeMessage
+
+ jgen.writeEndObject();
+ }
+
+ jgen.writeEndObject();
+
+ }
+}
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/websocket/ActiveThreadHandler.java b/web/src/main/java/com/navercorp/pinpoint/web/websocket/ActiveThreadHandler.java
new file mode 100644
index 000000000..00fa46ee2
--- /dev/null
+++ b/web/src/main/java/com/navercorp/pinpoint/web/websocket/ActiveThreadHandler.java
@@ -0,0 +1,208 @@
+/*
+ *
+ * * Copyright 2014 NAVER Corp.
+ * *
+ * * 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 com.navercorp.pinpoint.web.websocket;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.navercorp.pinpoint.common.bo.AgentInfoBo;
+import com.navercorp.pinpoint.common.util.PinpointThreadFactory;
+import com.navercorp.pinpoint.rpc.util.TimerFactory;
+import com.navercorp.pinpoint.thrift.dto.command.TActiveThreadResponse;
+import com.navercorp.pinpoint.web.service.AgentService;
+import com.navercorp.pinpoint.web.vo.AgentActiveThreadStatusList;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.Timer;
+import org.jboss.netty.util.TimerTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.handler.TextWebSocketHandler;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * @Author Taejin Koo
+ */
+public class ActiveThreadHandler extends TextWebSocketHandler implements PinpointWebSocketHandler {
+
+ private static final String APPLICATION_NAME_KEY = "applicationName";
+ private static final String DEFAULT_REQUEST_MAPPING = "/agent/activeThread";
+
+ private final String requestMapping;
+ private final AgentService agentSerivce;
+
+ // it will be changed.
+ private final long time = 1000;
+
+ private final PinpointThreadFactory threadFactory = new PinpointThreadFactory("ActiveThread Handler", true);
+ private final TimerFactory timerFactory = new TimerFactory();
+
+ private final Map> applicationGroup = new HashMap>();
+
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+ private final Lock readLock = lock.readLock();
+ private final Lock writeLock = lock.writeLock();
+
+ private final ObjectMapper jsonConverter = new ObjectMapper();
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private Timer timer;
+
+ public ActiveThreadHandler(AgentService agentSerivce) {
+ this(DEFAULT_REQUEST_MAPPING, agentSerivce);
+ }
+
+ public ActiveThreadHandler(String requestMapping, AgentService agentSerivce) {
+ this.requestMapping = requestMapping;
+ this.agentSerivce = agentSerivce;
+ }
+
+ @Override
+ public String getRequestMapping() {
+ return requestMapping;
+ }
+
+ @Override
+ public void afterConnectionEstablished(WebSocketSession newSession) throws Exception {
+ logger.info("ConnectionEstablished : {}", newSession);
+
+ List params = URLEncodedUtils.parse(newSession.getUri(), "UTF-8");
+ String applicationName = getValue(params, APPLICATION_NAME_KEY);
+ if (applicationName == null) {
+ logger.warn("Connection established refused. required parameter is missiong({}).", APPLICATION_NAME_KEY);
+ newSession.close(CloseStatus.POLICY_VIOLATION);
+ }
+ newSession.getAttributes().put(APPLICATION_NAME_KEY, applicationName);
+
+ writeLock.lock();
+ try {
+ List webSocketSessions = applicationGroup.get(applicationName);
+ if (webSocketSessions == null) {
+ webSocketSessions = new ArrayList();
+ applicationGroup.put(applicationName, webSocketSessions);
+ }
+ webSocketSessions.add(newSession);
+
+ if (timer == null) {
+ timer = timerFactory.createHashedWheelTimer(threadFactory, 100, TimeUnit.MILLISECONDS, 512);
+ Timeout timeout = timer.newTimeout(new ActiveThreadTimerTask(), time, TimeUnit.MILLISECONDS);
+ }
+ } finally {
+ writeLock.unlock();
+ }
+
+ super.afterConnectionEstablished(newSession);
+ }
+
+ @Override
+ public void afterConnectionClosed(WebSocketSession closeSession, CloseStatus status) throws Exception {
+ logger.info("ConnectionClosed : {}, caused : {}", closeSession, status);
+
+ String applicationName = (String) closeSession.getAttributes().get(APPLICATION_NAME_KEY);
+
+ writeLock.lock();
+ try {
+ if (applicationName != null) {
+ List webSocketSessions = applicationGroup.get(applicationName);
+ if (webSocketSessions == null) {
+ webSocketSessions = new ArrayList();
+ }
+ webSocketSessions.remove(closeSession);
+
+ if (webSocketSessions.size() == 0) {
+ applicationGroup.remove(applicationName);
+ }
+
+ if (applicationGroup.size() == 0) {
+ if (timer != null) {
+ timer.stop();
+ timer = null;
+ }
+ }
+ }
+ } finally {
+ writeLock.unlock();
+ }
+
+ super.afterConnectionClosed(closeSession, status);
+ }
+
+ @Override
+ protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
+ logger.info("handleTextMessage. session : {}, message : {}.", session, message);
+
+ // this method will be checked socket status.
+ super.handleTextMessage(session, message);
+ }
+
+ private String getValue(List params, String key) {
+ for (NameValuePair nv : params) {
+ if (key.equals(nv.getName())) {
+ return nv.getValue();
+ }
+ }
+
+ return null;
+ }
+
+ private class ActiveThreadTimerTask implements TimerTask {
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+ logger.info("ActiveThreadTimerTask started.");
+
+ readLock.lock();
+ try {
+ for (Map.Entry> applicationEntry : applicationGroup.entrySet()) {
+ List agentInfoList = agentSerivce.get(applicationEntry.getKey());
+ Map activeThreadStatuses = agentSerivce.getActiveThreadStatus(agentInfoList);
+
+ AgentActiveThreadStatusList agentActiveThreadStatusList = new AgentActiveThreadStatusList(activeThreadStatuses.size());
+ agentActiveThreadStatusList.addAll(activeThreadStatuses);
+ String textMessage = jsonConverter.writeValueAsString(agentActiveThreadStatusList);
+
+ for (WebSocketSession session : applicationEntry.getValue()) {
+ session.sendMessage(new TextMessage(textMessage));
+ }
+ }
+
+ if (timer != null) {
+ timer.newTimeout(new ActiveThreadTimerTask(), time, TimeUnit.MILLISECONDS);
+ }
+
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ }
+
+}
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/websocket/PinpointWebSocketHandler.java b/web/src/main/java/com/navercorp/pinpoint/web/websocket/PinpointWebSocketHandler.java
new file mode 100644
index 000000000..10d1d836b
--- /dev/null
+++ b/web/src/main/java/com/navercorp/pinpoint/web/websocket/PinpointWebSocketHandler.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * * Copyright 2014 NAVER Corp.
+ * *
+ * * 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 com.navercorp.pinpoint.web.websocket;
+
+import org.springframework.web.socket.WebSocketHandler;
+
+/**
+ * @Author Taejin Koo
+ */
+public interface PinpointWebSocketHandler extends WebSocketHandler {
+
+ String getRequestMapping();
+
+}
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/websocket/WebSocketHandlerRepository.java b/web/src/main/java/com/navercorp/pinpoint/web/websocket/WebSocketHandlerRepository.java
new file mode 100644
index 000000000..28e6dd82e
--- /dev/null
+++ b/web/src/main/java/com/navercorp/pinpoint/web/websocket/WebSocketHandlerRepository.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * * Copyright 2014 NAVER Corp.
+ * *
+ * * 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 com.navercorp.pinpoint.web.websocket;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Author Taejin Koo
+ */
+public class WebSocketHandlerRepository {
+
+ private final List webSocketHandlerRepository;
+
+ public WebSocketHandlerRepository(List webSocketHandlerRepository) {
+ this.webSocketHandlerRepository = webSocketHandlerRepository;
+ }
+
+ public List getWebSocketHandlerRepository() {
+ return new ArrayList(webSocketHandlerRepository);
+ }
+
+}
diff --git a/web/src/main/resources/applicationContext-web.xml b/web/src/main/resources/applicationContext-web.xml
index 74957febd..01e98628a 100644
--- a/web/src/main/resources/applicationContext-web.xml
+++ b/web/src/main/resources/applicationContext-web.xml
@@ -58,6 +58,8 @@
+
+
diff --git a/web/src/main/resources/applicationContext-websocket.xml b/web/src/main/resources/applicationContext-websocket.xml
new file mode 100644
index 000000000..4252ceae2
--- /dev/null
+++ b/web/src/main/resources/applicationContext-websocket.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/main/webapp/WEB-INF/web.xml b/web/src/main/webapp/WEB-INF/web.xml
index 7ab816ccc..26bdda282 100644
--- a/web/src/main/webapp/WEB-INF/web.xml
+++ b/web/src/main/webapp/WEB-INF/web.xml
@@ -37,6 +37,11 @@
*.pinpoint
+
+ pinpoint-web
+ *.pinpointws
+
+
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter