001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.php.dbgp;
042:
043: import java.io.IOException;
044: import java.net.Socket;
045: import java.util.ArrayList;
046: import java.util.LinkedList;
047: import java.util.List;
048: import java.util.concurrent.atomic.AtomicBoolean;
049: import java.util.concurrent.atomic.AtomicInteger;
050: import java.util.concurrent.atomic.AtomicReference;
051: import java.util.logging.Level;
052: import java.util.logging.Logger;
053:
054: import org.netbeans.api.debugger.DebuggerEngine;
055: import org.netbeans.api.debugger.DebuggerManager;
056: import org.netbeans.api.debugger.Session;
057: import org.netbeans.modules.php.dbgp.api.SessionId;
058: import org.netbeans.modules.php.dbgp.models.AbstractIDEBridge;
059: import org.netbeans.modules.php.dbgp.packets.DbgpCommand;
060: import org.netbeans.modules.php.dbgp.packets.DbgpMessage;
061: import org.netbeans.modules.php.dbgp.packets.DbgpResponse;
062: import org.netbeans.modules.php.dbgp.packets.InitMessage;
063:
064: /**
065: * @author ads
066: *
067: */
068: public class DebugSession implements Runnable {
069:
070: private static final int SLEEP_TIME = 100;
071:
072: DebugSession(Socket socket) {
073: mySocket = socket;
074: isStop = new AtomicBoolean(false);
075: myCommands = new LinkedList<DbgpCommand>();
076: mySessionId = new AtomicReference<SessionId>();
077: myBridge = new IDESessionBridge();
078: myFileName = new AtomicReference<String>();
079: myEngine = new AtomicReference<DebuggerEngine>();
080: }
081:
082: /* (non-Javadoc)
083: * @see java.lang.Runnable#run()
084: */
085: public void run() {
086: setSessionThread(Thread.currentThread());
087: boolean moreCommands = false;
088: synchronized (myCommands) {
089: moreCommands = myCommands.size() > 0;
090: }
091: while (!isStop.get() || moreCommands) {
092: try {
093: sendCommands();
094: receiveData();
095: sleepTillNewCommand();
096: } catch (IOException e) {
097: log(e);
098: }
099: }
100: setSessionThread(null);
101: try {
102: getSocket().close();
103: } catch (IOException e) {
104: log(e);
105: }
106: }
107:
108: public void sendCommandLater(DbgpCommand command) {
109: synchronized (this ) {
110: /*
111: * Do not collect command before session is not initialized.
112: * So any command before Init message will not be sent.
113: * ( F.e. commands for getting watch values will be just ignored
114: * if they was requested before Init message ).
115: */
116: if (getSessionId() == null) {
117: return;
118: }
119: if (getSessionThread() == null) {
120: return;
121: }
122: addCommand(command);
123: getSessionThread().interrupt();
124: }
125: }
126:
127: public DbgpResponse sendSynchronCommand(DbgpCommand command) {
128: if (getSessionThread() != Thread.currentThread()) {
129: throw new IllegalStateException("Method incorrect usage. "
130: + "It should be called in handler thread only"); // NOI18N
131: }
132: try {
133: command.send(getSocket().getOutputStream());
134: if (command.wantAcknowledgment()) {
135: DbgpMessage message = receiveData(command);
136: if (message instanceof DbgpResponse) {
137: return (DbgpResponse) message;
138: }
139: }
140: } catch (IOException e) {
141: log(e);
142: }
143: return null;
144: }
145:
146: public String getTransactionId() {
147: return myTransactionId.getAndIncrement() + "";
148: }
149:
150: public void setStop() {
151: isStop.set(true);
152: getBridge().setSuspended(false);
153: getBridge().hideAnnotations();
154: StartActionProviderImpl.getInstance().removeSession(this );
155: getBridge().getBreakpointModel().setCurrentStack(null, this );
156: getBridge().getCallStackModel().clearModel();
157: getBridge().getThreadsModel().update();
158: getBridge().getVariablesModel().clearModel();
159: getBridge().getWatchesModel().clearModel();
160: }
161:
162: public void setId(InitMessage message) {
163: setSessionFile(message.getFileUri());
164: String sessionId = message.getSessionId();
165: DebuggerEngine[] engines = DebuggerManager.getDebuggerManager()
166: .getDebuggerEngines();
167: for (DebuggerEngine engine : engines) {
168: SessionId id = (SessionId) engine.lookupFirst(null,
169: SessionId.class);
170: if (id.getId().equals(sessionId)) {
171: mySessionId.set(id);
172: id.setFileUri(message.getFileUri());
173: myEngine.set(engine);
174: }
175: }
176: Session[] sessions = DebuggerManager.getDebuggerManager()
177: .getSessions();
178: for (Session session : sessions) {
179: SessionId id = (SessionId) session.lookupFirst(null,
180: SessionId.class);
181: if (id.getId().equals(sessionId)) {
182: StartActionProviderImpl.getInstance()
183: .attachDebugSession(session, this );
184: }
185: }
186: }
187:
188: public SessionId getSessionId() {
189: return mySessionId.get();
190: }
191:
192: public IDESessionBridge getBridge() {
193: return myBridge;
194: }
195:
196: public String getFileName() {
197: return myFileName.get();
198: }
199:
200: private void setSessionFile(String fileName) {
201: myFileName.set(fileName);
202: }
203:
204: private void sleepTillNewCommand() {
205: try {
206: // Wake up every 100 milliseconds and see if the debuggee has something to say.
207: // The IDE side can interrupt the sleep to send new packets to the
208: // debugger.
209: Thread.sleep(SLEEP_TIME);
210: } catch (InterruptedException ie) {
211: // OK, run the look again.
212: }
213: }
214:
215: private synchronized void setSessionThread(Thread thread) {
216: mySessionThread = thread;
217: }
218:
219: private void sendCommands() throws IOException {
220: List<DbgpCommand> list;
221: synchronized (myCommands) {
222: list = new ArrayList<DbgpCommand>(myCommands);
223: myCommands.clear();
224: }
225: for (DbgpCommand command : list) {
226: command.send(getSocket().getOutputStream());
227: if (command.wantAcknowledgment()) {
228: receiveData(command);
229: }
230: }
231: }
232:
233: private void addCommand(DbgpCommand command) {
234: synchronized (myCommands) {
235: myCommands.add(command);
236: }
237: }
238:
239: private Thread getSessionThread() {
240: return mySessionThread;
241: }
242:
243: private void receiveData() throws IOException {
244: receiveData(null);
245: }
246:
247: private DbgpMessage receiveData(DbgpCommand command)
248: throws IOException {
249: if (command != null
250: || getSocket().getInputStream().available() > 0) {
251: DbgpMessage message = DbgpMessage.create(getSocket()
252: .getInputStream());
253: handleMessage(command, message);
254: return message;
255: }
256: return null;
257: }
258:
259: private void handleMessage(DbgpCommand command, DbgpMessage message)
260: throws IOException {
261: if (message == null) {
262: return;
263: }
264:
265: if (command == null) {
266: // this is case when we don't need achnowl-t
267: message.process(this , null);
268: return;
269: }
270:
271: boolean awaited = false;
272: if (message instanceof DbgpResponse) {
273: DbgpResponse response = (DbgpResponse) message;
274: String id = response.getTransactionId();
275: if (id.equals(command.getTransactionId())) {
276: awaited = true;
277: message.process(this , command);
278: }
279: }
280: if (!awaited) {
281: message.process(this , null);
282: receiveData(command);
283: }
284: }
285:
286: private Socket getSocket() {
287: return mySocket;
288: }
289:
290: private void log(IOException e) {
291: Logger.getLogger(DebugSession.class.getName()).log(
292: Level.SEVERE, null, e);
293: }
294:
295: /*
296: * This class is associated with DebugSession but is intended for
297: * cooperation with IDE UI.
298: */
299: public class IDESessionBridge extends AbstractIDEBridge {
300:
301: /* (non-Javadoc)
302: * @see org.netbeans.modules.php.dbgp.models.AbstractIDEBridge#getEngine()
303: */
304: @Override
305: protected DebuggerEngine getEngine() {
306: return myEngine.get();
307: }
308:
309: /* (non-Javadoc)
310: * @see org.netbeans.modules.php.dbgp.models.AbstractIDEBridge#getDebugSession()
311: */
312: protected DebugSession getDebugSession() {
313: return DebugSession.this ;
314: }
315:
316: }
317:
318: private Socket mySocket;
319:
320: private AtomicBoolean isStop;
321:
322: private Thread mySessionThread;
323:
324: private List<DbgpCommand> myCommands;
325:
326: private AtomicReference<SessionId> mySessionId;
327:
328: private AtomicReference<DebuggerEngine> myEngine;
329:
330: private static final AtomicInteger myTransactionId = new AtomicInteger(
331: 0);
332:
333: private final IDESessionBridge myBridge;
334:
335: private AtomicReference<String> myFileName;
336:
337: }
|