├── .gitignore ├── .travis.yml ├── README.md ├── jersey-netty-example ├── pom.xml └── src │ └── main │ └── java │ └── net │ └── javaforge │ └── netty │ ├── App.java │ └── jersey │ ├── HelloWorldResource.java │ └── MyApplication.java ├── netty-servlet-bridge ├── LICENSE.txt ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── net │ └── javaforge │ └── netty │ └── servlet │ └── bridge │ ├── ChannelThreadLocal.java │ ├── HttpSessionThreadLocal.java │ ├── ServletBridgeChannelPipelineFactory.java │ ├── ServletBridgeException.java │ ├── ServletBridgeHandler.java │ ├── ServletBridgeInterceptor.java │ ├── ServletBridgeRuntimeException.java │ ├── config │ ├── FilterConfiguration.java │ ├── HttpComponentConfigurationAdapter.java │ ├── ServletConfiguration.java │ ├── ServletContextListenerConfiguration.java │ └── WebappConfiguration.java │ ├── impl │ ├── ConfigAdapter.java │ ├── FilterChainImpl.java │ ├── FilterConfigImpl.java │ ├── HttpServletRequestImpl.java │ ├── HttpServletResponseImpl.java │ ├── HttpSessionImpl.java │ ├── PrintWriterImpl.java │ ├── RequestDispatcherImpl.java │ ├── ServletBridgeWebapp.java │ ├── ServletConfigImpl.java │ ├── ServletContextImpl.java │ ├── ServletInputStreamImpl.java │ ├── ServletOutputStreamImpl.java │ └── URIParser.java │ ├── interceptor │ ├── ChannelInterceptor.java │ └── HttpSessionInterceptor.java │ ├── session │ ├── DefaultServletBridgeHttpSessionStore.java │ └── ServletBridgeHttpSessionStore.java │ └── util │ └── Utils.java └── vaadin-netty-example ├── pom.xml └── src └── main └── java └── net └── javaforge └── netty ├── App.java └── vaadin └── AddressbookUI.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.ipr 3 | *.iws 4 | .idea 5 | target/ 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | before_install: cd netty-servlet-bridge 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project provides a Servlet API implementation for Netty.IO framework (http://netty.io/). 2 | 3 | Netty Servlet Bridge allows integration of existing Servlet API based web-applications 4 | into the Netty-backed infrastructure. 5 | 6 | Simple usage example: 7 | 8 | 9 | ```java 10 | // Configure netty server. 11 | final ServerBootstrap bootstrap = new ServerBootstrap( 12 | new NioServerSocketChannelFactory( 13 | Executors.newCachedThreadPool(), Executors.newCachedThreadPool() 14 | ) 15 | ); 16 | 17 | // configure web-app 18 | WebappConfiguration webapp = new WebappConfiguration() 19 | .addContextParameter("name1", "value1") 20 | .addContextParameter("name2", "value2") 21 | .addServletConfigurations( 22 | new ServletConfiguration(MyHttpServlet.class, "/servlet1"), 23 | new ServletConfiguration(MyAnotherHttpServlet.class, "/servlet2"), 24 | .addInitParameter("initpram1", "value1") 25 | .addInitParameter("initpram2", "value2") 26 | 27 | ) 28 | .addFilterConfigurations ( 29 | new FilterConfiguration(MyServletFilter.class, "/*"), 30 | ); 31 | 32 | 33 | // Set up the event pipeline factory. 34 | bootstrap.setPipelineFactory(new ServletBridgeChannelPipelineFactory(webapp)); 35 | 36 | // Bind and start to accept incoming connections. 37 | final Channel serverChannel = bootstrap.bind(new InetSocketAddress(8080)); 38 | 39 | ``` 40 | 41 | Please consult example projects demonstrating the library usage. 42 | 43 | Jersey Integration Example: https://github.com/bigpuritz/netty-servlet-bridge/tree/master/jersey-netty-example 44 | Vaadin Integration Example: https://github.com/bigpuritz/netty-servlet-bridge/tree/master/vaadin-netty-example 45 | -------------------------------------------------------------------------------- /jersey-netty-example/pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 4.0.0 20 | 21 | net.javaforge.netty 22 | jersey-netty-example 23 | 1.0-SNAPSHOT 24 | jar 25 | 26 | jersey-example 27 | http://maven.apache.org 28 | 29 | 30 | UTF-8 31 | 32 | 33 | 34 | 35 | 36 | io.netty 37 | netty 38 | 3.6.2.Final 39 | 40 | 41 | net.javaforge.netty 42 | netty-servlet-bridge 43 | 1.0.0-SNAPSHOT 44 | 45 | 46 | com.sun.jersey 47 | jersey-servlet 48 | 1.17 49 | 50 | 51 | com.sun.jersey 52 | jersey-json 53 | 1.17 54 | 55 | 56 | org.slf4j 57 | slf4j-simple 58 | 1.7.2 59 | 60 | 61 | junit 62 | junit 63 | 4.11 64 | test 65 | 66 | 67 | 68 | 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-eclipse-plugin 73 | 2.9 74 | 75 | true 76 | true 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /jersey-netty-example/src/main/java/net/javaforge/netty/App.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty; 18 | 19 | import com.sun.jersey.spi.container.servlet.ServletContainer; 20 | import net.javaforge.netty.jersey.MyApplication; 21 | import net.javaforge.netty.servlet.bridge.ServletBridgeChannelPipelineFactory; 22 | import net.javaforge.netty.servlet.bridge.config.ServletConfiguration; 23 | import net.javaforge.netty.servlet.bridge.config.WebappConfiguration; 24 | import org.jboss.netty.bootstrap.ServerBootstrap; 25 | import org.jboss.netty.channel.Channel; 26 | import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | import java.net.InetSocketAddress; 31 | import java.util.concurrent.Executors; 32 | 33 | /** 34 | * Hello world! 35 | */ 36 | public class App { 37 | 38 | private static final Logger log = LoggerFactory.getLogger(App.class); 39 | 40 | public static void main(String[] args) { 41 | 42 | long start = System.currentTimeMillis(); 43 | 44 | // Configure the server. 45 | final ServerBootstrap bootstrap = new ServerBootstrap( 46 | new NioServerSocketChannelFactory(Executors 47 | .newCachedThreadPool(), Executors.newCachedThreadPool())); 48 | 49 | WebappConfiguration webapp = new WebappConfiguration() 50 | .addServletConfigurations(new ServletConfiguration( 51 | ServletContainer.class, "/*").addInitParameter( 52 | "javax.ws.rs.Application", MyApplication.class 53 | .getName())); 54 | 55 | // Set up the event pipeline factory. 56 | final ServletBridgeChannelPipelineFactory servletBridge = new ServletBridgeChannelPipelineFactory( 57 | webapp); 58 | bootstrap.setPipelineFactory(servletBridge); 59 | 60 | // Bind and start to accept incoming connections. 61 | final Channel serverChannel = bootstrap 62 | .bind(new InetSocketAddress(8080)); 63 | 64 | long end = System.currentTimeMillis(); 65 | 66 | log.info(">>> Server started in {} ms .... <<< ", (end - start)); 67 | 68 | Runtime.getRuntime().addShutdownHook(new Thread() { 69 | 70 | @Override 71 | public void run() { 72 | servletBridge.shutdown(); 73 | serverChannel.close().awaitUninterruptibly(); 74 | bootstrap.releaseExternalResources(); 75 | } 76 | }); 77 | 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /jersey-netty-example/src/main/java/net/javaforge/netty/jersey/HelloWorldResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.jersey; 18 | 19 | import javax.ws.rs.GET; 20 | import javax.ws.rs.Path; 21 | import javax.ws.rs.Produces; 22 | 23 | //The Java class will be hosted at the URI path "/helloworld" 24 | @Path("/helloworld") 25 | public class HelloWorldResource { 26 | 27 | // The Java method will process HTTP GET requests 28 | @GET 29 | // The Java method will produce content identified by the MIME Media 30 | // type "text/plain" 31 | @Produces("text/plain") 32 | public String getClichedMessage() { 33 | // Return some cliched textual content 34 | return "Hello World"; 35 | } 36 | } -------------------------------------------------------------------------------- /jersey-netty-example/src/main/java/net/javaforge/netty/jersey/MyApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.jersey; 18 | 19 | import javax.ws.rs.ApplicationPath; 20 | import javax.ws.rs.core.Application; 21 | import java.util.HashSet; 22 | import java.util.Set; 23 | 24 | @ApplicationPath("/") 25 | public class MyApplication extends Application { 26 | 27 | @Override 28 | public Set> getClasses() { 29 | final Set> classes = new HashSet>(); 30 | // register root resource 31 | classes.add(HelloWorldResource.class); 32 | return classes; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /netty-servlet-bridge/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | -------------------------------------------------------------------------------- /netty-servlet-bridge/README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/bigpuritz/netty-servlet-bridge.svg?branch=master)](https://travis-ci.org/bigpuritz/netty-servlet-bridge) 2 | 3 | -------------------------------------------------------------------------------- /netty-servlet-bridge/pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 4.0.0 20 | net.javaforge.netty 21 | netty-servlet-bridge 22 | jar 23 | 2.0.0-SNAPSHOT 24 | netty-servlet-bridge 25 | http://maven.apache.org 26 | 27 | 28 | 29 | org.slf4j 30 | slf4j-api 31 | 1.7.2 32 | 33 | 34 | io.netty 35 | netty-all 36 | 4.1.0.Beta3 37 | 38 | 39 | javax.servlet 40 | servlet-api 41 | 2.5 42 | 43 | 44 | org.slf4j 45 | slf4j-simple 46 | 1.7.2 47 | test 48 | 49 | 50 | junit 51 | junit 52 | 4.11 53 | test 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-eclipse-plugin 62 | 2.9 63 | 64 | true 65 | true 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-compiler-plugin 71 | 72 | 1.6 73 | 1.6 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/ChannelThreadLocal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge; 18 | 19 | import io.netty.channel.Channel; 20 | 21 | public class ChannelThreadLocal { 22 | 23 | public static final ThreadLocal channelThreadLocal = new ThreadLocal(); 24 | 25 | public static void set(Channel channel) { 26 | channelThreadLocal.set(channel); 27 | } 28 | 29 | public static void unset() { 30 | channelThreadLocal.remove(); 31 | } 32 | 33 | public static Channel get() { 34 | return channelThreadLocal.get(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/HttpSessionThreadLocal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge; 18 | 19 | import net.javaforge.netty.servlet.bridge.impl.HttpSessionImpl; 20 | import net.javaforge.netty.servlet.bridge.impl.ServletBridgeWebapp; 21 | import net.javaforge.netty.servlet.bridge.session.DefaultServletBridgeHttpSessionStore; 22 | import net.javaforge.netty.servlet.bridge.session.ServletBridgeHttpSessionStore; 23 | 24 | public class HttpSessionThreadLocal { 25 | 26 | public static final ThreadLocal sessionThreadLocal = new ThreadLocal(); 27 | 28 | private static ServletBridgeHttpSessionStore sessionStore; 29 | 30 | public static ServletBridgeHttpSessionStore getSessionStore() { 31 | return sessionStore; 32 | } 33 | 34 | public static void setSessionStore(ServletBridgeHttpSessionStore store) { 35 | sessionStore = store; 36 | } 37 | 38 | public static void set(HttpSessionImpl session) { 39 | sessionThreadLocal.set(session); 40 | } 41 | 42 | public static void unset() { 43 | sessionThreadLocal.remove(); 44 | } 45 | 46 | public static HttpSessionImpl get() { 47 | HttpSessionImpl session = sessionThreadLocal.get(); 48 | if (session != null) 49 | session.touch(); 50 | return session; 51 | } 52 | 53 | public static HttpSessionImpl getOrCreate() { 54 | if (HttpSessionThreadLocal.get() == null) { 55 | if (sessionStore == null) { 56 | sessionStore = new DefaultServletBridgeHttpSessionStore(); 57 | } 58 | 59 | HttpSessionImpl newSession = sessionStore.createSession(); 60 | newSession.setMaxInactiveInterval(ServletBridgeWebapp.get() 61 | .getWebappConfig().getSessionTimeout()); 62 | sessionThreadLocal.set(sessionStore.createSession()); 63 | } 64 | return get(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/ServletBridgeChannelPipelineFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge; 18 | 19 | import io.netty.channel.ChannelHandler; 20 | import io.netty.channel.ChannelPipeline; 21 | import io.netty.channel.group.ChannelGroup; 22 | import io.netty.channel.group.DefaultChannelGroup; 23 | import io.netty.handler.codec.http.HttpContentCompressor; 24 | import io.netty.handler.codec.http.HttpRequestDecoder; 25 | import io.netty.handler.codec.http.HttpResponseEncoder; 26 | import io.netty.handler.timeout.IdleStateHandler; 27 | import io.netty.util.HashedWheelTimer; 28 | import io.netty.util.Timer; 29 | import io.netty.util.concurrent.DefaultEventExecutor; 30 | import net.javaforge.netty.servlet.bridge.config.WebappConfiguration; 31 | import net.javaforge.netty.servlet.bridge.impl.ServletBridgeWebapp; 32 | import net.javaforge.netty.servlet.bridge.interceptor.ChannelInterceptor; 33 | import net.javaforge.netty.servlet.bridge.interceptor.HttpSessionInterceptor; 34 | import net.javaforge.netty.servlet.bridge.session.DefaultServletBridgeHttpSessionStore; 35 | import net.javaforge.netty.servlet.bridge.session.ServletBridgeHttpSessionStore; 36 | 37 | //import io.netty.channel.ChannelPipelineFactory; 38 | //import io.netty.handler.codec.http.HttpChunkAggregator; 39 | 40 | //TODO Just fix the compilation error. not right implementation 41 | public class ServletBridgeChannelPipelineFactory { 42 | // implements 43 | // ChannelPipelineFactory { 44 | 45 | private DefaultEventExecutor eventExecutor = new DefaultEventExecutor(); 46 | 47 | private ChannelGroup allChannels = new DefaultChannelGroup(eventExecutor); 48 | 49 | private HttpSessionWatchdog watchdog; 50 | 51 | private final ChannelHandler idleStateHandler; 52 | 53 | private Timer timer; 54 | 55 | public ServletBridgeChannelPipelineFactory(WebappConfiguration config) { 56 | 57 | this.timer = new HashedWheelTimer(); 58 | this.idleStateHandler = new IdleStateHandler(60, 30, 0); // timer 59 | // must 60 | // be 61 | // shared. 62 | 63 | ServletBridgeWebapp webapp = ServletBridgeWebapp.get(); 64 | webapp.init(config, allChannels); 65 | 66 | new Thread(this.watchdog = new HttpSessionWatchdog()).start(); 67 | } 68 | 69 | public void shutdown() { 70 | this.watchdog.stopWatching(); 71 | ServletBridgeWebapp.get().destroy(); 72 | this.timer.stop(); 73 | this.allChannels.close().awaitUninterruptibly(); 74 | } 75 | 76 | // @Override 77 | public final ChannelPipeline pipeline() { 78 | ChannelPipeline pipeline = getDefaulHttpChannelPipeline(); 79 | pipeline.addLast("handler", getServletBridgeHandler()); 80 | return pipeline; 81 | } 82 | 83 | protected ServletBridgeHttpSessionStore getHttpSessionStore() { 84 | return new DefaultServletBridgeHttpSessionStore(); 85 | } 86 | 87 | protected ServletBridgeHandler getServletBridgeHandler() { 88 | 89 | ServletBridgeHandler bridge = new ServletBridgeHandler(); 90 | bridge.addInterceptor(new ChannelInterceptor()); 91 | bridge.addInterceptor(new HttpSessionInterceptor( 92 | getHttpSessionStore())); 93 | return bridge; 94 | } 95 | 96 | protected ChannelPipeline getDefaulHttpChannelPipeline() { 97 | 98 | // Create a default pipeline implementation. 99 | ChannelPipeline pipeline = pipeline(); 100 | 101 | pipeline.addLast("decoder", new HttpRequestDecoder()); 102 | // pipeline.addLast("aggregator", new HttpChunkAggregator(1048576)); 103 | pipeline.addLast("encoder", new HttpResponseEncoder()); 104 | 105 | // Remove the following line if you don't want automatic content 106 | // compression. 107 | pipeline.addLast("deflater", new HttpContentCompressor()); 108 | pipeline.addLast("idle", this.idleStateHandler); 109 | 110 | return pipeline; 111 | } 112 | 113 | private class HttpSessionWatchdog implements Runnable { 114 | 115 | private boolean shouldStopWatching = false; 116 | 117 | @Override 118 | public void run() { 119 | 120 | while (!shouldStopWatching) { 121 | 122 | try { 123 | ServletBridgeHttpSessionStore store = getHttpSessionStore(); 124 | if (store != null) { 125 | store.destroyInactiveSessions(); 126 | } 127 | Thread.sleep(5000); 128 | 129 | } catch (InterruptedException e) { 130 | return; 131 | } 132 | 133 | } 134 | 135 | } 136 | 137 | public void stopWatching() { 138 | this.shouldStopWatching = true; 139 | } 140 | 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/ServletBridgeException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge; 18 | 19 | public class ServletBridgeException extends Exception { 20 | 21 | private static final long serialVersionUID = 1L; 22 | 23 | public ServletBridgeException(String message, Throwable cause) { 24 | super(message, cause); 25 | } 26 | 27 | public ServletBridgeException(String message) { 28 | super(message); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/ServletBridgeHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge; 18 | 19 | import io.netty.buffer.ByteBuf; 20 | import io.netty.buffer.Unpooled; 21 | import io.netty.channel.*; 22 | import io.netty.handler.codec.TooLongFrameException; 23 | import io.netty.handler.codec.http.*; 24 | import io.netty.handler.ssl.SslHandler; 25 | import io.netty.handler.stream.ChunkedFile; 26 | import io.netty.handler.timeout.IdleStateEvent; 27 | import io.netty.handler.timeout.IdleStateHandler; 28 | import net.javaforge.netty.servlet.bridge.impl.FilterChainImpl; 29 | import net.javaforge.netty.servlet.bridge.impl.HttpServletRequestImpl; 30 | import net.javaforge.netty.servlet.bridge.impl.HttpServletResponseImpl; 31 | import net.javaforge.netty.servlet.bridge.impl.ServletBridgeWebapp; 32 | import net.javaforge.netty.servlet.bridge.util.Utils; 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | 36 | import java.io.File; 37 | import java.io.FileNotFoundException; 38 | import java.io.RandomAccessFile; 39 | import java.io.UnsupportedEncodingException; 40 | import java.util.ArrayList; 41 | import java.util.List; 42 | 43 | import static io.netty.handler.codec.http.HttpHeaders.Names.*; 44 | import static io.netty.handler.codec.http.HttpHeaders.setContentLength; 45 | import static io.netty.handler.codec.http.HttpMethod.GET; 46 | import static io.netty.handler.codec.http.HttpResponseStatus.*; 47 | import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; 48 | 49 | @ChannelHandler.Sharable 50 | public class ServletBridgeHandler extends IdleStateHandler { 51 | 52 | 53 | private static final Logger log = LoggerFactory 54 | .getLogger(ServletBridgeHandler.class); 55 | 56 | private List interceptors; 57 | 58 | 59 | /** 60 | * Which uri should be passed into this servlet container 61 | */ 62 | private String uriPrefix = "/"; 63 | 64 | public ServletBridgeHandler() { 65 | this("/"); 66 | } 67 | 68 | public ServletBridgeHandler(String uriPrefix) { 69 | super(20000, 20000, 20000); 70 | this.uriPrefix = uriPrefix; 71 | } 72 | 73 | public ServletBridgeHandler addInterceptor( 74 | ServletBridgeInterceptor interceptor) { 75 | 76 | if (interceptors == null) 77 | interceptors = new ArrayList(); 78 | 79 | interceptors.add(interceptor); 80 | return this; 81 | } 82 | 83 | @Override 84 | public void channelActive(ChannelHandlerContext ctx) 85 | throws Exception { 86 | log.debug("Opening new channel: {}", ctx.channel().id()); 87 | ServletBridgeWebapp.get().getSharedChannelGroup().add(ctx.channel()); 88 | 89 | ctx.fireChannelActive(); 90 | } 91 | 92 | @Override 93 | public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) { 94 | log.debug("Closing idle channel: {}", ctx.channel().id()); 95 | ctx.channel().close(); 96 | } 97 | 98 | @Override 99 | public void channelRead(ChannelHandlerContext ctx, Object e) 100 | throws Exception { 101 | 102 | if (e instanceof HttpRequest) { 103 | HttpRequest request = (HttpRequest) e; 104 | 105 | String uri = request.uri(); 106 | 107 | if (uri.startsWith(uriPrefix)) { 108 | if (HttpHeaders.is100ContinueExpected(request)) { 109 | ctx.channel().write(new DefaultHttpResponse(HTTP_1_1, CONTINUE)); 110 | } 111 | 112 | 113 | FilterChainImpl chain = ServletBridgeWebapp.get().initializeChain(uri); 114 | 115 | if (chain.isValid()) { 116 | handleHttpServletRequest(ctx, request, chain); 117 | } else if (ServletBridgeWebapp.get().getStaticResourcesFolder() != null) { 118 | handleStaticResourceRequest(ctx, request); 119 | } else { 120 | throw new ServletBridgeRuntimeException( 121 | "No handler found for uri: " + request.getUri()); 122 | } 123 | } else { 124 | ctx.fireChannelRead(e); 125 | } 126 | } else { 127 | ctx.fireChannelRead(e); 128 | } 129 | } 130 | 131 | protected void handleHttpServletRequest(ChannelHandlerContext ctx, 132 | HttpRequest request, FilterChainImpl chain) throws Exception { 133 | 134 | interceptOnRequestReceived(ctx, request); 135 | 136 | DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK); 137 | 138 | HttpServletResponseImpl resp = buildHttpServletResponse(response); 139 | HttpServletRequestImpl req = buildHttpServletRequest(request, chain); 140 | 141 | 142 | chain.doFilter(req, resp); 143 | 144 | interceptOnRequestSuccessed(ctx, request, response); 145 | 146 | resp.getWriter().flush(); 147 | 148 | boolean keepAlive = HttpHeaders.isKeepAlive(request); 149 | 150 | if (keepAlive) { 151 | 152 | // Add 'Content-Length' header only for a keep-alive connection. 153 | response.headers().set(CONTENT_LENGTH, response.content().readableBytes()); 154 | // Add keep alive header as per: 155 | // - 156 | // http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection 157 | response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); 158 | } 159 | 160 | // write response... 161 | ChannelFuture future = ctx.channel().writeAndFlush(response); 162 | 163 | if (!keepAlive) { 164 | future.addListener(ChannelFutureListener.CLOSE); 165 | } 166 | 167 | } 168 | 169 | protected void handleStaticResourceRequest(ChannelHandlerContext ctx, 170 | HttpRequest request) throws Exception { 171 | if (request.method() != GET) { 172 | sendError(ctx, METHOD_NOT_ALLOWED); 173 | return; 174 | } 175 | 176 | String uri = Utils.sanitizeUri(request.uri()); 177 | final String path = (uri != null ? ServletBridgeWebapp.get() 178 | .getStaticResourcesFolder().getAbsolutePath() 179 | + File.separator + uri : null); 180 | 181 | if (path == null) { 182 | sendError(ctx, FORBIDDEN); 183 | return; 184 | } 185 | 186 | File file = new File(path); 187 | if (file.isHidden() || !file.exists()) { 188 | sendError(ctx, NOT_FOUND); 189 | return; 190 | } 191 | if (!file.isFile()) { 192 | sendError(ctx, FORBIDDEN); 193 | return; 194 | } 195 | 196 | RandomAccessFile raf; 197 | try { 198 | raf = new RandomAccessFile(file, "r"); 199 | } catch (FileNotFoundException fnfe) { 200 | sendError(ctx, NOT_FOUND); 201 | return; 202 | } 203 | 204 | long fileLength = raf.length(); 205 | 206 | HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); 207 | setContentLength(response, fileLength); 208 | 209 | Channel ch = ctx.channel(); 210 | 211 | // Write the initial line and the header. 212 | ch.write(response); 213 | 214 | // Write the content. 215 | ChannelFuture writeFuture; 216 | if (isSslChannel(ch)) { 217 | // Cannot use zero-copy with HTTPS. 218 | writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192)); 219 | } else { 220 | // No encryption - use zero-copy. 221 | final FileRegion region = new DefaultFileRegion(raf.getChannel(), 222 | 0, fileLength); 223 | writeFuture = ch.write(region); 224 | writeFuture.addListener(new ChannelProgressiveFutureListener() { 225 | 226 | @Override 227 | public void operationProgressed(ChannelProgressiveFuture channelProgressiveFuture, long current, long total) throws Exception { 228 | System.out.printf("%s: %d / %d (+%d)%n", path, current, 229 | total, total); 230 | } 231 | 232 | @Override 233 | public void operationComplete(ChannelProgressiveFuture channelProgressiveFuture) throws Exception { 234 | region.release(); 235 | } 236 | }); 237 | } 238 | 239 | } 240 | 241 | @Override 242 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 243 | log.error("Unexpected exception from downstream.", cause); 244 | 245 | Channel ch = ctx.channel(); 246 | if (cause instanceof IllegalArgumentException) { 247 | ch.close(); 248 | } else { 249 | if (cause instanceof TooLongFrameException) { 250 | sendError(ctx, BAD_REQUEST); 251 | return; 252 | } 253 | 254 | if (ch.isActive()) { 255 | sendError(ctx, INTERNAL_SERVER_ERROR); 256 | } 257 | 258 | } 259 | 260 | } 261 | 262 | private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { 263 | String text = "Failure: " + status.toString() + "\r\n"; 264 | ByteBuf byteBuf = Unpooled.buffer(); 265 | byte[] bytes = null; 266 | try { 267 | bytes = text.getBytes("utf-8"); 268 | byteBuf.writeBytes(bytes); 269 | } catch (UnsupportedEncodingException e) { 270 | } 271 | 272 | FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, byteBuf); 273 | HttpHeaders headers = response.headers(); 274 | 275 | headers.add(CONTENT_TYPE, "text/plain;charset=utf-8"); 276 | headers.add(CACHE_CONTROL, "no-cache"); 277 | headers.add(PRAGMA, "No-cache"); 278 | headers.add(SERVER, "eBay Server"); 279 | headers.add(CONTENT_LENGTH, byteBuf.readableBytes()); 280 | ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); 281 | } 282 | 283 | private void interceptOnRequestReceived(ChannelHandlerContext ctx, 284 | HttpRequest request) { 285 | if (interceptors != null) { 286 | for (ServletBridgeInterceptor interceptor : interceptors) { 287 | interceptor.onRequestReceived(ctx, request); 288 | } 289 | } 290 | 291 | } 292 | 293 | private void interceptOnRequestSuccessed(ChannelHandlerContext ctx, 294 | HttpRequest request, HttpResponse response) { 295 | if (interceptors != null) { 296 | for (ServletBridgeInterceptor interceptor : interceptors) { 297 | interceptor.onRequestSuccessed(ctx, request, response); 298 | } 299 | } 300 | 301 | } 302 | 303 | // private void interceptOnRequestFailed(ChannelHandlerContext ctx, 304 | // ExceptionEvent e, HttpResponse response) { 305 | // if (this.interceptors != null) { 306 | // for (ServletBridgeInterceptor interceptor : this.interceptors) { 307 | // interceptor.onRequestFailed(ctx, e, response); 308 | // } 309 | // } 310 | // 311 | // } 312 | 313 | protected HttpServletResponseImpl buildHttpServletResponse( 314 | FullHttpResponse response) { 315 | return new HttpServletResponseImpl(response); 316 | } 317 | 318 | protected HttpServletRequestImpl buildHttpServletRequest( 319 | HttpRequest request, FilterChainImpl chain) { 320 | return new HttpServletRequestImpl(request, chain); 321 | } 322 | 323 | private boolean isSslChannel(Channel ch) { 324 | return ch.pipeline().get(SslHandler.class) != null; 325 | } 326 | 327 | public String getUriPrefix() { 328 | return uriPrefix; 329 | } 330 | 331 | public void setUriPrefix(String uriPrefix) { 332 | this.uriPrefix = uriPrefix; 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/ServletBridgeInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge; 18 | 19 | import io.netty.channel.ChannelHandlerContext; 20 | import io.netty.handler.codec.http.HttpRequest; 21 | import io.netty.handler.codec.http.HttpResponse; 22 | 23 | public interface ServletBridgeInterceptor { 24 | 25 | void onRequestReceived(ChannelHandlerContext ctx, HttpRequest e); 26 | 27 | void onRequestSuccessed(ChannelHandlerContext ctx, HttpRequest e, 28 | HttpResponse response); 29 | 30 | void onRequestFailed(ChannelHandlerContext ctx, Throwable e, 31 | HttpResponse response); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/ServletBridgeRuntimeException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge; 18 | 19 | public class ServletBridgeRuntimeException extends RuntimeException { 20 | 21 | private static final long serialVersionUID = 1L; 22 | 23 | public ServletBridgeRuntimeException(String message, Throwable cause) { 24 | super(message, cause); 25 | } 26 | 27 | public ServletBridgeRuntimeException(String message) { 28 | super(message); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/config/FilterConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.config; 18 | 19 | import net.javaforge.netty.servlet.bridge.impl.FilterConfigImpl; 20 | 21 | import javax.servlet.Filter; 22 | import javax.servlet.ServletException; 23 | 24 | public class FilterConfiguration extends 25 | HttpComponentConfigurationAdapter { 26 | 27 | public FilterConfiguration(Class servletClazz, 28 | String... urlPatterns) { 29 | super(servletClazz, urlPatterns); 30 | } 31 | 32 | public FilterConfiguration(Class componentClazz) { 33 | super(componentClazz); 34 | } 35 | 36 | public FilterConfiguration(Filter component, String... urlPatterns) { 37 | super(component, urlPatterns); 38 | } 39 | 40 | public FilterConfiguration(Filter servlet) { 41 | super(servlet); 42 | } 43 | 44 | @Override 45 | protected void doInit() throws ServletException { 46 | this.component.init(this.config); 47 | } 48 | 49 | @Override 50 | protected void doDestroy() throws ServletException { 51 | this.component.destroy(); 52 | } 53 | 54 | @Override 55 | protected FilterConfigImpl newConfigInstance( 56 | Class componentClazz) { 57 | return new FilterConfigImpl(componentClazz.getName()); 58 | } 59 | 60 | public FilterConfiguration addInitParameter(String name, String value) { 61 | super.addConfigInitParameter(name, value); 62 | return this; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/config/HttpComponentConfigurationAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.config; 18 | 19 | import net.javaforge.netty.servlet.bridge.impl.ConfigAdapter; 20 | import net.javaforge.netty.servlet.bridge.util.Utils; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import javax.servlet.ServletException; 25 | import java.util.regex.Pattern; 26 | 27 | public abstract class HttpComponentConfigurationAdapter { 28 | 29 | private static final Logger log = LoggerFactory 30 | .getLogger(HttpComponentConfigurationAdapter.class); 31 | 32 | private static final String DEFAULT_URL_PATTERN = "/*"; 33 | 34 | private String[] sanitizedUrlPatterns; 35 | 36 | private Pattern[] regexPatterns; 37 | 38 | protected COMP component; 39 | 40 | protected CONFIG config; 41 | 42 | private boolean initialized = false; 43 | 44 | public HttpComponentConfigurationAdapter( 45 | Class componentClazz) { 46 | this(Utils.newInstance(componentClazz), DEFAULT_URL_PATTERN); 47 | } 48 | 49 | public HttpComponentConfigurationAdapter( 50 | Class servletClazz, String... urlPatterns) { 51 | this(Utils.newInstance(servletClazz), urlPatterns); 52 | } 53 | 54 | public HttpComponentConfigurationAdapter(COMP servlet) { 55 | this(servlet, DEFAULT_URL_PATTERN); 56 | } 57 | 58 | @SuppressWarnings("unchecked") 59 | public HttpComponentConfigurationAdapter(COMP component, 60 | String... urlPatterns) { 61 | if (urlPatterns == null || urlPatterns.length == 0) 62 | throw new IllegalStateException( 63 | "No url patterns were assigned to http component: " 64 | + component); 65 | 66 | this.regexPatterns = new Pattern[urlPatterns.length]; 67 | this.sanitizedUrlPatterns = new String[urlPatterns.length]; 68 | 69 | for (int i = 0; i < urlPatterns.length; i++) { 70 | String regex = urlPatterns[i].replaceAll("\\*", ".*"); 71 | this.regexPatterns[i] = Pattern.compile(regex); 72 | this.sanitizedUrlPatterns[i] = urlPatterns[i].replaceAll("\\*", ""); 73 | if (this.sanitizedUrlPatterns[i].endsWith("/")) 74 | this.sanitizedUrlPatterns[i] = this.sanitizedUrlPatterns[i] 75 | .substring(0, this.sanitizedUrlPatterns[i].length() - 1); 76 | } 77 | 78 | this.component = component; 79 | this.config = newConfigInstance((Class) component 80 | .getClass()); 81 | } 82 | 83 | protected abstract CONFIG newConfigInstance( 84 | Class componentClazz); 85 | 86 | public void init() { 87 | try { 88 | 89 | log.debug("Initializing http component: {}", this.component 90 | .getClass()); 91 | 92 | this.doInit(); 93 | this.initialized = true; 94 | 95 | } catch (ServletException e) { 96 | 97 | this.initialized = false; 98 | log.error("Http component '" + this.component.getClass() 99 | + "' was not initialized!", e); 100 | } 101 | } 102 | 103 | public void destroy() { 104 | try { 105 | 106 | log.debug("Destroying http component: {}", this.component 107 | .getClass()); 108 | 109 | this.doDestroy(); 110 | this.initialized = false; 111 | 112 | } catch (ServletException e) { 113 | 114 | this.initialized = false; 115 | log.error("Http component '" + this.component.getClass() 116 | + "' was not destroyed!", e); 117 | } 118 | } 119 | 120 | protected abstract void doInit() throws ServletException; 121 | 122 | protected abstract void doDestroy() throws ServletException; 123 | 124 | public boolean matchesUrlPattern(String uri) { 125 | return getMatchingUrlPattern(uri) != null; 126 | } 127 | 128 | public String getMatchingUrlPattern(String uri) { 129 | int indx = uri.indexOf('?'); 130 | 131 | String path = indx != -1 ? uri.substring(0, indx) : uri.substring(0); 132 | if (!path.endsWith("/")) 133 | path += "/"; 134 | 135 | for (int i = 0; i < regexPatterns.length; i++) { 136 | Pattern pattern = regexPatterns[i]; 137 | if (pattern.matcher(path).matches()) { 138 | return sanitizedUrlPatterns[i]; 139 | } 140 | } 141 | 142 | return null; 143 | 144 | } 145 | 146 | protected void addConfigInitParameter(String name, String value) { 147 | this.config.addInitParameter(name, value); 148 | } 149 | 150 | public COMP getHttpComponent() { 151 | return this.component; 152 | } 153 | 154 | public CONFIG getConfig() { 155 | return this.config; 156 | } 157 | 158 | public boolean isInitialized() { 159 | return initialized; 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/config/ServletConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.config; 18 | 19 | import net.javaforge.netty.servlet.bridge.impl.ServletConfigImpl; 20 | 21 | import javax.servlet.ServletException; 22 | import javax.servlet.http.HttpServlet; 23 | 24 | public class ServletConfiguration extends 25 | HttpComponentConfigurationAdapter { 26 | 27 | public ServletConfiguration(Class servletClazz, 28 | String... urlPatterns) { 29 | super(servletClazz, urlPatterns); 30 | } 31 | 32 | public ServletConfiguration(Class componentClazz) { 33 | super(componentClazz); 34 | } 35 | 36 | public ServletConfiguration(HttpServlet component, String... urlPatterns) { 37 | super(component, urlPatterns); 38 | } 39 | 40 | public ServletConfiguration(HttpServlet servlet) { 41 | super(servlet); 42 | } 43 | 44 | @Override 45 | protected void doInit() throws ServletException { 46 | this.component.init(this.config); 47 | } 48 | 49 | @Override 50 | protected void doDestroy() throws ServletException { 51 | this.component.destroy(); 52 | } 53 | 54 | @Override 55 | protected ServletConfigImpl newConfigInstance( 56 | Class componentClazz) { 57 | return new ServletConfigImpl(this.component.getClass().getName()); 58 | } 59 | 60 | public ServletConfiguration addInitParameter(String name, String value) { 61 | super.addConfigInitParameter(name, value); 62 | return this; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/config/ServletContextListenerConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.config; 18 | 19 | import net.javaforge.netty.servlet.bridge.impl.ServletContextImpl; 20 | import net.javaforge.netty.servlet.bridge.util.Utils; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import javax.servlet.ServletContextEvent; 25 | import javax.servlet.ServletContextListener; 26 | 27 | public class ServletContextListenerConfiguration { 28 | 29 | private static final Logger log = LoggerFactory 30 | .getLogger(ServletContextListenerConfiguration.class); 31 | 32 | private ServletContextListener listener; 33 | 34 | private boolean initialized = false; 35 | 36 | public ServletContextListenerConfiguration( 37 | Class clazz) { 38 | this(Utils.newInstance(clazz)); 39 | } 40 | 41 | public ServletContextListenerConfiguration(ServletContextListener listener) { 42 | this.listener = listener; 43 | } 44 | 45 | public ServletContextListener getListener() { 46 | return listener; 47 | } 48 | 49 | public void init() { 50 | try { 51 | 52 | log.debug("Initializing listener: {}", this.listener.getClass()); 53 | 54 | this.listener.contextInitialized(new ServletContextEvent( 55 | ServletContextImpl.get())); 56 | this.initialized = true; 57 | 58 | } catch (Exception e) { 59 | 60 | this.initialized = false; 61 | log.error("Listener '" + this.listener.getClass() 62 | + "' was not initialized!", e); 63 | } 64 | } 65 | 66 | public void destroy() { 67 | try { 68 | 69 | log.debug("Destroying listener: {}", this.listener.getClass()); 70 | 71 | this.listener.contextDestroyed(new ServletContextEvent( 72 | ServletContextImpl.get())); 73 | this.initialized = false; 74 | 75 | } catch (Exception e) { 76 | 77 | this.initialized = false; 78 | log.error("Listener '" + this.listener.getClass() 79 | + "' was not destroyed!", e); 80 | } 81 | } 82 | 83 | public boolean isInitialized() { 84 | return initialized; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/config/WebappConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.config; 18 | 19 | import javax.servlet.Filter; 20 | import javax.servlet.ServletContextListener; 21 | import javax.servlet.http.HttpServlet; 22 | import java.io.File; 23 | import java.util.*; 24 | 25 | public class WebappConfiguration { 26 | 27 | private String name; 28 | 29 | private int sessionTimeout = 60 * 60; // 1 hour 30 | 31 | private Map contextParameters; 32 | 33 | private Collection contextListeners; 34 | 35 | private Collection filters; 36 | 37 | private Collection servlets; 38 | 39 | private File staticResourcesFolder; 40 | 41 | public WebappConfiguration addContextParameter(String name, String value) { 42 | 43 | if (this.contextParameters == null) 44 | this.contextParameters = new HashMap(); 45 | 46 | this.contextParameters.put(name, value); 47 | return this; 48 | } 49 | 50 | public WebappConfiguration addServletContextListener( 51 | Class listenerClass) { 52 | return this 53 | .addServletContextListenerConfigurations(new ServletContextListenerConfiguration( 54 | listenerClass)); 55 | } 56 | 57 | public WebappConfiguration addServletContextListener( 58 | ServletContextListener listener) { 59 | return this 60 | .addServletContextListenerConfigurations(new ServletContextListenerConfiguration( 61 | listener)); 62 | } 63 | 64 | public WebappConfiguration addServletContextListenerConfigurations( 65 | ServletContextListenerConfiguration... configs) { 66 | 67 | if (configs == null || configs.length == 0) 68 | return this; 69 | 70 | return this.addServletContextListenerConfigurations(Arrays 71 | .asList(configs)); 72 | } 73 | 74 | public WebappConfiguration addServletContextListenerConfigurations( 75 | List configs) { 76 | if (configs == null || configs.size() == 0) 77 | return this; 78 | 79 | if (this.contextListeners == null) 80 | this.contextListeners = new ArrayList(); 81 | 82 | this.contextListeners.addAll(configs); 83 | return this; 84 | } 85 | 86 | public Collection getServletContextListenerConfigurations() { 87 | return this.contextListeners != null ? Collections 88 | .unmodifiableCollection(this.contextListeners) : null; 89 | } 90 | 91 | public Map getContextParameters() { 92 | return this.contextParameters != null ? Collections 93 | .unmodifiableMap(this.contextParameters) : null; 94 | } 95 | 96 | public WebappConfiguration setName(String name) { 97 | this.name = name; 98 | return this; 99 | } 100 | 101 | public String getName() { 102 | return this.name; 103 | } 104 | 105 | public WebappConfiguration setSessionTimeout(int sessionTimeout) { 106 | this.sessionTimeout = sessionTimeout; 107 | return this; 108 | } 109 | 110 | public int getSessionTimeout() { 111 | return sessionTimeout; 112 | } 113 | 114 | public WebappConfiguration addFilter(Filter filter) { 115 | return this.addFilterConfigurations(new FilterConfiguration(filter)); 116 | } 117 | 118 | public WebappConfiguration addFilter(Filter filter, String... urlPatterns) { 119 | return this.addFilterConfigurations(new FilterConfiguration(filter, 120 | urlPatterns)); 121 | } 122 | 123 | public WebappConfiguration addFilter(Class filterClass) { 124 | return this 125 | .addFilterConfigurations(new FilterConfiguration(filterClass)); 126 | } 127 | 128 | public WebappConfiguration addFilter(Class filterClass, 129 | String... urlPatterns) { 130 | return this.addFilterConfigurations(new FilterConfiguration( 131 | filterClass, urlPatterns)); 132 | } 133 | 134 | public WebappConfiguration addFilterConfigurations( 135 | FilterConfiguration... filters) { 136 | 137 | if (filters == null || filters.length == 0) 138 | return this; 139 | 140 | return this.addfilterConfigurations(Arrays.asList(filters)); 141 | } 142 | 143 | public WebappConfiguration addfilterConfigurations( 144 | Collection configs) { 145 | 146 | if (configs == null || configs.size() == 0) 147 | return this; 148 | 149 | if (this.filters == null) 150 | this.filters = new ArrayList(); 151 | 152 | this.filters.addAll(configs); 153 | return this; 154 | } 155 | 156 | public Collection getFilterConfigurations() { 157 | return this.filters != null ? Collections 158 | .unmodifiableCollection(this.filters) : null; 159 | } 160 | 161 | public boolean hasFilterConfigurations() { 162 | return this.filters != null && !this.filters.isEmpty(); 163 | } 164 | 165 | public WebappConfiguration addHttpServlet(HttpServlet servlet) { 166 | return this.addServletConfigurations(new ServletConfiguration(servlet)); 167 | } 168 | 169 | public WebappConfiguration addHttpServlet(HttpServlet servlet, 170 | String... urlPatterns) { 171 | return this.addServletConfigurations(new ServletConfiguration(servlet, 172 | urlPatterns)); 173 | } 174 | 175 | public WebappConfiguration addHttpServlet( 176 | Class servletClass) { 177 | return this.addServletConfigurations(new ServletConfiguration( 178 | servletClass)); 179 | } 180 | 181 | public WebappConfiguration addHttpServlet( 182 | Class servletClass, String... urlPatterns) { 183 | return this.addServletConfigurations(new ServletConfiguration( 184 | servletClass, urlPatterns)); 185 | } 186 | 187 | public WebappConfiguration addServletConfigurations( 188 | ServletConfiguration... servlets) { 189 | 190 | if (servlets == null || servlets.length == 0) 191 | return this; 192 | 193 | return this.addServletConfigurations(Arrays.asList(servlets)); 194 | } 195 | 196 | public WebappConfiguration addServletConfigurations( 197 | Collection configs) { 198 | 199 | if (configs == null || configs.size() == 0) 200 | return this; 201 | 202 | if (this.servlets == null) 203 | this.servlets = new ArrayList(); 204 | 205 | this.servlets.addAll(configs); 206 | return this; 207 | } 208 | 209 | public Collection getServletConfigurations() { 210 | return this.servlets != null ? Collections 211 | .unmodifiableCollection(this.servlets) : null; 212 | } 213 | 214 | public boolean hasServletConfigurations() { 215 | return this.servlets != null && !this.servlets.isEmpty(); 216 | } 217 | 218 | public WebappConfiguration setStaticResourcesFolder(String folder) { 219 | return this.setStaticResourcesFolder(new File(folder)); 220 | } 221 | 222 | public WebappConfiguration setStaticResourcesFolder(File folder) { 223 | if (folder == null) 224 | throw new IllegalArgumentException( 225 | "Static resources folder must be not null!"); 226 | 227 | if (!folder.exists()) 228 | throw new IllegalArgumentException("Static resources folder '" 229 | + folder.getAbsolutePath() + "' was not found!"); 230 | 231 | if (!folder.isDirectory()) 232 | throw new IllegalArgumentException("Static resources folder '" 233 | + folder.getAbsolutePath() + "' must be a directory!"); 234 | 235 | this.staticResourcesFolder = folder; 236 | return this; 237 | } 238 | 239 | public File getStaticResourcesFolder() { 240 | return staticResourcesFolder; 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/ConfigAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import net.javaforge.netty.servlet.bridge.util.Utils; 20 | 21 | import java.util.Enumeration; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | public abstract class ConfigAdapter { 26 | 27 | private String ownerName; 28 | 29 | private Map initParameters; 30 | 31 | public ConfigAdapter(String ownerName) { 32 | this.ownerName = ownerName; 33 | } 34 | 35 | public void addInitParameter(String name, String value) { 36 | if (this.initParameters == null) 37 | this.initParameters = new HashMap(); 38 | 39 | this.initParameters.put(name, value); 40 | } 41 | 42 | public String getInitParameter(String name) { 43 | if (this.initParameters == null) 44 | return null; 45 | 46 | return this.initParameters.get(name); 47 | } 48 | 49 | @SuppressWarnings("unchecked") 50 | public Enumeration getInitParameterNames() { 51 | return Utils.enumerationFromKeys(this.initParameters); 52 | } 53 | 54 | String getOwnerName() { 55 | return ownerName; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/FilterChainImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import net.javaforge.netty.servlet.bridge.config.FilterConfiguration; 20 | import net.javaforge.netty.servlet.bridge.config.ServletConfiguration; 21 | 22 | import javax.servlet.FilterChain; 23 | import javax.servlet.ServletException; 24 | import javax.servlet.ServletRequest; 25 | import javax.servlet.ServletResponse; 26 | import java.io.IOException; 27 | import java.util.LinkedList; 28 | 29 | public class FilterChainImpl implements FilterChain { 30 | 31 | private LinkedList filterConfigurations; 32 | 33 | private ServletConfiguration servletConfiguration; 34 | 35 | public FilterChainImpl(ServletConfiguration servletConfiguration) { 36 | this.servletConfiguration = servletConfiguration; 37 | } 38 | 39 | public void addFilterConfiguration(FilterConfiguration config) { 40 | 41 | if (this.filterConfigurations == null) 42 | this.filterConfigurations = new LinkedList(); 43 | 44 | this.filterConfigurations.add(config); 45 | } 46 | 47 | @Override 48 | public void doFilter(ServletRequest request, ServletResponse response) 49 | throws IOException, ServletException { 50 | 51 | FilterConfiguration config = filterConfigurations != null ? filterConfigurations 52 | .poll() 53 | : null; 54 | 55 | if (config != null) 56 | config.getHttpComponent().doFilter(request, response, this); 57 | 58 | else if (this.servletConfiguration != null) { 59 | 60 | this.servletConfiguration.getHttpComponent().service(request, 61 | response); 62 | } 63 | } 64 | 65 | public boolean isValid() { 66 | return this.servletConfiguration != null 67 | || (this.filterConfigurations != null && !this.filterConfigurations 68 | .isEmpty()); 69 | } 70 | 71 | public ServletConfiguration getServletConfiguration() { 72 | return servletConfiguration; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/FilterConfigImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import javax.servlet.FilterConfig; 20 | import javax.servlet.ServletContext; 21 | 22 | public class FilterConfigImpl extends ConfigAdapter implements FilterConfig { 23 | 24 | public FilterConfigImpl(String filterName) { 25 | super(filterName); 26 | } 27 | 28 | @Override 29 | public String getFilterName() { 30 | return super.getOwnerName(); 31 | } 32 | 33 | @Override 34 | public ServletContext getServletContext() { 35 | return ServletContextImpl.get(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/HttpServletRequestImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import io.netty.handler.codec.http.*; 20 | import io.netty.handler.codec.http.HttpHeaders.Names; 21 | import io.netty.handler.ssl.SslHandler; 22 | import net.javaforge.netty.servlet.bridge.ChannelThreadLocal; 23 | import net.javaforge.netty.servlet.bridge.HttpSessionThreadLocal; 24 | import net.javaforge.netty.servlet.bridge.util.Utils; 25 | 26 | import javax.servlet.RequestDispatcher; 27 | import javax.servlet.ServletInputStream; 28 | import javax.servlet.http.Cookie; 29 | import javax.servlet.http.HttpServletRequest; 30 | import javax.servlet.http.HttpSession; 31 | import java.io.BufferedReader; 32 | import java.io.IOException; 33 | import java.io.InputStreamReader; 34 | import java.io.UnsupportedEncodingException; 35 | import java.net.InetSocketAddress; 36 | import java.security.Principal; 37 | import java.util.*; 38 | 39 | import static io.netty.handler.codec.http.HttpHeaders.Names.*; 40 | 41 | @SuppressWarnings("unchecked") 42 | public class HttpServletRequestImpl implements HttpServletRequest { 43 | 44 | private static final Locale DEFAULT_LOCALE = Locale.getDefault(); 45 | 46 | private URIParser uriParser; 47 | 48 | private HttpRequest originalRequest; 49 | 50 | private ServletInputStreamImpl inputStream; 51 | 52 | private BufferedReader reader; 53 | 54 | private QueryStringDecoder queryStringDecoder; 55 | 56 | private Map attributes; 57 | 58 | private Principal userPrincipal; 59 | 60 | // private ServerCookieDecoder cookieDecoder = new ServerCookieDecoder(); 61 | 62 | private String characterEncoding; 63 | 64 | public HttpServletRequestImpl(HttpRequest request, FilterChainImpl chain) { 65 | this.originalRequest = request; 66 | 67 | if (request instanceof FullHttpRequest) { 68 | this.inputStream = new ServletInputStreamImpl((FullHttpRequest) request); 69 | } else { 70 | this.inputStream = new ServletInputStreamImpl(request); 71 | } 72 | this.reader = new BufferedReader(new InputStreamReader(inputStream)); 73 | this.queryStringDecoder = new QueryStringDecoder(request.getUri()); 74 | this.uriParser = new URIParser(chain); 75 | this.uriParser.parse(request.getUri()); 76 | this.characterEncoding = Utils 77 | .getCharsetFromContentType(getContentType()); 78 | 79 | } 80 | 81 | public HttpRequest getOriginalRequest() { 82 | return originalRequest; 83 | } 84 | 85 | @Override 86 | public String getContextPath() { 87 | return ServletContextImpl.get().getContextPath(); 88 | } 89 | 90 | @Override 91 | public Cookie[] getCookies() { 92 | String cookieString = this.originalRequest.headers().get(COOKIE); 93 | if (cookieString != null) { 94 | Set cookies = CookieDecoder 95 | .decode(cookieString); 96 | if (!cookies.isEmpty()) { 97 | Cookie[] cookiesArray = new Cookie[cookies.size()]; 98 | int indx = 0; 99 | for (io.netty.handler.codec.http.Cookie c : cookies) { 100 | Cookie cookie = new Cookie(c.getName(), c.getValue()); 101 | cookie.setComment(c.getComment()); 102 | cookie.setDomain(c.getDomain()); 103 | cookie.setMaxAge((int) c.getMaxAge()); 104 | cookie.setPath(c.getPath()); 105 | cookie.setSecure(c.isSecure()); 106 | cookie.setVersion(c.getVersion()); 107 | cookiesArray[indx] = cookie; 108 | indx++; 109 | } 110 | return cookiesArray; 111 | 112 | } 113 | } 114 | return null; 115 | } 116 | 117 | @Override 118 | public long getDateHeader(String name) { 119 | String longVal = getHeader(name); 120 | if (longVal == null) 121 | return -1; 122 | 123 | return Long.parseLong(longVal); 124 | } 125 | 126 | @Override 127 | public String getHeader(String name) { 128 | return HttpHeaders.getHeader(this.originalRequest, name); 129 | } 130 | 131 | @Override 132 | public Enumeration getHeaderNames() { 133 | return Utils.enumeration(this.originalRequest.headers().names()); 134 | } 135 | 136 | @Override 137 | public Enumeration getHeaders(String name) { 138 | return Utils.enumeration(this.originalRequest.headers().getAll(name)); 139 | } 140 | 141 | @Override 142 | public int getIntHeader(String name) { 143 | return HttpHeaders.getIntHeader(this.originalRequest, name, -1); 144 | } 145 | 146 | @Override 147 | public String getMethod() { 148 | return this.originalRequest.method().name(); 149 | } 150 | 151 | @Override 152 | public String getQueryString() { 153 | return this.uriParser.getQueryString(); 154 | } 155 | 156 | @Override 157 | public String getRequestURI() { 158 | return this.uriParser.getRequestUri(); 159 | } 160 | 161 | @Override 162 | public StringBuffer getRequestURL() { 163 | StringBuffer url = new StringBuffer(); 164 | String scheme = this.getScheme(); 165 | int port = this.getServerPort(); 166 | String urlPath = this.getRequestURI(); 167 | 168 | // String servletPath = req.getServletPath (); 169 | // String pathInfo = req.getPathInfo (); 170 | 171 | url.append(scheme); // http, https 172 | url.append("://"); 173 | url.append(this.getServerName()); 174 | if ((scheme.equals("http") && port != 80) 175 | || (scheme.equals("https") && port != 443)) { 176 | url.append(':'); 177 | url.append(this.getServerPort()); 178 | } 179 | // if (servletPath != null) 180 | // url.append (servletPath); 181 | // if (pathInfo != null) 182 | // url.append (pathInfo); 183 | url.append(urlPath); 184 | return url; 185 | } 186 | 187 | @Override 188 | public int getContentLength() { 189 | return (int) HttpHeaders.getContentLength(this.originalRequest, -1); 190 | } 191 | 192 | @Override 193 | public String getContentType() { 194 | return HttpHeaders.getHeader(this.originalRequest, 195 | HttpHeaders.Names.CONTENT_TYPE); 196 | } 197 | 198 | @Override 199 | public ServletInputStream getInputStream() throws IOException { 200 | return this.inputStream; 201 | } 202 | 203 | @Override 204 | public String getCharacterEncoding() { 205 | return this.characterEncoding; 206 | } 207 | 208 | @Override 209 | public String getParameter(String name) { 210 | String[] values = getParameterValues(name); 211 | return values != null ? values[0] : null; 212 | } 213 | 214 | @Override 215 | public Map getParameterMap() { 216 | return this.queryStringDecoder.parameters(); 217 | } 218 | 219 | @Override 220 | public Enumeration getParameterNames() { 221 | return Utils.enumerationFromKeys(this.queryStringDecoder 222 | .parameters()); 223 | } 224 | 225 | @Override 226 | public String[] getParameterValues(String name) { 227 | List values = this.queryStringDecoder.parameters().get(name); 228 | if (values == null || values.isEmpty()) 229 | return null; 230 | return values.toArray(new String[values.size()]); 231 | } 232 | 233 | @Override 234 | public String getProtocol() { 235 | return this.originalRequest.getProtocolVersion().toString(); 236 | } 237 | 238 | @Override 239 | public Object getAttribute(String name) { 240 | if (attributes != null) 241 | return this.attributes.get(name); 242 | 243 | return null; 244 | } 245 | 246 | @Override 247 | public Enumeration getAttributeNames() { 248 | return Utils.enumerationFromKeys(this.attributes); 249 | } 250 | 251 | @Override 252 | public void removeAttribute(String name) { 253 | if (this.attributes != null) 254 | this.attributes.remove(name); 255 | } 256 | 257 | @Override 258 | public void setAttribute(String name, Object o) { 259 | if (this.attributes == null) 260 | this.attributes = new HashMap(); 261 | 262 | this.attributes.put(name, o); 263 | } 264 | 265 | @Override 266 | public BufferedReader getReader() throws IOException { 267 | return this.reader; 268 | } 269 | 270 | @Override 271 | public String getRequestedSessionId() { 272 | HttpSessionImpl session = HttpSessionThreadLocal.get(); 273 | return session != null ? session.getId() : null; 274 | } 275 | 276 | @Override 277 | public HttpSession getSession() { 278 | HttpSession s = HttpSessionThreadLocal.getOrCreate(); 279 | return s; 280 | } 281 | 282 | @Override 283 | public HttpSession getSession(boolean create) { 284 | HttpSession session = HttpSessionThreadLocal.get(); 285 | if (session == null && create) { 286 | session = HttpSessionThreadLocal.getOrCreate(); 287 | } 288 | return session; 289 | } 290 | 291 | @Override 292 | public String getPathInfo() { 293 | return this.uriParser.getPathInfo(); 294 | } 295 | 296 | @Override 297 | public Locale getLocale() { 298 | String locale = HttpHeaders.getHeader(this.originalRequest, 299 | Names.ACCEPT_LANGUAGE, DEFAULT_LOCALE.toString()); 300 | return new Locale(locale); 301 | } 302 | 303 | @Override 304 | public String getRemoteAddr() { 305 | InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get() 306 | .remoteAddress(); 307 | return addr.getAddress().getHostAddress(); 308 | } 309 | 310 | @Override 311 | public String getRemoteHost() { 312 | InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get() 313 | .remoteAddress(); 314 | return addr.getHostName(); 315 | } 316 | 317 | @Override 318 | public int getRemotePort() { 319 | InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get() 320 | .remoteAddress(); 321 | return addr.getPort(); 322 | } 323 | 324 | @Override 325 | public String getServerName() { 326 | InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get() 327 | .localAddress(); 328 | return addr.getHostName(); 329 | } 330 | 331 | @Override 332 | public int getServerPort() { 333 | InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get() 334 | .localAddress(); 335 | return addr.getPort(); 336 | } 337 | 338 | @Override 339 | public String getServletPath() { 340 | String servletPath = this.uriParser.getServletPath(); 341 | if (servletPath.equals("/")) 342 | return ""; 343 | 344 | return servletPath; 345 | } 346 | 347 | @Override 348 | public String getScheme() { 349 | return this.isSecure() ? "https" : "http"; 350 | } 351 | 352 | @Override 353 | public boolean isSecure() { 354 | return ChannelThreadLocal.get().pipeline().get(SslHandler.class) != null; 355 | } 356 | 357 | @Override 358 | public boolean isRequestedSessionIdFromCookie() { 359 | return true; 360 | } 361 | 362 | @Override 363 | public String getLocalAddr() { 364 | InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get() 365 | .localAddress(); 366 | return addr.getAddress().getHostAddress(); 367 | } 368 | 369 | @Override 370 | public String getLocalName() { 371 | return getServerName(); 372 | } 373 | 374 | @Override 375 | public int getLocalPort() { 376 | return getServerPort(); 377 | } 378 | 379 | @Override 380 | public void setCharacterEncoding(String env) 381 | throws UnsupportedEncodingException { 382 | this.characterEncoding = env; 383 | } 384 | 385 | @Override 386 | public Enumeration getLocales() { 387 | Collection locales = Utils 388 | .parseAcceptLanguageHeader(HttpHeaders 389 | .getHeader(this.originalRequest, 390 | HttpHeaders.Names.ACCEPT_LANGUAGE)); 391 | 392 | if (locales == null || locales.isEmpty()) { 393 | locales = new ArrayList(); 394 | locales.add(Locale.getDefault()); 395 | } 396 | return Utils.enumeration(locales); 397 | } 398 | 399 | @Override 400 | public String getAuthType() { 401 | return getHeader(WWW_AUTHENTICATE); 402 | } 403 | 404 | @Override 405 | public String getPathTranslated() { 406 | throw new IllegalStateException( 407 | "Method 'getPathTranslated' not yet implemented!"); 408 | } 409 | 410 | @Override 411 | public String getRemoteUser() { 412 | return getHeader(AUTHORIZATION); 413 | } 414 | 415 | @Override 416 | public Principal getUserPrincipal() { 417 | return userPrincipal; 418 | } 419 | 420 | @Override 421 | public boolean isRequestedSessionIdFromURL() { 422 | throw new IllegalStateException( 423 | "Method 'isRequestedSessionIdFromURL' not yet implemented!"); 424 | } 425 | 426 | @Override 427 | public boolean isRequestedSessionIdFromUrl() { 428 | throw new IllegalStateException( 429 | "Method 'isRequestedSessionIdFromUrl' not yet implemented!"); 430 | } 431 | 432 | @Override 433 | public boolean isRequestedSessionIdValid() { 434 | throw new IllegalStateException( 435 | "Method 'isRequestedSessionIdValid' not yet implemented!"); 436 | } 437 | 438 | @Override 439 | public boolean isUserInRole(String role) { 440 | throw new IllegalStateException( 441 | "Method 'isUserInRole' not yet implemented!"); 442 | } 443 | 444 | @Override 445 | public String getRealPath(String path) { 446 | throw new IllegalStateException( 447 | "Method 'getRealPath' not yet implemented!"); 448 | } 449 | 450 | @Override 451 | public RequestDispatcher getRequestDispatcher(String path) { 452 | throw new IllegalStateException( 453 | "Method 'getRequestDispatcher' not yet implemented!"); 454 | } 455 | 456 | } 457 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/HttpServletResponseImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import io.netty.handler.codec.http.*; 20 | import io.netty.handler.codec.http.HttpHeaders.Names; 21 | import net.javaforge.netty.servlet.bridge.ServletBridgeRuntimeException; 22 | 23 | import javax.servlet.ServletOutputStream; 24 | import javax.servlet.http.Cookie; 25 | import javax.servlet.http.HttpServletResponse; 26 | import java.io.IOException; 27 | import java.io.PrintWriter; 28 | import java.io.UnsupportedEncodingException; 29 | import java.net.URLEncoder; 30 | import java.util.Locale; 31 | 32 | import static io.netty.handler.codec.http.HttpHeaders.Names.LOCATION; 33 | import static io.netty.handler.codec.http.HttpHeaders.Names.SET_COOKIE; 34 | 35 | public class HttpServletResponseImpl implements HttpServletResponse { 36 | private HttpResponse originalResponse; 37 | private ServletOutputStreamImpl outputStream; 38 | private PrintWriterImpl writer; 39 | private boolean responseCommited = false; 40 | private Locale locale = null; 41 | 42 | public HttpServletResponseImpl(FullHttpResponse response) { 43 | this.originalResponse = response; 44 | this.outputStream = new ServletOutputStreamImpl(response); 45 | this.writer = new PrintWriterImpl(this.outputStream); 46 | } 47 | 48 | public HttpResponse getOriginalResponse() { 49 | return originalResponse; 50 | } 51 | 52 | @Override 53 | public void addCookie(Cookie cookie) { 54 | String result = ServerCookieEncoder.encode(new io.netty.handler.codec.http.DefaultCookie(cookie.getName(), cookie.getValue())); 55 | HttpHeaders.addHeader(this.originalResponse, SET_COOKIE, result); 56 | } 57 | 58 | @Override 59 | public void addDateHeader(String name, long date) { 60 | HttpHeaders.addHeader(this.originalResponse, name, date); 61 | } 62 | 63 | @Override 64 | public void addHeader(String name, String value) { 65 | HttpHeaders.addHeader(this.originalResponse, name, value); 66 | } 67 | 68 | @Override 69 | public void addIntHeader(String name, int value) { 70 | HttpHeaders.addIntHeader(this.originalResponse, name, value); 71 | } 72 | 73 | @Override 74 | public boolean containsHeader(String name) { 75 | return this.originalResponse.headers().contains(name); 76 | } 77 | 78 | @Override 79 | public void sendError(int sc) throws IOException { 80 | this.originalResponse.setStatus(HttpResponseStatus.valueOf(sc)); 81 | } 82 | 83 | @Override 84 | public void sendError(int sc, String msg) throws IOException { 85 | //Fix the following exception 86 | /* 87 | java.lang.IllegalArgumentException: reasonPhrase contains one of the following prohibited characters: \r\n: FAILED - Cannot find View Map for null. 88 | 89 | at io.netty.handler.codec.http.HttpResponseStatus.(HttpResponseStatus.java:514) ~[netty-all-4.1.0.Beta3.jar:4.1.0.Beta3] 90 | at io.netty.handler.codec.http.HttpResponseStatus.(HttpResponseStatus.java:496) ~[netty-all-4.1.0.Beta3.jar:4.1.0.Beta3] 91 | */ 92 | if (msg != null) { 93 | msg = msg.replace('\r', ' '); 94 | msg = msg.replace('\n', ' '); 95 | } 96 | this.originalResponse.setStatus(new HttpResponseStatus(sc, msg)); 97 | } 98 | 99 | @Override 100 | public void sendRedirect(String location) throws IOException { 101 | setStatus(SC_FOUND); 102 | setHeader(LOCATION, location); 103 | } 104 | 105 | @Override 106 | public void setDateHeader(String name, long date) { 107 | HttpHeaders.setHeader(this.originalResponse, name, date); 108 | } 109 | 110 | @Override 111 | public void setHeader(String name, String value) { 112 | HttpHeaders.setHeader(this.originalResponse, name, value); 113 | } 114 | 115 | @Override 116 | public void setIntHeader(String name, int value) { 117 | HttpHeaders.setIntHeader(this.originalResponse, name, value); 118 | 119 | } 120 | 121 | @Override 122 | public ServletOutputStream getOutputStream() throws IOException { 123 | return this.outputStream; 124 | } 125 | 126 | @Override 127 | public PrintWriter getWriter() throws IOException { 128 | return this.writer; 129 | } 130 | 131 | @Override 132 | public void setStatus(int sc) { 133 | this.originalResponse.setStatus(HttpResponseStatus.valueOf(sc)); 134 | } 135 | 136 | @Override 137 | public void setStatus(int sc, String sm) { 138 | this.originalResponse.setStatus(new HttpResponseStatus(sc, sm)); 139 | } 140 | 141 | @Override 142 | public String getContentType() { 143 | return HttpHeaders.getHeader(this.originalResponse, 144 | HttpHeaders.Names.CONTENT_TYPE); 145 | } 146 | 147 | @Override 148 | public void setContentType(String type) { 149 | HttpHeaders.setHeader(this.originalResponse, 150 | HttpHeaders.Names.CONTENT_TYPE, type); 151 | } 152 | 153 | @Override 154 | public void setContentLength(int len) { 155 | HttpHeaders.setContentLength(this.originalResponse, len); 156 | } 157 | 158 | @Override 159 | public boolean isCommitted() { 160 | return this.responseCommited; 161 | } 162 | 163 | @Override 164 | public void reset() { 165 | if (isCommitted()) 166 | throw new IllegalStateException("Response already commited!"); 167 | 168 | this.originalResponse.headers().clear(); 169 | this.resetBuffer(); 170 | } 171 | 172 | @Override 173 | public void resetBuffer() { 174 | if (isCommitted()) 175 | throw new IllegalStateException("Response already commited!"); 176 | 177 | this.outputStream.resetBuffer(); 178 | } 179 | 180 | @Override 181 | public void flushBuffer() throws IOException { 182 | this.getWriter().flush(); 183 | this.responseCommited = true; 184 | } 185 | 186 | @Override 187 | public int getBufferSize() { 188 | return this.outputStream.getBufferSize(); 189 | } 190 | 191 | @Override 192 | public void setBufferSize(int size) { 193 | // we using always dynamic buffer for now 194 | } 195 | 196 | @Override 197 | public String encodeRedirectURL(String url) { 198 | return this.encodeURL(url); 199 | } 200 | 201 | @Override 202 | public String encodeRedirectUrl(String url) { 203 | return this.encodeURL(url); 204 | } 205 | 206 | @Override 207 | public String encodeURL(String url) { 208 | try { 209 | return URLEncoder.encode(url, getCharacterEncoding()); 210 | } catch (UnsupportedEncodingException e) { 211 | throw new ServletBridgeRuntimeException("Error encoding url!", e); 212 | } 213 | } 214 | 215 | @Override 216 | public String encodeUrl(String url) { 217 | return this.encodeRedirectURL(url); 218 | } 219 | 220 | @Override 221 | public String getCharacterEncoding() { 222 | return HttpHeaders.getHeader(this.originalResponse, 223 | Names.CONTENT_ENCODING); 224 | } 225 | 226 | @Override 227 | public void setCharacterEncoding(String charset) { 228 | HttpHeaders.setHeader(this.originalResponse, 229 | Names.CONTENT_ENCODING, charset); 230 | } 231 | 232 | @Override 233 | public Locale getLocale() { 234 | return locale; 235 | } 236 | 237 | @Override 238 | public void setLocale(Locale loc) { 239 | this.locale = loc; 240 | } 241 | } -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/HttpSessionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import net.javaforge.netty.servlet.bridge.util.Utils; 20 | 21 | import javax.servlet.ServletContext; 22 | import javax.servlet.http.HttpSession; 23 | import javax.servlet.http.HttpSessionBindingEvent; 24 | import javax.servlet.http.HttpSessionBindingListener; 25 | import javax.servlet.http.HttpSessionContext; 26 | import java.util.Enumeration; 27 | import java.util.Map; 28 | import java.util.concurrent.ConcurrentHashMap; 29 | 30 | @SuppressWarnings({"deprecation", "unchecked"}) 31 | public class HttpSessionImpl implements HttpSession { 32 | 33 | public static final String SESSION_ID_KEY = "JSESSIONID"; 34 | 35 | private String id; 36 | 37 | private long creationTime; 38 | 39 | private long lastAccessedTime; 40 | 41 | private int maxInactiveInterval = -1; 42 | 43 | private Map attributes; 44 | 45 | public HttpSessionImpl(String id) { 46 | this.id = id; 47 | this.creationTime = System.currentTimeMillis(); 48 | this.lastAccessedTime = this.creationTime; 49 | } 50 | 51 | @Override 52 | public Object getAttribute(String name) { 53 | return attributes != null ? attributes.get(name) : null; 54 | } 55 | 56 | @Override 57 | public Enumeration getAttributeNames() { 58 | return Utils.enumerationFromKeys(attributes); 59 | } 60 | 61 | @Override 62 | public long getCreationTime() { 63 | return this.creationTime; 64 | } 65 | 66 | @Override 67 | public String getId() { 68 | return this.id; 69 | } 70 | 71 | @Override 72 | public long getLastAccessedTime() { 73 | return this.lastAccessedTime; 74 | } 75 | 76 | @Override 77 | public ServletContext getServletContext() { 78 | return ServletContextImpl.get(); 79 | } 80 | 81 | @Override 82 | public HttpSessionContext getSessionContext() { 83 | throw new IllegalStateException( 84 | "As of Version 2.1, this method is deprecated and has no replacement."); 85 | } 86 | 87 | @Override 88 | public Object getValue(String name) { 89 | return getAttribute(name); 90 | } 91 | 92 | @Override 93 | public String[] getValueNames() { 94 | if (attributes == null) 95 | return null; 96 | 97 | return attributes.keySet().toArray( 98 | new String[attributes.keySet().size()]); 99 | } 100 | 101 | @Override 102 | public void invalidate() { 103 | if (attributes != null) { 104 | attributes.clear(); 105 | } 106 | } 107 | 108 | @Override 109 | public void putValue(String name, Object value) { 110 | this.setAttribute(name, value); 111 | } 112 | 113 | @Override 114 | public void removeAttribute(String name) { 115 | if (attributes != null) { 116 | Object value = attributes.get(name); 117 | if (value != null && value instanceof HttpSessionBindingListener) { 118 | ((HttpSessionBindingListener) value) 119 | .valueUnbound(new HttpSessionBindingEvent(this, name, 120 | value)); 121 | } 122 | attributes.remove(name); 123 | } 124 | } 125 | 126 | @Override 127 | public void removeValue(String name) { 128 | this.removeAttribute(name); 129 | } 130 | 131 | @Override 132 | public void setAttribute(String name, Object value) { 133 | if (attributes == null) 134 | attributes = new ConcurrentHashMap(); 135 | 136 | attributes.put(name, value); 137 | 138 | if (value != null && value instanceof HttpSessionBindingListener) { 139 | ((HttpSessionBindingListener) value) 140 | .valueBound(new HttpSessionBindingEvent(this, name, value)); 141 | } 142 | 143 | } 144 | 145 | @Override 146 | public int getMaxInactiveInterval() { 147 | return this.maxInactiveInterval; 148 | } 149 | 150 | @Override 151 | public void setMaxInactiveInterval(int interval) { 152 | this.maxInactiveInterval = interval; 153 | 154 | } 155 | 156 | public void touch() { 157 | this.lastAccessedTime = System.currentTimeMillis(); 158 | } 159 | 160 | @Override 161 | public boolean isNew() { 162 | throw new IllegalStateException("Method 'isNew' not yet implemented!"); 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/PrintWriterImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import java.io.OutputStream; 20 | import java.io.PrintWriter; 21 | 22 | public class PrintWriterImpl extends PrintWriter { 23 | 24 | private boolean flushed = false; 25 | 26 | public PrintWriterImpl(OutputStream out) { 27 | super(out); 28 | } 29 | 30 | @Override 31 | public void flush() { 32 | super.flush(); 33 | this.flushed = true; 34 | } 35 | 36 | public boolean isFlushed() { 37 | return flushed; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/RequestDispatcherImpl.java: -------------------------------------------------------------------------------- 1 | package net.javaforge.netty.servlet.bridge.impl; 2 | 3 | import javax.servlet.RequestDispatcher; 4 | import javax.servlet.ServletException; 5 | import javax.servlet.ServletRequest; 6 | import javax.servlet.ServletResponse; 7 | import javax.servlet.http.HttpServlet; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | 11 | /** 12 | * Simple RequestDispatcher Implementation 13 | *

14 | * Created by xshao on 12/22/14. 15 | */ 16 | public class RequestDispatcherImpl implements RequestDispatcher { 17 | 18 | /** 19 | * The servlet name for a named dispatcher. 20 | */ 21 | private String name = null; 22 | 23 | /** 24 | * The servlet path for this RequestDispatcher. 25 | */ 26 | private String servletPath = null; 27 | 28 | 29 | private HttpServlet httpServlet; 30 | 31 | 32 | public RequestDispatcherImpl(String servletName, String servletPath, HttpServlet servlet) { 33 | this.name = servletName; 34 | this.servletPath = servletPath; 35 | this.httpServlet = servlet; 36 | } 37 | 38 | 39 | @Override 40 | public void forward(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { 41 | if (httpServlet != null) { 42 | //TODO Wrap 43 | httpServlet.service(servletRequest, servletResponse); 44 | } else { 45 | ((HttpServletResponse) servletResponse).sendError(HttpServletResponse.SC_NOT_FOUND); 46 | } 47 | } 48 | 49 | @Override 50 | public void include(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { 51 | if (httpServlet != null) { 52 | //TODO Wrap 53 | httpServlet.service(servletRequest, servletResponse); 54 | } else { 55 | ((HttpServletResponse) servletResponse).sendError(HttpServletResponse.SC_NOT_FOUND); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/ServletBridgeWebapp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import io.netty.channel.group.ChannelGroup; 20 | import net.javaforge.netty.servlet.bridge.config.FilterConfiguration; 21 | import net.javaforge.netty.servlet.bridge.config.ServletConfiguration; 22 | import net.javaforge.netty.servlet.bridge.config.ServletContextListenerConfiguration; 23 | import net.javaforge.netty.servlet.bridge.config.WebappConfiguration; 24 | 25 | import java.io.File; 26 | import java.util.Map; 27 | 28 | public class ServletBridgeWebapp { 29 | 30 | private static ServletBridgeWebapp instance; 31 | 32 | private WebappConfiguration webappConfig; 33 | 34 | private ChannelGroup sharedChannelGroup; 35 | 36 | public static ServletBridgeWebapp get() { 37 | 38 | if (instance == null) 39 | instance = new ServletBridgeWebapp(); 40 | 41 | return instance; 42 | } 43 | 44 | private ServletBridgeWebapp() { 45 | } 46 | 47 | public void init(WebappConfiguration webapp, ChannelGroup sharedChannelGroup) { 48 | this.webappConfig = webapp; 49 | this.sharedChannelGroup = sharedChannelGroup; 50 | this.initServletContext(); 51 | this.initContextListeners(); 52 | this.initFilters(); 53 | this.initServlets(); 54 | } 55 | 56 | public void destroy() { 57 | this.destroyServlets(); 58 | this.destroyFilters(); 59 | this.destroyContextListeners(); 60 | } 61 | 62 | private void initContextListeners() { 63 | if (webappConfig.getServletContextListenerConfigurations() != null) { 64 | for (ServletContextListenerConfiguration ctx : webappConfig 65 | .getServletContextListenerConfigurations()) { 66 | ctx.init(); 67 | } 68 | } 69 | } 70 | 71 | private void destroyContextListeners() { 72 | if (webappConfig.getServletContextListenerConfigurations() != null) { 73 | for (ServletContextListenerConfiguration ctx : webappConfig 74 | .getServletContextListenerConfigurations()) { 75 | ctx.destroy(); 76 | } 77 | } 78 | } 79 | 80 | private void destroyServlets() { 81 | if (webappConfig.getServletConfigurations() != null) { 82 | for (ServletConfiguration servlet : webappConfig 83 | .getServletConfigurations()) { 84 | servlet.destroy(); 85 | } 86 | } 87 | } 88 | 89 | private void destroyFilters() { 90 | if (webappConfig.getFilterConfigurations() != null) { 91 | for (FilterConfiguration filter : webappConfig 92 | .getFilterConfigurations()) { 93 | filter.destroy(); 94 | } 95 | } 96 | } 97 | 98 | protected void initServletContext() { 99 | ServletContextImpl ctx = ServletContextImpl.get(); 100 | ctx.setServletContextName(this.webappConfig.getName()); 101 | if (webappConfig.getContextParameters() != null) { 102 | for (Map.Entry entry : webappConfig 103 | .getContextParameters().entrySet()) { 104 | ctx.addInitParameter(entry.getKey(), entry.getValue()); 105 | } 106 | } 107 | } 108 | 109 | protected void initFilters() { 110 | if (webappConfig.getFilterConfigurations() != null) { 111 | for (FilterConfiguration filter : webappConfig 112 | .getFilterConfigurations()) { 113 | filter.init(); 114 | } 115 | } 116 | } 117 | 118 | protected void initServlets() { 119 | if (webappConfig.hasServletConfigurations()) { 120 | for (ServletConfiguration servlet : webappConfig 121 | .getServletConfigurations()) { 122 | servlet.init(); 123 | } 124 | } 125 | } 126 | 127 | public FilterChainImpl initializeChain(String uri) { 128 | ServletConfiguration servletConfiguration = this.findServlet(uri); 129 | FilterChainImpl chain = new FilterChainImpl(servletConfiguration); 130 | 131 | if (this.webappConfig.hasFilterConfigurations()) { 132 | for (FilterConfiguration s : this.webappConfig 133 | .getFilterConfigurations()) { 134 | if (s.matchesUrlPattern(uri)) 135 | chain.addFilterConfiguration(s); 136 | } 137 | } 138 | 139 | return chain; 140 | } 141 | 142 | private ServletConfiguration findServlet(String uri) { 143 | 144 | if (!this.webappConfig.hasServletConfigurations()) { 145 | return null; 146 | } 147 | 148 | for (ServletConfiguration s : this.webappConfig 149 | .getServletConfigurations()) { 150 | if (s.matchesUrlPattern(uri)) 151 | return s; 152 | } 153 | 154 | return null; 155 | } 156 | 157 | public File getStaticResourcesFolder() { 158 | return this.webappConfig.getStaticResourcesFolder(); 159 | } 160 | 161 | public WebappConfiguration getWebappConfig() { 162 | return webappConfig; 163 | } 164 | 165 | public ChannelGroup getSharedChannelGroup() { 166 | return sharedChannelGroup; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/ServletConfigImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import javax.servlet.ServletConfig; 20 | import javax.servlet.ServletContext; 21 | 22 | public class ServletConfigImpl extends ConfigAdapter implements ServletConfig { 23 | 24 | public ServletConfigImpl(String servletName) { 25 | super(servletName); 26 | } 27 | 28 | @Override 29 | public String getServletName() { 30 | return super.getOwnerName(); 31 | } 32 | 33 | @Override 34 | public ServletContext getServletContext() { 35 | return ServletContextImpl.get(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/ServletContextImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import net.javaforge.netty.servlet.bridge.config.ServletConfiguration; 20 | import net.javaforge.netty.servlet.bridge.util.Utils; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import javax.servlet.RequestDispatcher; 25 | import javax.servlet.Servlet; 26 | import javax.servlet.ServletContext; 27 | import javax.servlet.ServletException; 28 | import javax.servlet.http.HttpServlet; 29 | import java.io.File; 30 | import java.io.IOException; 31 | import java.io.InputStream; 32 | import java.net.MalformedURLException; 33 | import java.net.URL; 34 | import java.util.*; 35 | 36 | @SuppressWarnings("unchecked") 37 | public class ServletContextImpl extends ConfigAdapter implements ServletContext { 38 | 39 | private static final Logger log = LoggerFactory 40 | .getLogger(ServletContextImpl.class); 41 | 42 | private static ServletContextImpl instance; 43 | 44 | private Map attributes; 45 | 46 | private String servletContextName; 47 | 48 | public static ServletContextImpl get() { 49 | if (instance == null) 50 | instance = new ServletContextImpl(); 51 | 52 | return instance; 53 | } 54 | 55 | private ServletContextImpl() { 56 | super("Netty Servlet Bridge"); 57 | } 58 | 59 | @Override 60 | public Object getAttribute(String name) { 61 | return attributes != null ? attributes.get(name) : null; 62 | } 63 | 64 | @Override 65 | public Enumeration getAttributeNames() { 66 | return Utils.enumerationFromKeys(attributes); 67 | } 68 | 69 | @Override 70 | public String getContextPath() { 71 | return ""; 72 | } 73 | 74 | @Override 75 | public int getMajorVersion() { 76 | return 2; 77 | } 78 | 79 | @Override 80 | public int getMinorVersion() { 81 | return 4; 82 | } 83 | 84 | @Override 85 | public URL getResource(String path) throws MalformedURLException { 86 | return ServletContextImpl.class.getResource(path); 87 | } 88 | 89 | @Override 90 | public InputStream getResourceAsStream(String path) { 91 | return ServletContextImpl.class.getResourceAsStream(path); 92 | } 93 | 94 | @Override 95 | public String getServerInfo() { 96 | return super.getOwnerName(); 97 | } 98 | 99 | @Override 100 | public void log(String msg) { 101 | log.info(msg); 102 | } 103 | 104 | @Override 105 | public void log(Exception exception, String msg) { 106 | log.error(msg, exception); 107 | } 108 | 109 | @Override 110 | public void log(String message, Throwable throwable) { 111 | log.error(message, throwable); 112 | } 113 | 114 | @Override 115 | public void removeAttribute(String name) { 116 | if (this.attributes != null) 117 | this.attributes.remove(name); 118 | } 119 | 120 | @Override 121 | public void setAttribute(String name, Object object) { 122 | if (this.attributes == null) 123 | this.attributes = new HashMap(); 124 | 125 | this.attributes.put(name, object); 126 | } 127 | 128 | @Override 129 | public String getServletContextName() { 130 | return this.servletContextName; 131 | } 132 | 133 | void setServletContextName(String servletContextName) { 134 | this.servletContextName = servletContextName; 135 | } 136 | 137 | @Override 138 | public Servlet getServlet(String name) throws ServletException { 139 | throw new IllegalStateException( 140 | "Deprecated as of Java Servlet API 2.1, with no direct replacement!"); 141 | } 142 | 143 | @Override 144 | public Enumeration getServletNames() { 145 | throw new IllegalStateException( 146 | "Method 'getServletNames' deprecated as of Java Servlet API 2.0, with no replacement."); 147 | } 148 | 149 | @Override 150 | public Enumeration getServlets() { 151 | throw new IllegalStateException( 152 | "Method 'getServlets' deprecated as of Java Servlet API 2.0, with no replacement."); 153 | } 154 | 155 | @Override 156 | public ServletContext getContext(String uripath) { 157 | return this; 158 | } 159 | 160 | @Override 161 | public String getMimeType(String file) { 162 | return Utils.getMimeType(file); 163 | 164 | } 165 | 166 | @Override 167 | public Set getResourcePaths(String path) { 168 | throw new IllegalStateException( 169 | "Method 'getResourcePaths' not yet implemented!"); 170 | } 171 | 172 | @Override 173 | public RequestDispatcher getNamedDispatcher(String name) { 174 | Collection colls = ServletBridgeWebapp.get().getWebappConfig().getServletConfigurations(); 175 | HttpServlet servlet = null; 176 | for (ServletConfiguration configuration : colls) { 177 | if (configuration.getConfig().getServletName().equals(name)) { 178 | servlet = configuration.getHttpComponent(); 179 | } 180 | } 181 | 182 | return new RequestDispatcherImpl(name, null, servlet); 183 | } 184 | 185 | @Override 186 | public String getRealPath(String path) { 187 | if ("/".equals(path)) { 188 | try { 189 | File file = File.createTempFile("netty-servlet-bridge", ""); 190 | file.mkdirs(); 191 | return file.getAbsolutePath(); 192 | } catch (IOException e) { 193 | throw new IllegalStateException( 194 | "Method 'getRealPath' not yet implemented!"); 195 | } 196 | } else { 197 | throw new IllegalStateException( 198 | "Method 'getRealPath' not yet implemented!"); 199 | } 200 | } 201 | 202 | @Override 203 | public RequestDispatcher getRequestDispatcher(String path) { 204 | Collection colls = ServletBridgeWebapp.get().getWebappConfig().getServletConfigurations(); 205 | HttpServlet servlet = null; 206 | String servletName = null; 207 | for (ServletConfiguration configuration : colls) { 208 | if (configuration.matchesUrlPattern(path)) { 209 | servlet = configuration.getHttpComponent(); 210 | servletName = configuration.getHttpComponent().getServletName(); 211 | } 212 | } 213 | 214 | return new RequestDispatcherImpl(servletName, path, servlet); 215 | } 216 | 217 | } 218 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/ServletInputStreamImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import io.netty.buffer.ByteBufInputStream; 20 | import io.netty.buffer.Unpooled; 21 | import io.netty.handler.codec.http.FullHttpRequest; 22 | import io.netty.handler.codec.http.HttpRequest; 23 | 24 | import javax.servlet.ServletInputStream; 25 | import java.io.IOException; 26 | 27 | public class ServletInputStreamImpl extends ServletInputStream { 28 | 29 | private HttpRequest request; 30 | 31 | private ByteBufInputStream in; 32 | 33 | public ServletInputStreamImpl(FullHttpRequest request) { 34 | this.request = request; 35 | 36 | this.in = new ByteBufInputStream(request.content()); 37 | } 38 | 39 | public ServletInputStreamImpl(HttpRequest request) { 40 | this.request = request; 41 | 42 | this.in = new ByteBufInputStream(Unpooled.buffer(0)); 43 | } 44 | 45 | 46 | @Override 47 | public int read() throws IOException { 48 | return this.in.read(); 49 | } 50 | 51 | @Override 52 | public int read(byte[] buf) throws IOException { 53 | return this.in.read(buf); 54 | } 55 | 56 | @Override 57 | public int read(byte[] buf, int offset, int len) throws IOException { 58 | return this.in.read(buf, offset, len); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/ServletOutputStreamImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | import io.netty.buffer.ByteBufOutputStream; 20 | import io.netty.handler.codec.http.FullHttpResponse; 21 | 22 | import javax.servlet.ServletOutputStream; 23 | import java.io.IOException; 24 | 25 | public class ServletOutputStreamImpl extends ServletOutputStream { 26 | 27 | private FullHttpResponse response; 28 | 29 | private ByteBufOutputStream out; 30 | 31 | private boolean flushed = false; 32 | 33 | public ServletOutputStreamImpl(FullHttpResponse response) { 34 | this.response = response; 35 | this.out = new ByteBufOutputStream(response.content()); 36 | } 37 | 38 | @Override 39 | public void write(int b) throws IOException { 40 | this.out.write(b); 41 | } 42 | 43 | @Override 44 | public void write(byte[] b) throws IOException { 45 | this.out.write(b); 46 | } 47 | 48 | @Override 49 | public void write(byte[] b, int offset, int len) throws IOException { 50 | this.out.write(b, offset, len); 51 | } 52 | 53 | @Override 54 | public void flush() throws IOException { 55 | // this.response.setContent(out.buffer()); 56 | this.flushed = true; 57 | } 58 | 59 | public void resetBuffer() { 60 | this.out.buffer().clear(); 61 | } 62 | 63 | public boolean isFlushed() { 64 | return flushed; 65 | } 66 | 67 | public int getBufferSize() { 68 | return this.out.buffer().capacity(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/impl/URIParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.impl; 18 | 19 | 20 | public class URIParser { 21 | 22 | private FilterChainImpl chain; 23 | 24 | private String servletPath; 25 | 26 | private String requestUri; 27 | 28 | private String pathInfo; 29 | 30 | private String queryString; 31 | 32 | public URIParser(FilterChainImpl chain) { 33 | this.chain = chain; 34 | } 35 | 36 | public void parse(String uri) { 37 | 38 | int indx = uri.indexOf('?'); 39 | this.servletPath = this.chain.getServletConfiguration() 40 | .getMatchingUrlPattern(uri); 41 | if (!this.servletPath.startsWith("/")) 42 | this.servletPath = "/" + this.servletPath; 43 | 44 | if (indx != -1) { 45 | this.pathInfo = uri.substring(servletPath.length(), indx); 46 | this.queryString = uri.substring(indx + 1); 47 | this.requestUri = uri.substring(0, indx); 48 | } else { 49 | this.pathInfo = uri.substring(servletPath.length()); 50 | this.requestUri = uri; 51 | } 52 | 53 | if (this.requestUri.endsWith("/")) 54 | this.requestUri.substring(0, this.requestUri.length() - 1); 55 | 56 | if (this.pathInfo.equals("")) 57 | this.pathInfo = null; 58 | else if (!this.pathInfo.startsWith("/")) 59 | this.pathInfo = "/" + this.pathInfo; 60 | 61 | } 62 | 63 | public String getServletPath() { 64 | return servletPath; 65 | } 66 | 67 | public String getQueryString() { 68 | return queryString; 69 | } 70 | 71 | public String getPathInfo() { 72 | return this.pathInfo; 73 | } 74 | 75 | public String getRequestUri() { 76 | return requestUri; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/interceptor/ChannelInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.interceptor; 18 | 19 | import io.netty.channel.ChannelHandlerContext; 20 | import io.netty.handler.codec.http.HttpRequest; 21 | import io.netty.handler.codec.http.HttpResponse; 22 | import net.javaforge.netty.servlet.bridge.ChannelThreadLocal; 23 | import net.javaforge.netty.servlet.bridge.ServletBridgeInterceptor; 24 | 25 | public class ChannelInterceptor implements ServletBridgeInterceptor { 26 | 27 | @Override 28 | public void onRequestFailed(ChannelHandlerContext ctx, Throwable e, 29 | HttpResponse response) { 30 | ChannelThreadLocal.unset(); 31 | } 32 | 33 | @Override 34 | public void onRequestReceived(ChannelHandlerContext ctx, HttpRequest e) { 35 | ChannelThreadLocal.set(ctx.channel()); 36 | } 37 | 38 | @Override 39 | public void onRequestSuccessed(ChannelHandlerContext ctx, HttpRequest e, 40 | HttpResponse response) { 41 | ChannelThreadLocal.unset(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/interceptor/HttpSessionInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.interceptor; 18 | 19 | import io.netty.channel.ChannelHandlerContext; 20 | import io.netty.handler.codec.http.*; 21 | import net.javaforge.netty.servlet.bridge.HttpSessionThreadLocal; 22 | import net.javaforge.netty.servlet.bridge.ServletBridgeInterceptor; 23 | import net.javaforge.netty.servlet.bridge.impl.HttpSessionImpl; 24 | import net.javaforge.netty.servlet.bridge.session.ServletBridgeHttpSessionStore; 25 | import net.javaforge.netty.servlet.bridge.util.Utils; 26 | 27 | import java.util.Collection; 28 | 29 | import static io.netty.handler.codec.http.HttpHeaders.Names.SET_COOKIE; 30 | 31 | public class HttpSessionInterceptor implements ServletBridgeInterceptor { 32 | 33 | private boolean sessionRequestedByCookie = false; 34 | 35 | public HttpSessionInterceptor(ServletBridgeHttpSessionStore sessionStore) { 36 | HttpSessionThreadLocal.setSessionStore(sessionStore); 37 | } 38 | 39 | @Override 40 | public void onRequestReceived(ChannelHandlerContext ctx, HttpRequest request) { 41 | 42 | HttpSessionThreadLocal.unset(); 43 | 44 | Collection cookies = Utils.getCookies( 45 | HttpSessionImpl.SESSION_ID_KEY, request); 46 | if (cookies != null) { 47 | for (Cookie cookie : cookies) { 48 | String jsessionId = cookie.getValue(); 49 | HttpSessionImpl s = HttpSessionThreadLocal.getSessionStore() 50 | .findSession(jsessionId); 51 | if (s != null) { 52 | HttpSessionThreadLocal.set(s); 53 | this.sessionRequestedByCookie = true; 54 | break; 55 | } 56 | } 57 | } 58 | } 59 | 60 | @Override 61 | public void onRequestSuccessed(ChannelHandlerContext ctx, HttpRequest request, 62 | HttpResponse response) { 63 | 64 | HttpSessionImpl s = HttpSessionThreadLocal.get(); 65 | if (s != null && !this.sessionRequestedByCookie) { 66 | HttpHeaders.addHeader(response, SET_COOKIE, ServerCookieEncoder.encode(HttpSessionImpl.SESSION_ID_KEY, s.getId())); 67 | } 68 | 69 | } 70 | 71 | @Override 72 | public void onRequestFailed(ChannelHandlerContext ctx, Throwable e, 73 | HttpResponse response) { 74 | this.sessionRequestedByCookie = false; 75 | HttpSessionThreadLocal.unset(); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/session/DefaultServletBridgeHttpSessionStore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.session; 18 | 19 | import net.javaforge.netty.servlet.bridge.impl.HttpSessionImpl; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import java.util.Map; 24 | import java.util.UUID; 25 | import java.util.concurrent.ConcurrentHashMap; 26 | 27 | public class DefaultServletBridgeHttpSessionStore implements 28 | ServletBridgeHttpSessionStore { 29 | 30 | private static final Logger log = LoggerFactory 31 | .getLogger(DefaultServletBridgeHttpSessionStore.class); 32 | 33 | public static ConcurrentHashMap sessions = new ConcurrentHashMap(); 34 | 35 | @Override 36 | public HttpSessionImpl createSession() { 37 | String sessionId = this.generateNewSessionId(); 38 | log.debug("Creating new session with id {}", sessionId); 39 | 40 | HttpSessionImpl session = new HttpSessionImpl(sessionId); 41 | sessions.put(sessionId, session); 42 | return session; 43 | } 44 | 45 | @Override 46 | public void destroySession(String sessionId) { 47 | log.debug("Destroying session with id {}", sessionId); 48 | sessions.remove(sessionId); 49 | } 50 | 51 | @Override 52 | public HttpSessionImpl findSession(String sessionId) { 53 | if (sessionId == null) 54 | return null; 55 | 56 | return sessions.get(sessionId); 57 | } 58 | 59 | protected String generateNewSessionId() { 60 | return UUID.randomUUID().toString(); 61 | } 62 | 63 | @Override 64 | public void destroyInactiveSessions() { 65 | for (Map.Entry entry : sessions.entrySet()) { 66 | HttpSessionImpl session = entry.getValue(); 67 | if (session.getMaxInactiveInterval() < 0) 68 | continue; 69 | 70 | long currentMillis = System.currentTimeMillis(); 71 | 72 | if (currentMillis - session.getLastAccessedTime() > session 73 | .getMaxInactiveInterval() * 1000) { 74 | 75 | destroySession(entry.getKey()); 76 | } 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/session/ServletBridgeHttpSessionStore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.session; 18 | 19 | import net.javaforge.netty.servlet.bridge.impl.HttpSessionImpl; 20 | 21 | public interface ServletBridgeHttpSessionStore { 22 | 23 | HttpSessionImpl findSession(String sessionId); 24 | 25 | HttpSessionImpl createSession(); 26 | 27 | void destroySession(String sessionId); 28 | 29 | void destroyInactiveSessions(); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /netty-servlet-bridge/src/main/java/net/javaforge/netty/servlet/bridge/util/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.servlet.bridge.util; 18 | 19 | import io.netty.handler.codec.http.Cookie; 20 | import io.netty.handler.codec.http.CookieDecoder; 21 | import io.netty.handler.codec.http.HttpRequest; 22 | import io.netty.handler.codec.http.HttpResponse; 23 | import net.javaforge.netty.servlet.bridge.ServletBridgeRuntimeException; 24 | 25 | import java.io.File; 26 | import java.io.UnsupportedEncodingException; 27 | import java.net.FileNameMap; 28 | import java.net.URLConnection; 29 | import java.net.URLDecoder; 30 | import java.util.*; 31 | 32 | import static io.netty.handler.codec.http.HttpHeaders.Names.COOKIE; 33 | 34 | public final class Utils { 35 | 36 | private Utils() { 37 | } 38 | 39 | public static final Enumeration emptyEnumeration() { 40 | return Collections.enumeration(Collections.emptySet()); 41 | } 42 | 43 | public static final Enumeration enumeration(Collection collection) { 44 | if (collection == null) 45 | return emptyEnumeration(); 46 | 47 | return Collections.enumeration(collection); 48 | } 49 | 50 | public static final Enumeration enumerationFromKeys(Map map) { 51 | if (map == null) 52 | return emptyEnumeration(); 53 | 54 | return Collections.enumeration(map.keySet()); 55 | } 56 | 57 | public static final Enumeration enumerationFromValues(Map map) { 58 | if (map == null) 59 | return emptyEnumeration(); 60 | 61 | return Collections.enumeration(map.values()); 62 | } 63 | 64 | public static final T newInstance(Class clazz) { 65 | 66 | try { 67 | return clazz.newInstance(); 68 | } catch (InstantiationException e) { 69 | throw new ServletBridgeRuntimeException( 70 | "Error instantiating class: " + clazz, e); 71 | } catch (IllegalAccessException e) { 72 | throw new ServletBridgeRuntimeException( 73 | "Error instantiating class: " + clazz, e); 74 | } 75 | 76 | } 77 | 78 | /** 79 | * Parse the character encoding from the specified content type header. If 80 | * the content type is null, or there is no explicit character encoding, 81 | * null is returned. 82 | * 83 | * @param contentType 84 | * a content type header 85 | */ 86 | public static final String getCharsetFromContentType(String contentType) { 87 | 88 | if (contentType == null) { 89 | return (null); 90 | } 91 | int start = contentType.indexOf("charset="); 92 | if (start < 0) { 93 | return (null); 94 | } 95 | String encoding = contentType.substring(start + 8); 96 | int end = encoding.indexOf(';'); 97 | if (end >= 0) { 98 | encoding = encoding.substring(0, end); 99 | } 100 | encoding = encoding.trim(); 101 | if ((encoding.length() > 2) && (encoding.startsWith("\"")) 102 | && (encoding.endsWith("\""))) { 103 | encoding = encoding.substring(1, encoding.length() - 1); 104 | } 105 | return (encoding.trim()); 106 | 107 | } 108 | 109 | public static final Collection getCookies(String name, 110 | HttpRequest request) { 111 | String cookieString = request.headers().get(COOKIE); 112 | if (cookieString != null) { 113 | List foundCookie = new ArrayList(); 114 | Set cookies = CookieDecoder.decode(cookieString); 115 | for (Cookie cookie : cookies) { 116 | if (cookie.getName().equals(name)) 117 | foundCookie.add(cookie); 118 | } 119 | 120 | return foundCookie; 121 | } 122 | return null; 123 | } 124 | 125 | public static final Collection getCookies(String name, 126 | HttpResponse response) { 127 | String cookieString = response.headers().get(COOKIE); 128 | if (cookieString != null) { 129 | List foundCookie = new ArrayList(); 130 | Set cookies = CookieDecoder.decode(cookieString); 131 | for (Cookie cookie : cookies) { 132 | if (cookie.getName().equals(name)) 133 | foundCookie.add(cookie); 134 | } 135 | 136 | return foundCookie; 137 | } 138 | return null; 139 | } 140 | 141 | public static final String getMimeType(String fileUrl) { 142 | FileNameMap fileNameMap = URLConnection.getFileNameMap(); 143 | String type = fileNameMap.getContentTypeFor(fileUrl); 144 | return type; 145 | } 146 | 147 | public static final String sanitizeUri(String uri) { 148 | // Decode the path. 149 | try { 150 | uri = URLDecoder.decode(uri, "UTF-8"); 151 | } catch (UnsupportedEncodingException e) { 152 | try { 153 | uri = URLDecoder.decode(uri, "ISO-8859-1"); 154 | } catch (UnsupportedEncodingException e1) { 155 | throw new Error(); 156 | } 157 | } 158 | 159 | // Convert file separators. 160 | uri = uri.replace('/', File.separatorChar); 161 | 162 | // Simplistic dumb security check. 163 | // You will have to do something serious in the production environment. 164 | if (uri.contains(File.separator + ".") 165 | || uri.contains("." + File.separator) || uri.startsWith(".") 166 | || uri.endsWith(".")) { 167 | return null; 168 | } 169 | 170 | return uri; 171 | } 172 | 173 | public static final Collection parseAcceptLanguageHeader( 174 | String acceptLanguageHeader) { 175 | 176 | if (acceptLanguageHeader == null) 177 | return null; 178 | 179 | List locales = new ArrayList(); 180 | 181 | for (String str : acceptLanguageHeader.split(",")) { 182 | String[] arr = str.trim().replace("-", "_").split(";"); 183 | 184 | // Parse the locale 185 | Locale locale = null; 186 | String[] l = arr[0].split("_"); 187 | switch (l.length) { 188 | case 2: 189 | locale = new Locale(l[0], l[1]); 190 | break; 191 | case 3: 192 | locale = new Locale(l[0], l[1], l[2]); 193 | break; 194 | default: 195 | locale = new Locale(l[0]); 196 | break; 197 | } 198 | 199 | // Parse the q-value 200 | /* 201 | * Double q = 1.0D; for (String s : arr) { s = s.trim(); if 202 | * (s.startsWith("q=")) { q = 203 | * Double.parseDouble(s.substring(2).trim()); break; } } 204 | */ 205 | 206 | locales.add(locale); 207 | } 208 | 209 | return locales; 210 | 211 | } 212 | 213 | } 214 | -------------------------------------------------------------------------------- /vaadin-netty-example/pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 4.0.0 20 | 21 | net.javaforge.netty 22 | vaadin-netty-example 23 | 1.0-SNAPSHOT 24 | jar 25 | 26 | vaadin-netty-example 27 | http://maven.apache.org 28 | 29 | 30 | UTF-8 31 | 7.0.0 32 | 33 | 34 | 35 | 36 | 37 | net.javaforge.netty 38 | netty-servlet-bridge 39 | 1.0.0-SNAPSHOT 40 | 41 | 42 | com.vaadin 43 | vaadin-server 44 | ${vaadin.version} 45 | 46 | 47 | com.vaadin 48 | vaadin-theme-compiler 49 | 50 | 51 | 52 | 53 | com.vaadin 54 | vaadin-client-compiled 55 | ${vaadin.version} 56 | 57 | 58 | com.vaadin 59 | vaadin-themes 60 | ${vaadin.version} 61 | 62 | 63 | org.slf4j 64 | slf4j-simple 65 | 1.7.2 66 | 67 | 68 | junit 69 | junit 70 | 4.11 71 | test 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-eclipse-plugin 80 | 2.9 81 | 82 | true 83 | true 84 | 85 | 86 | 87 | org.apache.maven.plugins 88 | maven-compiler-plugin 89 | 90 | 1.6 91 | 1.6 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /vaadin-netty-example/src/main/java/net/javaforge/netty/App.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty; 18 | 19 | import com.vaadin.server.VaadinServlet; 20 | import net.javaforge.netty.servlet.bridge.ServletBridgeChannelPipelineFactory; 21 | import net.javaforge.netty.servlet.bridge.config.ServletConfiguration; 22 | import net.javaforge.netty.servlet.bridge.config.WebappConfiguration; 23 | import net.javaforge.netty.vaadin.AddressbookUI; 24 | import org.jboss.netty.bootstrap.ServerBootstrap; 25 | import org.jboss.netty.channel.Channel; 26 | import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | import java.net.InetSocketAddress; 31 | import java.util.concurrent.Executors; 32 | 33 | public class App { 34 | 35 | private static final Logger log = LoggerFactory.getLogger(App.class); 36 | 37 | public static void main(String[] args) { 38 | 39 | long start = System.currentTimeMillis(); 40 | 41 | // Configure the server. 42 | final ServerBootstrap bootstrap = new ServerBootstrap( 43 | new NioServerSocketChannelFactory(Executors 44 | .newCachedThreadPool(), Executors.newCachedThreadPool())); 45 | 46 | WebappConfiguration webapp = new WebappConfiguration(); 47 | webapp.addServletConfigurations(new ServletConfiguration( 48 | VaadinServlet.class, "/*").addInitParameter("UI", 49 | AddressbookUI.class.getName())); 50 | 51 | // Set up the event pipeline factory. 52 | final ServletBridgeChannelPipelineFactory servletBridge = new ServletBridgeChannelPipelineFactory( 53 | webapp); 54 | bootstrap.setPipelineFactory(servletBridge); 55 | 56 | // Bind and start to accept incoming connections. 57 | final Channel serverChannel = bootstrap 58 | .bind(new InetSocketAddress(8080)); 59 | 60 | long end = System.currentTimeMillis(); 61 | log.info(">>> Server started in {} ms .... <<< ", (end - start)); 62 | 63 | Runtime.getRuntime().addShutdownHook(new Thread() { 64 | 65 | @Override 66 | public void run() { 67 | servletBridge.shutdown(); 68 | serverChannel.close().awaitUninterruptibly(); 69 | bootstrap.releaseExternalResources(); 70 | } 71 | }); 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /vaadin-netty-example/src/main/java/net/javaforge/netty/vaadin/AddressbookUI.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 by Maxim Kalina 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.javaforge.netty.vaadin; 18 | 19 | import com.vaadin.annotations.Title; 20 | import com.vaadin.data.Container.Filter; 21 | import com.vaadin.data.Item; 22 | import com.vaadin.data.Property; 23 | import com.vaadin.data.Property.ValueChangeEvent; 24 | import com.vaadin.data.fieldgroup.FieldGroup; 25 | import com.vaadin.data.util.IndexedContainer; 26 | import com.vaadin.event.FieldEvents; 27 | import com.vaadin.event.FieldEvents.BlurEvent; 28 | import com.vaadin.event.FieldEvents.TextChangeEvent; 29 | import com.vaadin.event.FieldEvents.TextChangeListener; 30 | import com.vaadin.server.VaadinRequest; 31 | import com.vaadin.ui.AbstractTextField.TextChangeEventMode; 32 | import com.vaadin.ui.*; 33 | import com.vaadin.ui.Button.ClickEvent; 34 | import com.vaadin.ui.Button.ClickListener; 35 | 36 | /* 37 | * UI class is the starting point for your app. You may deploy it with VaadinServlet 38 | * or VaadinPortlet by giving your UI class name a parameter. When you browse to your 39 | * app a web page showing your UI is automatically generated. Or you may choose to 40 | * embed your UI to an existing web page. 41 | */ 42 | @Title("Addressbook") 43 | public class AddressbookUI extends UI { 44 | 45 | private static final long serialVersionUID = 1L; 46 | 47 | /* User interface components are stored in session. */ 48 | private Table contactList = new Table(); 49 | private TextField searchField = new TextField(); 50 | private Button addNewContactButton = new Button("New"); 51 | private Button removeContactButton = new Button("Remove this contact"); 52 | private FormLayout editorLayout = new FormLayout(); 53 | private FieldGroup editorFields = new FieldGroup(); 54 | 55 | private static final String FNAME = "First Name"; 56 | private static final String LNAME = "Last Name"; 57 | private static final String COMPANY = "Company"; 58 | private static final String[] fieldNames = new String[]{FNAME, LNAME, 59 | COMPANY, "Mobile Phone", "Work Phone", "Home Phone", "Work Email", 60 | "Home Email", "Street", "City", "Zip", "State", "Country"}; 61 | 62 | /* 63 | * Any component can be bound to an external data source. This example uses 64 | * just a dummy in-memory list, but there are many more practical 65 | * implementations. 66 | */ 67 | IndexedContainer contactContainer = createDummyDatasource(); 68 | 69 | /* 70 | * After UI class is created, init() is executed. You should build and wire 71 | * up your user interface here. 72 | */ 73 | protected void init(VaadinRequest request) { 74 | initLayout(); 75 | initContactList(); 76 | initEditor(); 77 | initSearch(); 78 | initAddRemoveButtons(); 79 | } 80 | 81 | /* 82 | * In this example layouts are programmed in Java. You may choose use a 83 | * visual editor, CSS or HTML templates for layout instead. 84 | */ 85 | private void initLayout() { 86 | 87 | /* Root of the user interface component tree is set */ 88 | HorizontalSplitPanel splitPanel = new HorizontalSplitPanel(); 89 | setContent(splitPanel); 90 | 91 | /* Build the component tree */ 92 | VerticalLayout leftLayout = new VerticalLayout(); 93 | splitPanel.addComponent(leftLayout); 94 | splitPanel.addComponent(editorLayout); 95 | leftLayout.addComponent(contactList); 96 | HorizontalLayout bottomLeftLayout = new HorizontalLayout(); 97 | leftLayout.addComponent(bottomLeftLayout); 98 | bottomLeftLayout.addComponent(searchField); 99 | bottomLeftLayout.addComponent(addNewContactButton); 100 | 101 | /* Set the contents in the left of the split panel to use all the space */ 102 | leftLayout.setSizeFull(); 103 | 104 | /* 105 | * On the left side, expand the size of the contactList so that it uses 106 | * all the space left after from bottomLeftLayout 107 | */ 108 | leftLayout.setExpandRatio(contactList, 1); 109 | contactList.setSizeFull(); 110 | 111 | /* 112 | * In the bottomLeftLayout, searchField takes all the width there is 113 | * after adding addNewContactButton. The height of the layout is defined 114 | * by the tallest component. 115 | */ 116 | bottomLeftLayout.setWidth("100%"); 117 | searchField.setWidth("100%"); 118 | bottomLeftLayout.setExpandRatio(searchField, 1); 119 | 120 | /* Put a little margin around the fields in the right side editor */ 121 | editorLayout.setMargin(true); 122 | editorLayout.setVisible(false); 123 | } 124 | 125 | private void initEditor() { 126 | 127 | editorLayout.addComponent(removeContactButton); 128 | 129 | /* User interface can be created dynamically to reflect underlying data. */ 130 | for (String fieldName : fieldNames) { 131 | final TextField field = new TextField(fieldName); 132 | editorLayout.addComponent(field); 133 | field.setWidth("100%"); 134 | 135 | /* 136 | * We use a FieldGroup to connect multiple components to a data 137 | * source at once. 138 | */ 139 | editorFields.bind(field, fieldName); 140 | 141 | field.addBlurListener(new FieldEvents.BlurListener() { 142 | private static final long serialVersionUID = 1L; 143 | 144 | @Override 145 | public void blur(BlurEvent event) { 146 | field.commit(); 147 | } 148 | }); 149 | 150 | } 151 | 152 | /* 153 | * Data can be buffered in the user interface. When doing so, commit() 154 | * writes the changes to the data source. Here we choose to write the 155 | * changes automatically without calling commit(). 156 | */ 157 | 158 | // editorFields.setBuffered(false); 159 | } 160 | 161 | private void initSearch() { 162 | 163 | /* 164 | * We want to show a subtle prompt in the search field. We could also 165 | * set a caption that would be shown above the field or description to 166 | * be shown in a tooltip. 167 | */ 168 | searchField.setInputPrompt("Search contacts"); 169 | 170 | /* 171 | * Granularity for sending events over the wire can be controlled. By 172 | * default simple changes like writing a text in TextField are sent to 173 | * server with the next Ajax call. You can set your component to be 174 | * immediate to send the changes to server immediately after focus 175 | * leaves the field. Here we choose to send the text over the wire as 176 | * soon as user stops writing for a moment. 177 | */ 178 | searchField.setTextChangeEventMode(TextChangeEventMode.LAZY); 179 | 180 | /* 181 | * When the event happens, we handle it in the anonymous inner class. 182 | * You may choose to use separate controllers (in MVC) or presenters (in 183 | * MVP) instead. In the end, the preferred application architecture is 184 | * up to you. 185 | */ 186 | searchField.addTextChangeListener(new TextChangeListener() { 187 | private static final long serialVersionUID = 1L; 188 | 189 | public void textChange(final TextChangeEvent event) { 190 | 191 | /* Reset the filter for the contactContainer. */ 192 | contactContainer.removeAllContainerFilters(); 193 | contactContainer.addContainerFilter(new ContactFilter(event 194 | .getText())); 195 | } 196 | }); 197 | } 198 | 199 | /* 200 | * A custom filter for searching names and companies in the 201 | * contactContainer. 202 | */ 203 | private class ContactFilter implements Filter { 204 | private static final long serialVersionUID = 1L; 205 | private String needle; 206 | 207 | public ContactFilter(String needle) { 208 | this.needle = needle.toLowerCase(); 209 | } 210 | 211 | public boolean passesFilter(Object itemId, Item item) { 212 | String haystack = ("" + item.getItemProperty(FNAME).getValue() 213 | + item.getItemProperty(LNAME).getValue() + item 214 | .getItemProperty(COMPANY).getValue()).toLowerCase(); 215 | return haystack.contains(needle); 216 | } 217 | 218 | public boolean appliesToProperty(Object id) { 219 | return true; 220 | } 221 | } 222 | 223 | private void initAddRemoveButtons() { 224 | addNewContactButton.addClickListener(new ClickListener() { 225 | private static final long serialVersionUID = 1L; 226 | 227 | @SuppressWarnings("unchecked") 228 | public void buttonClick(ClickEvent event) { 229 | 230 | /* 231 | * Rows in the Container data model are called Item. Here we add 232 | * a new row in the beginning of the list. 233 | */ 234 | contactContainer.removeAllContainerFilters(); 235 | Object contactId = contactContainer.addItemAt(0); 236 | 237 | /* 238 | * Each Item has a set of Properties that hold values. Here we 239 | * set a couple of those. 240 | */ 241 | contactList.getContainerProperty(contactId, FNAME).setValue( 242 | "New"); 243 | contactList.getContainerProperty(contactId, LNAME).setValue( 244 | "Contact"); 245 | 246 | /* Lets choose the newly created contact to edit it. */ 247 | contactList.select(contactId); 248 | } 249 | }); 250 | 251 | removeContactButton.addClickListener(new ClickListener() { 252 | private static final long serialVersionUID = 1L; 253 | 254 | public void buttonClick(ClickEvent event) { 255 | Object contactId = contactList.getValue(); 256 | contactList.removeItem(contactId); 257 | } 258 | }); 259 | } 260 | 261 | private void initContactList() { 262 | contactList.setContainerDataSource(contactContainer); 263 | contactList.setVisibleColumns(new String[]{FNAME, LNAME, COMPANY}); 264 | contactList.setSelectable(true); 265 | contactList.setImmediate(true); 266 | 267 | contactList.addValueChangeListener(new Property.ValueChangeListener() { 268 | private static final long serialVersionUID = 1L; 269 | 270 | public void valueChange(ValueChangeEvent event) { 271 | Object contactId = contactList.getValue(); 272 | 273 | /* 274 | * When a contact is selected from the list, we want to show 275 | * that in our editor on the right. This is nicely done by the 276 | * FieldGroup that binds all the fields to the corresponding 277 | * Properties in our contact at once. 278 | */ 279 | if (contactId != null) 280 | editorFields.setItemDataSource(contactList 281 | .getItem(contactId)); 282 | 283 | editorLayout.setVisible(contactId != null); 284 | } 285 | }); 286 | } 287 | 288 | /* 289 | * Generate some in-memory example data to play with. In a real application 290 | * we could be using SQLContainer, JPAContainer or some other to persist the 291 | * data. 292 | */ 293 | @SuppressWarnings("unchecked") 294 | private static IndexedContainer createDummyDatasource() { 295 | IndexedContainer ic = new IndexedContainer(); 296 | 297 | for (String p : fieldNames) { 298 | ic.addContainerProperty(p, String.class, ""); 299 | } 300 | 301 | /* Create dummy data by randomly combining first and last names */ 302 | String[] fnames = {"Peter", "Alice", "Joshua", "Mike", "Olivia", 303 | "Nina", "Alex", "Rita", "Dan", "Umberto", "Henrik", "Rene", 304 | "Lisa", "Marge"}; 305 | String[] lnames = {"Smith", "Gordon", "Simpson", "Brown", "Clavel", 306 | "Simons", "Verne", "Scott", "Allison", "Gates", "Rowling", 307 | "Barks", "Ross", "Schneider", "Tate"}; 308 | for (int i = 0; i < 1000; i++) { 309 | Object id = ic.addItem(); 310 | ic.getContainerProperty(id, FNAME).setValue( 311 | fnames[(int) (fnames.length * Math.random())]); 312 | ic.getContainerProperty(id, LNAME).setValue( 313 | lnames[(int) (lnames.length * Math.random())]); 314 | } 315 | 316 | return ic; 317 | } 318 | 319 | } 320 | --------------------------------------------------------------------------------