1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.monitor;
14
15 import java.io.BufferedReader;
16 import java.io.ByteArrayInputStream;
17 import java.io.ByteArrayOutputStream;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.PrintWriter;
21 import java.io.StringReader;
22 import java.nio.channels.SocketChannel;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29
30 import javax.servlet.ServletException;
31 import javax.servlet.ServletInputStream;
32 import javax.servlet.ServletOutputStream;
33 import javax.servlet.http.HttpServletRequest;
34 import javax.servlet.http.HttpServletResponse;
35
36 import org.apache.log4j.Logger;
37 import org.mortbay.component.AbstractLifeCycle;
38 import org.mortbay.io.Connection;
39 import org.mortbay.io.EndPoint;
40 import org.mortbay.io.nio.SelectChannelEndPoint;
41 import org.mortbay.jetty.Connector;
42 import org.mortbay.jetty.HttpConnection;
43 import org.mortbay.jetty.Request;
44 import org.mortbay.jetty.RequestLog;
45 import org.mortbay.jetty.Response;
46 import org.mortbay.jetty.Server;
47 import org.mortbay.jetty.handler.AbstractHandler;
48 import org.mortbay.jetty.handler.RequestLogHandler;
49 import org.mortbay.jetty.nio.SelectChannelConnector;
50 import org.mortbay.jetty.security.SslSocketConnector;
51 import org.mortbay.thread.QueuedThreadPool;
52
53 import com.eviware.soapui.SoapUI;
54 import com.eviware.soapui.impl.wsdl.mock.DispatchException;
55 import com.eviware.soapui.impl.wsdl.mock.WsdlMockRunner;
56 import com.eviware.soapui.impl.wsdl.mock.WsdlMockService;
57 import com.eviware.soapui.impl.wsdl.support.soap.SoapMessageBuilder;
58 import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
59 import com.eviware.soapui.model.mock.MockResult;
60 import com.eviware.soapui.model.mock.MockRunner;
61 import com.eviware.soapui.model.mock.MockService;
62 import com.eviware.soapui.settings.HttpSettings;
63 import com.eviware.soapui.settings.SSLSettings;
64 import com.eviware.soapui.support.StringUtils;
65 import com.eviware.soapui.support.Tools;
66 import com.eviware.soapui.support.UISupport;
67 import com.eviware.soapui.support.log.JettyLogger;
68
69 /***
70 * Core Mock-Engine hosting a Jetty web server
71 *
72 * @author ole.matzura
73 */
74
75 public class MockEngine
76 {
77 public final static Logger log = Logger.getLogger( MockEngine.class );
78
79 private Server server;
80 private Map<Integer, Map<String, List<MockRunner>>> runners = new HashMap<Integer, Map<String, List<MockRunner>>>();
81 private Map<Integer, SoapUIConnector> connectors = new HashMap<Integer, SoapUIConnector>();
82 private List<MockRunner> mockRunners = new ArrayList<MockRunner>();
83
84 private SslSocketConnector sslConnector;
85
86 public MockEngine()
87 {
88 System.setProperty( "org.mortbay.log.class", JettyLogger.class.getName() );
89 }
90
91 public boolean hasRunningMock( MockService mockService )
92 {
93 for( MockRunner runner : mockRunners )
94 if( runner.getMockService() == mockService )
95 return true;
96
97 return false;
98 }
99
100 public synchronized void startMockService( MockRunner runner ) throws Exception
101 {
102 if( server == null )
103 initServer();
104
105 synchronized( server )
106 {
107 WsdlMockService mockService = ( WsdlMockService )runner.getMockService();
108 int port = mockService.getPort();
109
110 if( !runners.containsKey( port ) )
111 {
112 SoapUIConnector connector = new SoapUIConnector();
113
114 connector.setPort( port );
115 if( sslConnector != null )
116 connector.setConfidentialPort( sslConnector.getPort() );
117
118 if( mockService.getBindToHostOnly() )
119 {
120 String host = mockService.getHost();
121 if( StringUtils.hasContent( host ) )
122 {
123 connector.setHost( host );
124 }
125 }
126
127 boolean wasRunning = server.isRunning();
128
129 if( wasRunning )
130 {
131 server.stop();
132 }
133
134 server.addConnector( connector );
135 try
136 {
137 server.start();
138 }
139 catch( RuntimeException e )
140 {
141 UISupport.showErrorMessage( e );
142
143 server.removeConnector( connector );
144 if( wasRunning )
145 {
146 server.start();
147 return;
148 }
149 }
150
151 connectors.put( new Integer( port ), connector );
152 runners.put( new Integer( port ), new HashMap<String, List<MockRunner>>() );
153 }
154
155 Map<String, List<MockRunner>> map = runners.get( port );
156 String path = mockService.getPath();
157 if( !map.containsKey( path ) )
158 {
159 map.put( path, new ArrayList<MockRunner>() );
160 }
161 map.get( path ).add( runner );
162 mockRunners.add( runner );
163
164 log.info( "Started mockService [" + mockService.getName() + "] on port [" + port + "] at path [" + path + "]" );
165 }
166 }
167
168 private void initServer() throws Exception
169 {
170 server = new Server();
171 QueuedThreadPool threadPool = new QueuedThreadPool();
172
173 server.setThreadPool( threadPool );
174 server.setHandler( new ServerHandler() );
175
176 RequestLogHandler logHandler = new RequestLogHandler();
177 logHandler.setRequestLog( new MockRequestLog() );
178 server.addHandler( logHandler );
179
180 if( SoapUI.getSettings().getBoolean( SSLSettings.ENABLE_MOCK_SSL ) )
181 {
182 sslConnector = new SslSocketConnector();
183 sslConnector.setKeystore( SoapUI.getSettings().getString( SSLSettings.MOCK_KEYSTORE, null ) );
184 sslConnector.setPassword( SoapUI.getSettings().getString( SSLSettings.MOCK_PASSWORD, null ) );
185 sslConnector.setKeyPassword( SoapUI.getSettings().getString( SSLSettings.MOCK_KEYSTORE_PASSWORD, null ) );
186 sslConnector.setTruststore( SoapUI.getSettings().getString( SSLSettings.MOCK_TRUSTSTORE, null ) );
187 sslConnector.setTrustPassword( SoapUI.getSettings().getString( SSLSettings.MOCK_TRUSTSTORE_PASSWORD, null ) );
188 sslConnector.setMaxIdleTime( 30000 );
189 sslConnector.setPort( ( int )SoapUI.getSettings().getLong( SSLSettings.MOCK_PORT, 443 ) );
190 sslConnector.setNeedClientAuth( SoapUI.getSettings().getBoolean( SSLSettings.CLIENT_AUTHENTICATION ) );
191
192 server.addConnector( sslConnector );
193 }
194 }
195
196 public void stopMockService( WsdlMockRunner runner )
197 {
198 synchronized( server )
199 {
200 MockService mockService = runner.getMockService();
201 final Integer port = new Integer( mockService.getPort() );
202 Map<String, List<MockRunner>> map = runners.get( port );
203
204 if( map == null )
205 return;
206
207 map.get( mockService.getPath() ).remove( runner );
208 if( map.get( mockService.getPath() ).isEmpty() )
209 {
210 map.remove( mockService.getPath() );
211 }
212
213 mockRunners.remove( runner );
214
215 log.info( "Stopped MockService [" + mockService.getName() + "] on port [" + port + "]" );
216
217 if( map.isEmpty() && !SoapUI.getSettings().getBoolean( HttpSettings.LEAVE_MOCKENGINE ) )
218 {
219 SoapUIConnector connector = connectors.get( port );
220 if( connector == null )
221 {
222 log.warn( "Missing connectors on port [" + port + "]" );
223 return;
224 }
225
226 try
227 {
228 log.info( "Stopping connector on port " + port );
229 if( !connector.waitUntilIdle( 5000 ) )
230 {
231 log.warn( "Failed to wait for idle.. stopping connector anyway.." );
232 }
233 connector.stop();
234 }
235 catch( Exception e )
236 {
237 SoapUI.logError( e );
238 }
239 server.removeConnector( connector );
240 runners.remove( port );
241 if( runners.isEmpty() )
242 {
243 try
244 {
245 log.info( "No more connectors.. stopping server" );
246 server.stop();
247 if( sslConnector != null )
248 {
249
250
251
252 }
253 }
254 catch( Exception e )
255 {
256 SoapUI.logError( e );
257 }
258 }
259 }
260 }
261 }
262
263 private class SoapUIConnector extends SelectChannelConnector
264 {
265 private Set<HttpConnection> connections = new HashSet<HttpConnection>();
266
267 @Override
268 protected void connectionClosed( HttpConnection arg0 )
269 {
270 super.connectionClosed( arg0 );
271 connections.remove( arg0 );
272 }
273
274 @Override
275 protected void connectionOpened( HttpConnection arg0 )
276 {
277 super.connectionOpened( arg0 );
278 connections.add( arg0 );
279 }
280
281 @Override
282 protected Connection newConnection( SocketChannel socketChannel, SelectChannelEndPoint selectChannelEndPoint )
283 {
284 return new SoapUIHttpConnection( SoapUIConnector.this, selectChannelEndPoint, getServer() );
285 }
286
287 public boolean waitUntilIdle( long maxwait ) throws Exception
288 {
289 while( maxwait > 0 && hasActiveConnections() )
290 {
291 System.out.println( "Waiting for active connections to finish.." );
292 Thread.sleep( 500 );
293 maxwait -= 500;
294 }
295
296 return !hasActiveConnections();
297 }
298
299 private boolean hasActiveConnections()
300 {
301 for( HttpConnection connection : connections )
302 {
303 if( !connection.isIdle() )
304 return true;
305 }
306
307 return false;
308 }
309 }
310
311 private class SoapUIHttpConnection extends HttpConnection
312 {
313 private CapturingServletInputStream capturingServletInputStream;
314 private BufferedServletInputStream bufferedServletInputStream;
315 private MockEngine.CapturingServletOutputStream capturingServletOutputStream;
316
317 public SoapUIHttpConnection( Connector connector, EndPoint endPoint, Server server )
318 {
319 super( connector, endPoint, server );
320 }
321
322 @Override
323 public ServletInputStream getInputStream()
324 {
325 if( SoapUI.getSettings().getBoolean( HttpSettings.ENABLE_MOCK_WIRE_LOG ) )
326 {
327 if( capturingServletInputStream == null )
328 {
329 capturingServletInputStream = new CapturingServletInputStream( super.getInputStream() );
330 bufferedServletInputStream = new BufferedServletInputStream( capturingServletInputStream );
331 }
332 }
333 else
334 {
335 bufferedServletInputStream = new BufferedServletInputStream( super.getInputStream() );
336 }
337
338 return bufferedServletInputStream;
339 }
340
341 @Override
342 public ServletOutputStream getOutputStream()
343 {
344 if( SoapUI.getSettings().getBoolean( HttpSettings.ENABLE_MOCK_WIRE_LOG ) )
345 {
346 if( capturingServletOutputStream == null )
347 {
348 capturingServletOutputStream = new CapturingServletOutputStream( super.getOutputStream() );
349 }
350 return capturingServletOutputStream;
351 }
352 else return super.getOutputStream();
353 }
354 }
355
356 private class BufferedServletInputStream extends ServletInputStream
357 {
358 private InputStream source = null;
359 private byte[] data = null;
360 private InputStream buffer1 = null;
361
362 public BufferedServletInputStream( InputStream is )
363 {
364 super();
365 source = is;
366 }
367
368 public InputStream getBuffer() throws IOException
369 {
370 if( source.available() > 0 )
371 {
372
373 data = null;
374 }
375 if( data == null )
376 {
377 ByteArrayOutputStream out = Tools.readAll( source, Tools.READ_ALL );
378 data = out.toByteArray();
379 }
380 if( buffer1 == null )
381 {
382 buffer1 = new ByteArrayInputStream( data );
383 }
384 return buffer1;
385 }
386
387 public int read() throws IOException
388 {
389 int i = getBuffer().read();
390 return i;
391 }
392
393 public int readLine( byte[] b, int off, int len ) throws IOException
394 {
395
396 if( len <= 0 )
397 {
398 return 0;
399 }
400 int count = 0, c;
401
402 while( ( c = read() ) != -1 )
403 {
404 b[off++ ] = ( byte )c;
405 count++ ;
406 if( c == '\n' || count == len )
407 {
408 break;
409 }
410 }
411 return count > 0 ? count : -1;
412 }
413
414 public int read( byte[] b ) throws IOException
415 {
416 int i = getBuffer().read( b );
417 return i;
418 }
419
420 public int read( byte[] b, int off, int len ) throws IOException
421 {
422 int result = getBuffer().read( b, off, len );
423 return result;
424 }
425
426 public long skip( long n ) throws IOException
427 {
428 return getBuffer().skip( n );
429 }
430
431 public int available() throws IOException
432 {
433 return getBuffer().available();
434 }
435
436 public void close() throws IOException
437 {
438 getBuffer().close();
439 }
440
441 public void mark( int readlimit )
442 {
443
444 }
445
446 public boolean markSupported()
447 {
448 return false;
449 }
450
451 public void reset() throws IOException
452 {
453 buffer1 = null;
454 }
455 }
456
457 private class CapturingServletOutputStream extends ServletOutputStream
458 {
459 private ServletOutputStream outputStream;
460 private ByteArrayOutputStream captureOutputStream = new ByteArrayOutputStream();
461
462 public CapturingServletOutputStream( ServletOutputStream outputStream )
463 {
464 this.outputStream = outputStream;
465 }
466
467 public void print( String s ) throws IOException
468 {
469 outputStream.print( s );
470 }
471
472 public void print( boolean b ) throws IOException
473 {
474 outputStream.print( b );
475 }
476
477 public void print( char c ) throws IOException
478 {
479 outputStream.print( c );
480 }
481
482 public void print( int i ) throws IOException
483 {
484 outputStream.print( i );
485 }
486
487 public void print( long l ) throws IOException
488 {
489 outputStream.print( l );
490 }
491
492 public void print( float v ) throws IOException
493 {
494 outputStream.print( v );
495 }
496
497 public void print( double v ) throws IOException
498 {
499 outputStream.print( v );
500 }
501
502 public void println() throws IOException
503 {
504 outputStream.println();
505 }
506
507 public void println( String s ) throws IOException
508 {
509 outputStream.println( s );
510 }
511
512 public void println( boolean b ) throws IOException
513 {
514 outputStream.println( b );
515 }
516
517 public void println( char c ) throws IOException
518 {
519 outputStream.println( c );
520 }
521
522 public void println( int i ) throws IOException
523 {
524 outputStream.println( i );
525 }
526
527 public void println( long l ) throws IOException
528 {
529 outputStream.println( l );
530 }
531
532 public void println( float v ) throws IOException
533 {
534 outputStream.println( v );
535 }
536
537 public void println( double v ) throws IOException
538 {
539 outputStream.println( v );
540 }
541
542 public void write( int b ) throws IOException
543 {
544 captureOutputStream.write( b );
545 outputStream.write( b );
546 }
547
548 public void write( byte[] b ) throws IOException
549 {
550 captureOutputStream.write( b );
551 outputStream.write( b );
552 }
553
554 public void write( byte[] b, int off, int len ) throws IOException
555 {
556 captureOutputStream.write( b, off, len );
557 outputStream.write( b, off, len );
558 }
559
560 public void flush() throws IOException
561 {
562 outputStream.flush();
563 }
564
565 public void close() throws IOException
566 {
567 outputStream.close();
568
569
570 }
571 }
572
573 private class CapturingServletInputStream extends ServletInputStream
574 {
575 private ServletInputStream inputStream;
576 private ByteArrayOutputStream captureOutputStream = new ByteArrayOutputStream();
577
578 public CapturingServletInputStream( ServletInputStream inputStream )
579 {
580 this.inputStream = inputStream;
581 }
582
583 public int read() throws IOException
584 {
585 int i = inputStream.read();
586 captureOutputStream.write( i );
587 return i;
588 }
589
590 public int readLine( byte[] bytes, int i, int i1 ) throws IOException
591 {
592 int result = inputStream.readLine( bytes, i, i1 );
593 captureOutputStream.write( bytes, i, i1 );
594 return result;
595 }
596
597 public int read( byte[] b ) throws IOException
598 {
599 int i = inputStream.read( b );
600 captureOutputStream.write( b );
601 return i;
602 }
603
604 public int read( byte[] b, int off, int len ) throws IOException
605 {
606 int result = inputStream.read( b, off, len );
607 if( result != -1 )
608 captureOutputStream.write( b, off, result );
609 return result;
610 }
611
612 public long skip( long n ) throws IOException
613 {
614 return inputStream.skip( n );
615 }
616
617 public int available() throws IOException
618 {
619 return inputStream.available();
620 }
621
622 public void close() throws IOException
623 {
624 inputStream.close();
625
626
627 }
628
629 public void mark( int readlimit )
630 {
631 inputStream.mark( readlimit );
632 }
633
634 public boolean markSupported()
635 {
636 return inputStream.markSupported();
637 }
638
639 public void reset() throws IOException
640 {
641 inputStream.reset();
642 }
643 }
644
645 private class ServerHandler extends AbstractHandler
646 {
647 public void handle( String target, HttpServletRequest request, HttpServletResponse response, int dispatch )
648 throws IOException, ServletException
649 {
650
651 Map<String, List<MockRunner>> map = runners.get( request.getLocalPort() );
652
653
654 if( map == null && sslConnector != null && request.getLocalPort() == sslConnector.getPort() )
655 {
656 for( Map<String, List<MockRunner>> runnerMap : runners.values() )
657 {
658 if( runnerMap.containsKey( request.getPathInfo() ) )
659 {
660 map = runnerMap;
661 break;
662 }
663 }
664 }
665
666 if( map != null )
667 {
668 List<MockRunner> wsdlMockRunners = map.get( request.getPathInfo() );
669 if( wsdlMockRunners == null && request.getMethod().equals( "GET" ) )
670 {
671 for( String root : map.keySet() )
672 {
673 if( request.getPathInfo().startsWith( root ) )
674 {
675 wsdlMockRunners = map.get( root );
676 }
677 }
678 }
679
680 if( wsdlMockRunners != null )
681 {
682 try
683 {
684 DispatchException ex = null;
685 MockResult result = null;
686
687 for( MockRunner wsdlMockRunner : wsdlMockRunners )
688 {
689 if( !wsdlMockRunner.isRunning() )
690 continue;
691
692 try
693 {
694 result = wsdlMockRunner.dispatchRequest( request, response );
695 if( result != null )
696 result.finish();
697
698
699 break;
700 }
701 catch( DispatchException e )
702 {
703 log.debug( wsdlMockRunner.getMockService().getName() + " was unable to dispatch mock request ",
704 e );
705
706 ex = e;
707 }
708 }
709
710 if( ex != null && result == null )
711 throw ex;
712 }
713 catch( Exception e )
714 {
715 SoapUI.logError( e );
716
717 response.setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
718 response.setContentType( "text/html" );
719 response.getWriter().print(
720 SoapMessageBuilder.buildFault( "Server", e.getMessage(), SoapVersion.Utils
721 .getSoapVersionForContentType( request.getContentType(), SoapVersion.Soap11 ) ) );
722
723 }
724 }
725 else
726 {
727 printMockServiceList( response );
728 }
729 }
730 else
731 {
732 printMockServiceList( response );
733 }
734
735 response.flushBuffer();
736 }
737
738 private void printMockServiceList( HttpServletResponse response ) throws IOException
739 {
740 response.setStatus( HttpServletResponse.SC_OK );
741 response.setContentType( "text/html" );
742
743 MockRunner[] mockRunners = getMockRunners();
744 PrintWriter out = response.getWriter();
745 out
746 .print( "<html><body><p>There are currently " + mockRunners.length
747 + " running soapUI MockServices</p><ul>" );
748
749 for( MockRunner mockRunner : mockRunners )
750 {
751 out.print( "<li><a href=\"" );
752 out.print( mockRunner.getMockService().getPath() + "?WSDL" );
753 out.print( "\">" + mockRunner.getMockService().getName() + "</a></li>" );
754 }
755
756 out.print( "</ul></p></body></html>" );
757 }
758 }
759
760 public MockRunner[] getMockRunners()
761 {
762 return mockRunners.toArray( new MockRunner[mockRunners.size()] );
763 }
764
765 private class MockRequestLog extends AbstractLifeCycle implements RequestLog
766 {
767 public void log( Request request, Response response )
768 {
769 if( !SoapUI.getSettings().getBoolean( HttpSettings.ENABLE_MOCK_WIRE_LOG ) )
770 return;
771
772 if( SoapUI.getLogMonitor() == null || SoapUI.getLogMonitor().getLogArea( "jetty log" ) == null
773 || SoapUI.getLogMonitor().getLogArea( "jetty log" ).getLoggers() == null )
774 return;
775
776 Logger logger = SoapUI.getLogMonitor().getLogArea( "jetty log" ).getLoggers()[0];
777
778 try
779 {
780 ServletInputStream inputStream = request.getInputStream();
781 if( inputStream instanceof CapturingServletInputStream )
782 {
783 ByteArrayOutputStream byteArrayOutputStream = ( ( CapturingServletInputStream )inputStream ).captureOutputStream;
784 String str = request.toString() + byteArrayOutputStream.toString();
785 BufferedReader reader = new BufferedReader( new StringReader( str ) );
786 ( ( CapturingServletInputStream )inputStream ).captureOutputStream = new ByteArrayOutputStream();
787
788 String line = reader.readLine();
789 while( line != null )
790 {
791 logger.info( ">> \"" + line + "\"" );
792 line = reader.readLine();
793 }
794 }
795 }
796 catch( Throwable e )
797 {
798 SoapUI.logError( e );
799 }
800
801 try
802 {
803 ServletOutputStream outputStream = response.getOutputStream();
804 if( outputStream instanceof CapturingServletOutputStream )
805 {
806 ByteArrayOutputStream byteArrayOutputStream = ( ( CapturingServletOutputStream )outputStream ).captureOutputStream;
807 String str = request.toString() + byteArrayOutputStream.toString();
808 BufferedReader reader = new BufferedReader( new StringReader( str ) );
809 ( ( CapturingServletOutputStream )outputStream ).captureOutputStream = new ByteArrayOutputStream();
810
811 String line = reader.readLine();
812 while( line != null )
813 {
814 logger.info( "<< \"" + line + "\"" );
815 line = reader.readLine();
816 }
817 }
818 }
819 catch( Throwable e )
820 {
821 SoapUI.logError( e );
822 }
823 }
824 }
825 }