1 package org.eclipse.jetty.server.handler;
2
3
4
5
6
7
8
9
10
11
12
13
14
15 import java.io.IOException;
16 import java.net.InetSocketAddress;
17 import java.net.SocketException;
18 import java.net.SocketTimeoutException;
19 import java.nio.channels.ClosedChannelException;
20 import java.nio.channels.SelectionKey;
21 import java.nio.channels.SocketChannel;
22 import java.util.Arrays;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.CountDownLatch;
26 import java.util.concurrent.TimeUnit;
27 import javax.servlet.ServletException;
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
30
31 import org.eclipse.jetty.http.HttpMethods;
32 import org.eclipse.jetty.http.HttpParser;
33 import org.eclipse.jetty.io.AsyncEndPoint;
34 import org.eclipse.jetty.io.Buffer;
35 import org.eclipse.jetty.io.ConnectedEndPoint;
36 import org.eclipse.jetty.io.Connection;
37 import org.eclipse.jetty.io.EndPoint;
38 import org.eclipse.jetty.io.nio.AsyncConnection;
39 import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
40 import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
41 import org.eclipse.jetty.io.nio.SelectorManager;
42 import org.eclipse.jetty.server.AbstractHttpConnection;
43 import org.eclipse.jetty.server.Handler;
44 import org.eclipse.jetty.server.Request;
45 import org.eclipse.jetty.server.Server;
46 import org.eclipse.jetty.util.HostMap;
47 import org.eclipse.jetty.util.TypeUtil;
48 import org.eclipse.jetty.util.component.LifeCycle;
49 import org.eclipse.jetty.util.log.Log;
50 import org.eclipse.jetty.util.log.Logger;
51 import org.eclipse.jetty.util.thread.ThreadPool;
52
53
54
55
56
57
58 public class ConnectHandler extends HandlerWrapper
59 {
60 private static final Logger LOG = Log.getLogger(ConnectHandler.class);
61 private final SelectorManager _selectorManager = new Manager();
62 private volatile int _connectTimeout = 5000;
63 private volatile int _writeTimeout = 30000;
64 private volatile ThreadPool _threadPool;
65 private volatile boolean _privateThreadPool;
66 private HostMap<String> _white = new HostMap<String>();
67 private HostMap<String> _black = new HostMap<String>();
68
69 public ConnectHandler()
70 {
71 this(null);
72 }
73
74 public ConnectHandler(String[] white, String[] black)
75 {
76 this(null, white, black);
77 }
78
79 public ConnectHandler(Handler handler)
80 {
81 setHandler(handler);
82 }
83
84 public ConnectHandler(Handler handler, String[] white, String[] black)
85 {
86 setHandler(handler);
87 set(white, _white);
88 set(black, _black);
89 }
90
91
92
93
94 public int getConnectTimeout()
95 {
96 return _connectTimeout;
97 }
98
99
100
101
102 public void setConnectTimeout(int connectTimeout)
103 {
104 _connectTimeout = connectTimeout;
105 }
106
107
108
109
110 public int getWriteTimeout()
111 {
112 return _writeTimeout;
113 }
114
115
116
117
118 public void setWriteTimeout(int writeTimeout)
119 {
120 _writeTimeout = writeTimeout;
121 }
122
123 @Override
124 public void setServer(Server server)
125 {
126 super.setServer(server);
127
128 server.getContainer().update(this, null, _selectorManager, "selectManager");
129
130 if (_privateThreadPool)
131 server.getContainer().update(this, null, _privateThreadPool, "threadpool", true);
132 else
133 _threadPool = server.getThreadPool();
134 }
135
136
137
138
139 public ThreadPool getThreadPool()
140 {
141 return _threadPool;
142 }
143
144
145
146
147 public void setThreadPool(ThreadPool threadPool)
148 {
149 if (getServer() != null)
150 getServer().getContainer().update(this, _privateThreadPool ? _threadPool : null, threadPool, "threadpool", true);
151 _privateThreadPool = threadPool != null;
152 _threadPool = threadPool;
153 }
154
155 @Override
156 protected void doStart() throws Exception
157 {
158 super.doStart();
159
160 if (_threadPool == null)
161 {
162 _threadPool = getServer().getThreadPool();
163 _privateThreadPool = false;
164 }
165 if (_threadPool instanceof LifeCycle && !((LifeCycle)_threadPool).isRunning())
166 ((LifeCycle)_threadPool).start();
167
168 _selectorManager.start();
169 }
170
171 @Override
172 protected void doStop() throws Exception
173 {
174 _selectorManager.stop();
175
176 ThreadPool threadPool = _threadPool;
177 if (_privateThreadPool && _threadPool != null && threadPool instanceof LifeCycle)
178 ((LifeCycle)threadPool).stop();
179
180 super.doStop();
181 }
182
183 @Override
184 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
185 {
186 if (HttpMethods.CONNECT.equalsIgnoreCase(request.getMethod()))
187 {
188 LOG.debug("CONNECT request for {}", request.getRequestURI());
189 try
190 {
191 handleConnect(baseRequest, request, response, request.getRequestURI());
192 }
193 catch(Exception e)
194 {
195 LOG.warn("ConnectHandler "+baseRequest.getUri()+" "+ e);
196 LOG.debug(e);
197 }
198 }
199 else
200 {
201 super.handle(target, baseRequest, request, response);
202 }
203 }
204
205
206
207
208
209
210
211
212
213
214
215
216
217 protected void handleConnect(Request baseRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress) throws ServletException, IOException
218 {
219 boolean proceed = handleAuthentication(request, response, serverAddress);
220 if (!proceed)
221 return;
222
223 String host = serverAddress;
224 int port = 80;
225 int colon = serverAddress.indexOf(':');
226 if (colon > 0)
227 {
228 host = serverAddress.substring(0, colon);
229 port = Integer.parseInt(serverAddress.substring(colon + 1));
230 }
231
232 if (!validateDestination(host))
233 {
234 LOG.info("ProxyHandler: Forbidden destination " + host);
235 response.setStatus(HttpServletResponse.SC_FORBIDDEN);
236 baseRequest.setHandled(true);
237 return;
238 }
239
240 SocketChannel channel;
241
242 try
243 {
244 channel = connectToServer(request,host,port);
245 }
246 catch (SocketException se)
247 {
248 LOG.info("ConnectHandler: SocketException " + se.getMessage());
249 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
250 baseRequest.setHandled(true);
251 return;
252 }
253 catch (SocketTimeoutException ste)
254 {
255 LOG.info("ConnectHandler: SocketTimeoutException" + ste.getMessage());
256 response.setStatus(HttpServletResponse.SC_GATEWAY_TIMEOUT);
257 baseRequest.setHandled(true);
258 return;
259 }
260 catch (IOException ioe)
261 {
262 LOG.info("ConnectHandler: IOException" + ioe.getMessage());
263 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
264 baseRequest.setHandled(true);
265 return;
266 }
267
268
269
270
271
272
273 AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentConnection();
274 Buffer headerBuffer = ((HttpParser)httpConnection.getParser()).getHeaderBuffer();
275 Buffer bodyBuffer = ((HttpParser)httpConnection.getParser()).getBodyBuffer();
276 int length = headerBuffer == null ? 0 : headerBuffer.length();
277 length += bodyBuffer == null ? 0 : bodyBuffer.length();
278 IndirectNIOBuffer buffer = null;
279 if (length > 0)
280 {
281 buffer = new IndirectNIOBuffer(length);
282 if (headerBuffer != null)
283 {
284 buffer.put(headerBuffer);
285 headerBuffer.clear();
286 }
287 if (bodyBuffer != null)
288 {
289 buffer.put(bodyBuffer);
290 bodyBuffer.clear();
291 }
292 }
293
294 ConcurrentMap<String, Object> context = new ConcurrentHashMap<String, Object>();
295 prepareContext(request, context);
296
297 ClientToProxyConnection clientToProxy = prepareConnections(context, channel, buffer);
298
299
300 response.setStatus(HttpServletResponse.SC_OK);
301
302
303 baseRequest.getConnection().getGenerator().setPersistent(true);
304
305
306 response.getOutputStream().close();
307
308 upgradeConnection(request, response, clientToProxy);
309 }
310
311 private ClientToProxyConnection prepareConnections(ConcurrentMap<String, Object> context, SocketChannel channel, Buffer buffer)
312 {
313 AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentConnection();
314 ProxyToServerConnection proxyToServer = newProxyToServerConnection(context, buffer);
315 ClientToProxyConnection clientToProxy = newClientToProxyConnection(context, channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp());
316 clientToProxy.setConnection(proxyToServer);
317 proxyToServer.setConnection(clientToProxy);
318 return clientToProxy;
319 }
320
321
322
323
324
325
326
327
328
329
330
331
332 protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address) throws ServletException, IOException
333 {
334 return true;
335 }
336
337 protected ClientToProxyConnection newClientToProxyConnection(ConcurrentMap<String, Object> context, SocketChannel channel, EndPoint endPoint, long timeStamp)
338 {
339 return new ClientToProxyConnection(context, channel, endPoint, timeStamp);
340 }
341
342 protected ProxyToServerConnection newProxyToServerConnection(ConcurrentMap<String, Object> context, Buffer buffer)
343 {
344 return new ProxyToServerConnection(context, buffer);
345 }
346
347
348 private SocketChannel connectToServer(HttpServletRequest request, String host, int port) throws IOException
349 {
350 SocketChannel channel = connect(request, host, port);
351 channel.configureBlocking(false);
352 return channel;
353 }
354
355
356
357
358
359
360
361
362
363
364 protected SocketChannel connect(HttpServletRequest request, String host, int port) throws IOException
365 {
366 SocketChannel channel = SocketChannel.open();
367
368 if (channel == null)
369 {
370 throw new IOException("unable to connect to " + host + ":" + port);
371 }
372
373 try
374 {
375
376 LOG.debug("Establishing connection to {}:{}", host, port);
377 channel.socket().setTcpNoDelay(true);
378 channel.socket().connect(new InetSocketAddress(host, port), getConnectTimeout());
379 LOG.debug("Established connection to {}:{}", host, port);
380 return channel;
381 }
382 catch (IOException x)
383 {
384 LOG.debug("Failed to establish connection to " + host + ":" + port, x);
385 try
386 {
387 channel.close();
388 }
389 catch (IOException xx)
390 {
391 LOG.ignore(xx);
392 }
393 throw x;
394 }
395 }
396
397 protected void prepareContext(HttpServletRequest request, ConcurrentMap<String, Object> context)
398 {
399 }
400
401 private void upgradeConnection(HttpServletRequest request, HttpServletResponse response, Connection connection) throws IOException
402 {
403
404
405 request.setAttribute("org.eclipse.jetty.io.Connection", connection);
406 response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
407 LOG.debug("Upgraded connection to {}", connection);
408 }
409
410 private void register(SocketChannel channel, ProxyToServerConnection proxyToServer) throws IOException
411 {
412 _selectorManager.register(channel, proxyToServer);
413 proxyToServer.waitReady(_connectTimeout);
414 }
415
416
417
418
419
420
421
422
423
424
425
426 protected int read(EndPoint endPoint, Buffer buffer, ConcurrentMap<String, Object> context) throws IOException
427 {
428 return endPoint.fill(buffer);
429 }
430
431
432
433
434
435
436
437
438
439
440 protected int write(EndPoint endPoint, Buffer buffer, ConcurrentMap<String, Object> context) throws IOException
441 {
442 if (buffer == null)
443 return 0;
444
445 int length = buffer.length();
446 final StringBuilder debug = LOG.isDebugEnabled()?new StringBuilder():null;
447 int flushed = endPoint.flush(buffer);
448 if (debug!=null)
449 debug.append(flushed);
450
451
452 while (buffer.length()>0 && !endPoint.isOutputShutdown())
453 {
454 if (!endPoint.isBlocking())
455 {
456 boolean ready = endPoint.blockWritable(getWriteTimeout());
457 if (!ready)
458 throw new IOException("Write timeout");
459 }
460 flushed = endPoint.flush(buffer);
461 if (debug!=null)
462 debug.append("+").append(flushed);
463 }
464
465 LOG.debug("Written {}/{} bytes {}", debug, length, endPoint);
466 buffer.compact();
467 return length;
468 }
469
470 private class Manager extends SelectorManager
471 {
472 @Override
473 protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
474 {
475 SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, key, channel.socket().getSoTimeout());
476 endp.setConnection(selectSet.getManager().newConnection(channel,endp, key.attachment()));
477 endp.setMaxIdleTime(_writeTimeout);
478 return endp;
479 }
480
481 @Override
482 public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment)
483 {
484 ProxyToServerConnection proxyToServer = (ProxyToServerConnection)attachment;
485 proxyToServer.setTimeStamp(System.currentTimeMillis());
486 proxyToServer.setEndPoint(endpoint);
487 return proxyToServer;
488 }
489
490 @Override
491 protected void endPointOpened(SelectChannelEndPoint endpoint)
492 {
493 ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment();
494 proxyToServer.ready();
495 }
496
497 @Override
498 public boolean dispatch(Runnable task)
499 {
500 return _threadPool.dispatch(task);
501 }
502
503 @Override
504 protected void endPointClosed(SelectChannelEndPoint endpoint)
505 {
506 }
507
508 @Override
509 protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection)
510 {
511 }
512 }
513
514
515
516 public class ProxyToServerConnection implements AsyncConnection
517 {
518 private final CountDownLatch _ready = new CountDownLatch(1);
519 private final Buffer _buffer = new IndirectNIOBuffer(4096);
520 private final ConcurrentMap<String, Object> _context;
521 private volatile Buffer _data;
522 private volatile ClientToProxyConnection _toClient;
523 private volatile long _timestamp;
524 private volatile AsyncEndPoint _endPoint;
525
526 public ProxyToServerConnection(ConcurrentMap<String, Object> context, Buffer data)
527 {
528 _context = context;
529 _data = data;
530 }
531
532 @Override
533 public String toString()
534 {
535 StringBuilder builder = new StringBuilder("ProxyToServer");
536 builder.append("(:").append(_endPoint.getLocalPort());
537 builder.append("<=>:").append(_endPoint.getRemotePort());
538 return builder.append(")").toString();
539 }
540
541 public Connection handle() throws IOException
542 {
543 LOG.debug("{}: begin reading from server", this);
544 try
545 {
546 writeData();
547
548 while (true)
549 {
550 int read = read(_endPoint, _buffer, _context);
551
552 if (read == -1)
553 {
554 LOG.debug("{}: server closed connection {}", this, _endPoint);
555
556 if (_endPoint.isOutputShutdown() || !_endPoint.isOpen())
557 closeClient();
558 else
559 _toClient.shutdownOutput();
560
561 break;
562 }
563
564 if (read == 0)
565 break;
566
567 LOG.debug("{}: read from server {} bytes {}", this, read, _endPoint);
568 int written = write(_toClient._endPoint, _buffer, _context);
569 LOG.debug("{}: written to {} {} bytes", this, _toClient, written);
570 }
571 return this;
572 }
573 catch (ClosedChannelException x)
574 {
575 LOG.debug(x);
576 throw x;
577 }
578 catch (IOException x)
579 {
580 LOG.warn(this + ": unexpected exception", x);
581 close();
582 throw x;
583 }
584 catch (RuntimeException x)
585 {
586 LOG.warn(this + ": unexpected exception", x);
587 close();
588 throw x;
589 }
590 finally
591 {
592 LOG.debug("{}: end reading from server", this);
593 }
594 }
595
596 public void onInputShutdown() throws IOException
597 {
598
599 }
600
601 private void writeData() throws IOException
602 {
603
604
605
606 synchronized (this)
607 {
608 if (_data != null)
609 {
610 try
611 {
612 int written = write(_endPoint, _data, _context);
613 LOG.debug("{}: written to server {} bytes", this, written);
614 }
615 finally
616 {
617
618
619
620 _data = null;
621 }
622 }
623 }
624 }
625
626 public void setConnection(ClientToProxyConnection connection)
627 {
628 _toClient = connection;
629 }
630
631 public long getTimeStamp()
632 {
633 return _timestamp;
634 }
635
636 public void setTimeStamp(long timestamp)
637 {
638 _timestamp = timestamp;
639 }
640
641 public void setEndPoint(AsyncEndPoint endpoint)
642 {
643 _endPoint = endpoint;
644 }
645
646 public boolean isIdle()
647 {
648 return false;
649 }
650
651 public boolean isSuspended()
652 {
653 return false;
654 }
655
656 public void onClose()
657 {
658 }
659
660 public void ready()
661 {
662 _ready.countDown();
663 }
664
665 public void waitReady(long timeout) throws IOException
666 {
667 try
668 {
669 _ready.await(timeout, TimeUnit.MILLISECONDS);
670 }
671 catch (final InterruptedException x)
672 {
673 throw new IOException()
674 {{
675 initCause(x);
676 }};
677 }
678 }
679
680 public void closeClient() throws IOException
681 {
682 _toClient.closeClient();
683 }
684
685 public void closeServer() throws IOException
686 {
687 _endPoint.close();
688 }
689
690 public void close()
691 {
692 try
693 {
694 closeClient();
695 }
696 catch (IOException x)
697 {
698 LOG.debug(this + ": unexpected exception closing the client", x);
699 }
700
701 try
702 {
703 closeServer();
704 }
705 catch (IOException x)
706 {
707 LOG.debug(this + ": unexpected exception closing the server", x);
708 }
709 }
710
711 public void shutdownOutput() throws IOException
712 {
713 writeData();
714 _endPoint.shutdownOutput();
715 }
716
717 public void onIdleExpired(long idleForMs)
718 {
719 try
720 {
721 shutdownOutput();
722 }
723 catch(Exception e)
724 {
725 LOG.debug(e);
726 close();
727 }
728 }
729 }
730
731 public class ClientToProxyConnection implements AsyncConnection
732 {
733 private final Buffer _buffer = new IndirectNIOBuffer(4096);
734 private final ConcurrentMap<String, Object> _context;
735 private final SocketChannel _channel;
736 private final EndPoint _endPoint;
737 private final long _timestamp;
738 private volatile ProxyToServerConnection _toServer;
739 private boolean _firstTime = true;
740
741 public ClientToProxyConnection(ConcurrentMap<String, Object> context, SocketChannel channel, EndPoint endPoint, long timestamp)
742 {
743 _context = context;
744 _channel = channel;
745 _endPoint = endPoint;
746 _timestamp = timestamp;
747 }
748
749 @Override
750 public String toString()
751 {
752 StringBuilder builder = new StringBuilder("ClientToProxy");
753 builder.append("(:").append(_endPoint.getLocalPort());
754 builder.append("<=>:").append(_endPoint.getRemotePort());
755 return builder.append(")").toString();
756 }
757
758 public Connection handle() throws IOException
759 {
760 LOG.debug("{}: begin reading from client", this);
761 try
762 {
763 if (_firstTime)
764 {
765 _firstTime = false;
766 register(_channel, _toServer);
767 LOG.debug("{}: registered channel {} with connection {}", this, _channel, _toServer);
768 }
769
770 while (true)
771 {
772 int read = read(_endPoint, _buffer, _context);
773
774 if (read == -1)
775 {
776 LOG.debug("{}: client closed connection {}", this, _endPoint);
777
778 if (_endPoint.isOutputShutdown() || !_endPoint.isOpen())
779 closeServer();
780 else
781 _toServer.shutdownOutput();
782
783 break;
784 }
785
786 if (read == 0)
787 break;
788
789 LOG.debug("{}: read from client {} bytes {}", this, read, _endPoint);
790 int written = write(_toServer._endPoint, _buffer, _context);
791 LOG.debug("{}: written to {} {} bytes", this, _toServer, written);
792 }
793 return this;
794 }
795 catch (ClosedChannelException x)
796 {
797 LOG.debug(x);
798 closeServer();
799 throw x;
800 }
801 catch (IOException x)
802 {
803 LOG.warn(this + ": unexpected exception", x);
804 close();
805 throw x;
806 }
807 catch (RuntimeException x)
808 {
809 LOG.warn(this + ": unexpected exception", x);
810 close();
811 throw x;
812 }
813 finally
814 {
815 LOG.debug("{}: end reading from client", this);
816 }
817 }
818
819 public void onInputShutdown() throws IOException
820 {
821
822 }
823
824 public long getTimeStamp()
825 {
826 return _timestamp;
827 }
828
829 public boolean isIdle()
830 {
831 return false;
832 }
833
834 public boolean isSuspended()
835 {
836 return false;
837 }
838
839 public void onClose()
840 {
841 }
842
843 public void setConnection(ProxyToServerConnection connection)
844 {
845 _toServer = connection;
846 }
847
848 public void closeClient() throws IOException
849 {
850 _endPoint.close();
851 }
852
853 public void closeServer() throws IOException
854 {
855 _toServer.closeServer();
856 }
857
858 public void close()
859 {
860 try
861 {
862 closeClient();
863 }
864 catch (IOException x)
865 {
866 LOG.debug(this + ": unexpected exception closing the client", x);
867 }
868
869 try
870 {
871 closeServer();
872 }
873 catch (IOException x)
874 {
875 LOG.debug(this + ": unexpected exception closing the server", x);
876 }
877 }
878
879 public void shutdownOutput() throws IOException
880 {
881 _endPoint.shutdownOutput();
882 }
883
884 public void onIdleExpired(long idleForMs)
885 {
886 try
887 {
888 shutdownOutput();
889 }
890 catch(Exception e)
891 {
892 LOG.debug(e);
893 close();
894 }
895 }
896 }
897
898
899
900
901
902
903 public void addWhite(String entry)
904 {
905 add(entry, _white);
906 }
907
908
909
910
911
912
913 public void addBlack(String entry)
914 {
915 add(entry, _black);
916 }
917
918
919
920
921
922
923 public void setWhite(String[] entries)
924 {
925 set(entries, _white);
926 }
927
928
929
930
931
932
933 public void setBlack(String[] entries)
934 {
935 set(entries, _black);
936 }
937
938
939
940
941
942
943
944
945 protected void set(String[] entries, HostMap<String> hostMap)
946 {
947 hostMap.clear();
948
949 if (entries != null && entries.length > 0)
950 {
951 for (String addrPath : entries)
952 {
953 add(addrPath, hostMap);
954 }
955 }
956 }
957
958
959
960
961
962
963
964
965 private void add(String entry, HostMap<String> hostMap)
966 {
967 if (entry != null && entry.length() > 0)
968 {
969 entry = entry.trim();
970 if (hostMap.get(entry) == null)
971 {
972 hostMap.put(entry, entry);
973 }
974 }
975 }
976
977
978
979
980
981
982
983 public boolean validateDestination(String host)
984 {
985 if (_white.size() > 0)
986 {
987 Object whiteObj = _white.getLazyMatches(host);
988 if (whiteObj == null)
989 {
990 return false;
991 }
992 }
993
994 if (_black.size() > 0)
995 {
996 Object blackObj = _black.getLazyMatches(host);
997 if (blackObj != null)
998 {
999 return false;
1000 }
1001 }
1002
1003 return true;
1004 }
1005
1006 @Override
1007 public void dump(Appendable out, String indent) throws IOException
1008 {
1009 dumpThis(out);
1010 if (_privateThreadPool)
1011 dump(out, indent, Arrays.asList(_threadPool, _selectorManager), TypeUtil.asList(getHandlers()), getBeans());
1012 else
1013 dump(out, indent, Arrays.asList(_selectorManager), TypeUtil.asList(getHandlers()), getBeans());
1014 }
1015 }