| = Howto |
| :jbake-date: 2019-12-21 |
| :jbake-type: page |
| :jbake-status: published |
| :jbake-meecrowavepdf: |
| :jbake-meecrowavecolor: body-green |
| :icons: font |
| |
| == How to create a simple maven project using Meecrowave ? |
| |
| You should add the following dependencies do the dependencies section of your pom.xml (adjust version to current stable version) |
| |
| [source,xml] |
| ---- |
| <dependency> |
| <groupId>org.apache.meecrowave</groupId> |
| <artifactId>meecrowave-specs-api</artifactId> |
| <version>${meecrowave.version}</version> |
| </dependency> |
| <dependency> |
| <groupId>org.apache.meecrowave</groupId> |
| <artifactId>meecrowave-core</artifactId> |
| <version>${meecrowave.version}</version> |
| </dependency> |
| |
| <!-- if you intend to have unit tests (you really should) --> |
| <dependency> |
| <groupId>org.apache.meecrowave</groupId> |
| <artifactId>meecrowave-junit</artifactId> |
| <version>${meecrowave.version}</version> |
| <scope>test</scope> |
| </dependency> |
| ---- |
| |
| and the following plugin configuration to the build/plugins section of your pom.xml |
| |
| [source,xml] |
| ---- |
| <plugin> |
| <!-- |
| For starting meecrowave via Maven. Just run |
| $> mvn clean install meecrowave:run |
| --> |
| <groupId>org.apache.meecrowave</groupId> |
| <artifactId>meecrowave-maven-plugin</artifactId> |
| <version>${meecrowave.version}</version> |
| </plugin> |
| ---- |
| |
| Then, you can start your app by running |
| |
| [source,shell] |
| ---- |
| mvn clean install meecrowave:run |
| ---- |
| |
| |
| == How to add a REST Endpoint ? |
| |
| You should declare your endpoint path and verd : |
| |
| [source,java] |
| ---- |
| package org.mypackage; |
| |
| import javax.enterprise.context.ApplicationScoped; |
| import javax.ws.rs.GET; |
| import javax.ws.rs.Path; |
| |
| @Path("mypath") |
| @ApplicationScoped |
| public class MyEndpoint { |
| |
| /** |
| * Ping / pong rest GET method, to check backend and replies to queries |
| * |
| * @return |
| */ |
| @Path("/ping") |
| @GET |
| public String getPing() { |
| return "pong"; |
| } |
| } |
| ---- |
| |
| == How to add a filter (simple case) ? |
| |
| Use standard Servlet 4.0 link:https://docs.oracle.com/javaee/6/api/javax/servlet/annotation/WebFilter.html[@WebFilter] annotation. A simple example : |
| |
| [source,java] |
| ---- |
| |
| package org.mypackage; |
| |
| import java.io.IOException; |
| import javax.servlet.Filter; |
| import javax.servlet.FilterChain; |
| import javax.servlet.ServletException; |
| import javax.servlet.ServletRequest; |
| import javax.servlet.ServletResponse; |
| import javax.servlet.annotation.WebFilter; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| /** |
| * A simple CORS filter |
| * |
| */ |
| @WebFilter(asyncSupported = true, urlPatterns = {"/*"}) |
| public class CORSFilter implements Filter { |
| |
| /** |
| * A basic CORS filter, allowing everything |
| */ |
| @Override |
| public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) |
| throws IOException, ServletException { |
| |
| HttpServletRequest request = (HttpServletRequest) servletRequest; |
| |
| HttpServletResponse response = (HttpServletResponse) servletResponse; |
| response.addHeader("Access-Control-Allow-Origin", "*"); |
| response.addHeader("Access-Control-Allow-Methods","GET, OPTIONS, HEAD, PUT, POST, DELETE"); |
| response.addHeader("Access-Control-Allow-Headers","*"); |
| |
| if (request.getMethod().equals("OPTIONS")) { |
| // special case of return code for "OPTIONS" query |
| response.setStatus(HttpServletResponse.SC_ACCEPTED); |
| return; |
| } |
| |
| // pass the request along the filter chain |
| chain.doFilter(request, servletResponse); |
| } |
| } |
| ---- |
| |
| == How to add a servlet ? |
| |
| If your servlet requires no configuration that you would typically put in the web.xml file, you can use the link:https://docs.oracle.com/javaee/6/api/javax/servlet/annotation/WebServlet.html[@WebServlet] annotation from the Servlet 3.0 specification. |
| |
| If you need to configure the servlet, you should use a link:https://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html[ServletContainerInitializer]. |
| |
| If you would have a declaration such as : |
| |
| [source,xml] |
| ---- |
| <servlet> |
| <description>My Servlet</description> |
| <servlet-name>MyServlet</servlet-name> |
| <servlet-class>org.my.servlet.ImplementationClass</servlet-class> |
| <init-param> |
| <param-name>param-name</param-name> |
| <param-value>My param value</param-value> |
| </init-param> |
| <load-on-startup>0</load-on-startup> |
| <async-supported>true</async-supported> |
| </servlet> |
| <servlet-mapping> |
| <servlet-name>MyServlet</servlet-name> |
| <url-pattern>/my_mapping/*</url-pattern> |
| </servlet-mapping> |
| ---- |
| |
| in your web.xml, you would have a SerlvetContainerInitializer such as : |
| |
| [source,java] |
| ---- |
| package org.mypackage; |
| |
| import java.util.Set; |
| |
| import javax.servlet.ServletContainerInitializer; |
| import javax.servlet.ServletContext; |
| import javax.servlet.ServletRegistration; |
| |
| import org.my.servlet.ImplementationClass; |
| |
| public class MyServletContainerInitializer implements ServletContainerInitializer { |
| @Override |
| public void onStartup(final Set<Class<?>> c, final ServletContext context) { |
| final ServletRegistration.Dynamic def = context.addServlet("My Servlet", ImplementationClass.class); |
| def.setInitParameter("param-name", "My param value"); |
| |
| def.setLoadOnStartup(0); |
| def.addMapping("/my_mapping/*"); |
| def.setAsyncSupported(true); |
| } |
| } |
| ---- |
| |
| Then, you should register this implementation of ServletContainerInitializer: |
| |
| * in a SPI, in src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer: |
| |
| [source] |
| ---- |
| org.mypackage.MyServletContainerInitializer |
| ---- |
| |
| * or add it to Meecrowave configuration using a Meecrowave.ConfigurationCustomizer such as : |
| |
| [source,java] |
| ---- |
| package org.mypackage; |
| |
| import org.apache.meecrowave.Meecrowave; |
| |
| public class ServletContainerInitializerCustomizer implements Meecrowave.ConfigurationCustomizer { |
| @Override |
| public void accept(final Meecrowave.Builder builder) { |
| builder.addServletContextInitializer(new MyServletContainerInitializer()); |
| } |
| } |
| ---- |
| |
| Using this last option, the configuration will also be performed before unit tests are executed. |
| |
| Your implementation of Meecrowave.ConfigurationCustomizer should be added to the configuration by appending its canonical name to the src/main/resources/META-INF/org.apache.meecrowave.Meecrowave$ConfigurationCustomizer file. |
| |
| == How to add a valve ? |
| |
| Simple cases should be handled using link:http://openwebbeans.apache.org/meecrowave/meecrowave-core/configuration.html#_valve_configuration[a meecrowave.properties file]. |
| |
| More complex cases can be handled using an implementation of Meecrowave.ConfigurationCustomizer. |
| |
| In the following example, we instantiate a link:https://tomcat.apache.org/tomcat-9.0-doc/rewrite.html[Tomcat RewriteValve] and load the rewrite.config file we usually put in src/main/webapp/WEB-INF in a webapp packaged as a war, and that we would put in src/main/resources in a meecrowave app : |
| |
| [source,java] |
| ---- |
| package org.mypackage; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import lombok.extern.log4j.Log4j2; |
| import org.apache.catalina.LifecycleException; |
| import org.apache.catalina.valves.rewrite.RewriteValve; |
| import org.apache.meecrowave.Meecrowave; |
| |
| /** |
| * A bit of glue to set proxy / RewriteValve configuration at startup |
| * |
| */ |
| @Log4j2 |
| public class RewriteValveCustomizer implements Meecrowave.ConfigurationCustomizer { |
| final String PROXY_CONFIG = "rewrite.config"; |
| @Override |
| public void accept(final Meecrowave.Builder builder) { |
| log.info("Loading proxy / rewrite configuration from {}", PROXY_CONFIG); |
| log.info("This file should be in src/main/resources in project sources"); |
| try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(PROXY_CONFIG)) { |
| if (null == stream) { |
| log.info("Rewrite configuration file {} not found", PROXY_CONFIG); |
| return; |
| } |
| configuration = new BufferedReader(new InputStreamReader(stream)).lines().collect(Collectors.joining("\n")); |
| } catch (IOException ex) { |
| log.error("Error reading rewrite / proxy configuration file {}", PROXY_CONFIG); |
| return; |
| } |
| final RewriteValve proxy = new RewriteValve() { |
| @Override |
| protected synchronized void startInternal() throws LifecycleException { |
| super.startInternal(); |
| try { |
| setConfiguration(configuration); |
| } catch (final Exception e) { |
| throw new LifecycleException(e); |
| } |
| } |
| }; |
| // at this time, we are still single threaded. So, this should be safe. |
| builder.instanceCustomizer(tomcat -> tomcat.getHost().getPipeline().addValve(proxy)); |
| log.info("Proxy / rewrite configuration valve configured and added to tomcat."); |
| } |
| } |
| ---- |
| |
| Your implementation of Meecrowave.ConfigurationCustomizer should be added to the configuration by appending its canonical name to the src/main/resources/META-INF/org.apache.meecrowave.Meecrowave$ConfigurationCustomizer file. |
| |
| |
| A more complex example link:https://rmannibucau.metawerx.net/post/tomcat-rewrite-url[is available on Romain Manni-Bucau's blog]. |
| |
| == How to add a web frontend ? |
| |
| You should add a <webapp> element to the meecrowave plugin configuration. Example : |
| |
| [source,xml] |
| ---- |
| <plugin> |
| <!-- |
| For starting meecrowave via Maven. Just run |
| $> mvn clean install meecrowave:run |
| --> |
| <groupId>org.apache.meecrowave</groupId> |
| <artifactId>meecrowave-maven-plugin</artifactId> |
| <version>${meecrowave.version}</version> |
| <configuration> |
| <!-- include packaged app as webapp --> |
| <webapp>src/main/webapp/dist</webapp> |
| </configuration> |
| </plugin> |
| ---- |
| |
| will add the content of the "dist" folder to your package and its files will be available on the application root. |
| |
| Note that your frontend will be served when executing the app (on a mvn meecrowave:run or when running a packaged app). It will not be available during unit tests. |